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; } } }