using System;
using System.Collections.Generic;
using System.Net.Sockets;
using NPSharp.RPC.Packets;
namespace NPSharp.RPC
{
///
/// Represents a low-level client stream which can communicate with an NP server using RPC packets.
///
public class RPCClientStream
{
private NetworkStream _ns;
private uint _id;
private readonly string _host;
private readonly ushort _port;
private readonly Dictionary> _callbacks = new Dictionary>();
///
/// Initializes an RPC connection stream with a specified host and port.
///
/// The host to connect to.
/// The port to use. Default: 3025.
public RPCClientStream(string host, ushort port = 3025)
{
_host = host;
_port = port;
}
///
/// Opens the RPC stream to the NP server.
///
/// True if the connection succeeded, otherwise false.
public bool Open()
{
// Connection already established?
if (_ns != null)
throw new InvalidOperationException("Connection already opened");
var tcp = new TcpClient();
try
{
tcp.Connect(_host, _port);
}
catch
{
return false;
}
_ns = tcp.GetStream();
return true;
}
///
/// Closes the connection with the NP server.
///
///
public void Close(int timeout = 2000)
{
// Connection already closed?
if (_ns == null)
throw new InvalidOperationException("Connection already closed");
try
{
_ns.Close(timeout);
_ns.Dispose();
}
finally
{
_ns = null;
}
}
///
/// Attaches a callback to the next message being sent out. This allows handling response packets.
///
/// The method to call when we receive a response to the next message
public void AttachCallback(Action callback)
{
if (_callbacks.ContainsKey(_id))
throw new Exception("There is already a callback for the current message. You can only add max. one callback.");
_callbacks.Add(_id, callback);
}
// TODO: Exposure of message ID needed or no?
///
/// Sends out an RPC message.
///
/// The RPC message to send out.
/// The new ID of the message.
public uint Send(RPCClientMessage message)
{
if (_ns == null)
throw new InvalidOperationException("You need to open the stream first.");
var buffer = message.Serialize(_id);
_ns.Write(buffer, 0, buffer.Length);
return _id++;
}
///
/// Waits for the next RPC message from the server and reads it.
///
/// The received server message.
public RPCServerMessage Read()
{
if (_ns == null)
throw new InvalidOperationException("You need to open the stream first.");
var message = RPCServerMessage.Deserialize(_ns);
if (message == null)
return null;
if (!_callbacks.ContainsKey(message.MessageId))
return message;
_callbacks[message.MessageId].Invoke(message);
_callbacks.Remove(message.MessageId);
return message;
}
}
}