3 using System.Threading;
\r
4 using System.Net.Sockets;
\r
5 using Newtonsoft.Json.Linq;
\r
8 using System.Collections.Generic;
\r
12 delegate void NotificationCallback(out object[] notificationData);
\r
14 public class Stratum
\r
16 private Socket client;
\r
18 object responsesLock = new object();
\r
19 private Dictionary<string, string> responses = new Dictionary<string, string>();
\r
20 ManualResetEvent gotResponse = new ManualResetEvent(false);
\r
23 /// Constructor of Stratum interface class
\r
25 /// <param name="ipAddress">IPv4 address</param>
\r
26 /// <param name="port">Port number</param>
\r
27 public Stratum(string ipAddress, int port)
\r
29 // End point for the remote device
\r
30 IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ipAddress), port);
\r
32 // Create TCP socket
\r
33 client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
\r
35 // Connect done event
\r
36 ManualResetEvent connectDone = new ManualResetEvent(false);
\r
38 Action<IAsyncResult> ConnectCallback = null;
\r
39 ConnectCallback = (IAsyncResult ar) =>
\r
41 // Retrieve socket from the state object
\r
42 Socket arClient = (Socket)ar.AsyncState;
\r
44 // Complete the connection
\r
45 arClient.EndConnect(ar);
\r
47 // Signal that connection has been established
\r
51 // Connect to the remote device
\r
52 client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
\r
55 connectDone.WaitOne();
\r
57 // Start receive handler
\r
63 // Release the socket
\r
64 client.Shutdown(SocketShutdown.Both);
\r
68 private void Send(Socket client, String data)
\r
71 ManualResetEvent sendDone = new ManualResetEvent(false);
\r
73 Action<IAsyncResult> SendCallback = null;
\r
74 SendCallback = (IAsyncResult ar) =>
\r
76 // Retrieve the socket from the state object
\r
77 Socket arClient = (Socket)ar.AsyncState;
\r
79 // Complete sending the data to the remote device
\r
80 int bytesSent = arClient.EndSend(ar);
\r
82 // Signal that all bytes have been sent
\r
86 // Convert the string data to byte data using ASCII encoding.
\r
87 byte[] byteData = Encoding.ASCII.GetBytes(data);
\r
89 // Begin sending the data to the remote device.
\r
90 client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
\r
97 /// Invoke remote method
\r
99 /// <typeparam name="T">Return type</typeparam>
\r
100 /// <param name="method">Method name</param>
\r
101 /// <returns>StratumResponse object</returns>
\r
102 public StratumResponse<T> Invoke<T>(string method)
\r
104 var req = new StratumRequest()
\r
107 Params = new object[] { }
\r
109 return Invoke<T>(req);
\r
113 /// Invoke remote method
\r
115 /// <typeparam name="T">Return type</typeparam>
\r
116 /// <param name="method">Method name</param>
\r
117 /// <param name="arg">Argument</param>
\r
118 /// <returns>StratumResponse object</returns>
\r
119 public StratumResponse<T> Invoke<T>(string method, object arg)
\r
121 var req = new StratumRequest()
\r
124 Params = new object[] { arg }
\r
126 return Invoke<T>(req);
\r
130 /// Invoke remote method
\r
132 /// <typeparam name="T">Return type</typeparam>
\r
133 /// <param name="method">Method name</param>
\r
134 /// <param name="args">Arguments array</param>
\r
135 /// <returns>StratumResponse object</returns>
\r
136 public StratumResponse<T> Invoke<T>(string method, object[] args)
\r
138 var req = new StratumRequest()
\r
143 return Invoke<T>(req);
\r
146 private StratumResponse<T> Invoke<T>(StratumRequest stratumReq)
\r
148 // Serialize stratumReq into JSON string
\r
149 var reqJSON = Newtonsoft.Json.JsonConvert.SerializeObject(stratumReq) + '\n';
\r
150 var reqId = (string) stratumReq.Id;
\r
152 string strResponse = "";
\r
153 StratumResponse<T> responseObj = null;
\r
155 // Send JSON data to the remote device.
\r
156 Send(client, reqJSON);
\r
158 // Wait for response
\r
159 gotResponse.WaitOne();
\r
161 lock (responsesLock)
\r
163 // Deserialize the response
\r
164 strResponse = responses[reqId];
\r
165 responses.Remove(reqId);
\r
167 responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<StratumResponse<T>>(strResponse);
\r
170 gotResponse.Reset();
\r
172 if (responseObj == null)
\r
176 JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(strResponse) as JObject;
\r
177 throw new Exception(jo["Error"].ToString());
\r
179 catch (Newtonsoft.Json.JsonSerializationException)
\r
181 throw new Exception("Inconsistent or empty response");
\r
185 return responseObj;
\r
188 private void Receiver(Socket client)
\r
190 // Create the reading state object.
\r
191 StratumReadState state = new StratumReadState(client);
\r
193 Action<IAsyncResult> ReceiveCallback = null;
\r
194 ReceiveCallback = (IAsyncResult ar) =>
\r
196 // Retrieve the state object and the client socket
\r
197 // from the asynchronous state object.
\r
198 StratumReadState arStatus = (StratumReadState)ar.AsyncState;
\r
199 Socket arClient = arStatus.workSocket;
\r
201 // Read data from the remote device.
\r
202 int bytesRead = arClient.EndReceive(ar);
\r
204 if (bytesRead <= 0)
\r
209 // There might be more data, so store the data received so far.
\r
210 arStatus.sb.Append(Encoding.ASCII.GetString(arStatus.buffer, 0, bytesRead));
\r
212 if (arStatus.buffer[bytesRead - 1] == '\n')
\r
214 string strMessage = arStatus.sb.ToString();
\r
215 arStatus.sb.Clear();
\r
219 JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(strMessage) as JObject;
\r
220 string requestId = (string)jo["id"];
\r
222 if (!String.IsNullOrEmpty(requestId))
\r
224 lock (responsesLock)
\r
226 responses.Add(requestId, strMessage);
\r
233 // TODO: notifications handling
\r
234 Console.WriteLine("Notification: {0}", strMessage);
\r
237 catch (Newtonsoft.Json.JsonSerializationException e)
\r
239 // TODO: handle parse error
\r
244 arClient.BeginReceive(arStatus.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), arStatus);
\r
247 client.BeginReceive(state.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
\r