3 using System.Threading;
\r
4 using System.Net.Sockets;
\r
5 using Newtonsoft.Json;
\r
6 using Newtonsoft.Json.Linq;
\r
9 using System.Collections.Generic;
\r
13 public class Stratum
\r
15 private Socket client;
\r
17 object responsesLock = new object();
\r
18 private Dictionary<string, string> responses = new Dictionary<string, string>();
\r
19 ManualResetEvent gotResponse = new ManualResetEvent(false);
\r
22 /// Constructor of Stratum interface class
\r
24 /// <param name="ipAddress">IPv4 address</param>
\r
25 /// <param name="port">Port number</param>
\r
26 public Stratum(string ipAddress, int port)
\r
28 // End point for the remote device
\r
29 IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ipAddress), port);
\r
31 // Create TCP socket
\r
32 client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
\r
34 // Connect done event
\r
35 ManualResetEvent connectDone = new ManualResetEvent(false);
\r
37 Action<IAsyncResult> ConnectCallback = null;
\r
38 ConnectCallback = (IAsyncResult ar) =>
\r
40 // Retrieve socket from the state object
\r
41 Socket arClient = (Socket)ar.AsyncState;
\r
43 // Complete the connection
\r
44 arClient.EndConnect(ar);
\r
46 // Signal that connection has been established
\r
50 // Connect to the remote device
\r
51 client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
\r
54 connectDone.WaitOne();
\r
56 // Start receive handler
\r
62 // Release the socket
\r
63 client.Shutdown(SocketShutdown.Both);
\r
67 private void Send(Socket client, String data)
\r
70 ManualResetEvent sendDone = new ManualResetEvent(false);
\r
72 Action<IAsyncResult> SendCallback = null;
\r
73 SendCallback = (IAsyncResult ar) =>
\r
75 // Retrieve the socket from the state object
\r
76 Socket arClient = (Socket)ar.AsyncState;
\r
78 // Complete sending the data to the remote device
\r
79 int bytesSent = arClient.EndSend(ar);
\r
81 // Signal that all bytes have been sent
\r
85 // Convert the string data to byte data using ASCII encoding.
\r
86 byte[] byteData = Encoding.ASCII.GetBytes(data);
\r
88 // Begin sending the data to the remote device.
\r
89 client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
\r
96 /// Invoke remote method
\r
98 /// <typeparam name="T">Return type</typeparam>
\r
99 /// <param name="method">Method name</param>
\r
100 /// <returns>StratumResponse object</returns>
\r
101 public StratumResponse<T> Invoke<T>(string method)
\r
103 var req = new StratumRequest()
\r
106 Params = new object[] { }
\r
108 return Invoke<T>(req);
\r
112 /// Invoke remote method
\r
114 /// <typeparam name="T">Return type</typeparam>
\r
115 /// <param name="method">Method name</param>
\r
116 /// <param name="arg">Argument</param>
\r
117 /// <returns>StratumResponse object</returns>
\r
118 public StratumResponse<T> Invoke<T>(string method, object arg)
\r
120 var req = new StratumRequest()
\r
123 Params = new object[] { arg }
\r
125 return Invoke<T>(req);
\r
129 /// Invoke remote method
\r
131 /// <typeparam name="T">Return type</typeparam>
\r
132 /// <param name="method">Method name</param>
\r
133 /// <param name="args">Arguments array</param>
\r
134 /// <returns>StratumResponse object</returns>
\r
135 public StratumResponse<T> Invoke<T>(string method, object[] args)
\r
137 var req = new StratumRequest()
\r
142 return Invoke<T>(req);
\r
145 private StratumResponse<T> Invoke<T>(StratumRequest stratumReq)
\r
147 // Serialize stratumReq into JSON string
\r
148 var reqJSON = JsonConvert.SerializeObject(stratumReq) + '\n';
\r
150 // Send JSON data to the remote device.
\r
151 Send(client, reqJSON);
\r
153 // Wait for response
\r
154 gotResponse.WaitOne();
\r
156 var strResponse = string.Empty;
\r
157 lock (responsesLock)
\r
159 // Deserialize the response
\r
160 strResponse = responses[stratumReq.Id];
\r
161 responses.Remove(stratumReq.Id);
\r
164 // Deserialize response into new instance of StratumResponse<T>
\r
165 StratumResponse<T> responseObj = JsonConvert.DeserializeObject<StratumResponse<T>>(strResponse);
\r
168 gotResponse.Reset();
\r
170 if (responseObj == null)
\r
174 JObject jResponseObj = JsonConvert.DeserializeObject(strResponse) as JObject;
\r
175 throw new Exception(jResponseObj["Error"].ToString());
\r
177 catch (JsonSerializationException)
\r
179 throw new Exception("Inconsistent or empty response");
\r
183 return responseObj;
\r
186 private void Receiver(Socket client)
\r
188 // Create the reading state object.
\r
189 StratumReadState state = new StratumReadState(client);
\r
191 Action<IAsyncResult> ReceiveCallback = null;
\r
192 ReceiveCallback = (IAsyncResult ar) =>
\r
194 // Retrieve the state object and the client socket
\r
195 // from the asynchronous state object.
\r
196 StratumReadState arStatus = (StratumReadState)ar.AsyncState;
\r
197 Socket arClient = arStatus.workSocket;
\r
199 // Read data from the remote device.
\r
200 int bytesRead = arClient.EndReceive(ar);
\r
202 if (bytesRead <= 0)
\r
207 // There might be more data, so store the data received so far.
\r
208 arStatus.sb.Append(Encoding.ASCII.GetString(arStatus.buffer, 0, bytesRead));
\r
210 if (arStatus.buffer[bytesRead - 1] == '\n')
\r
212 var strMessage = arStatus.sb.ToString();
\r
213 arStatus.sb.Clear();
\r
217 JObject jResponse = JsonConvert.DeserializeObject(strMessage) as JObject;
\r
218 var reqId = (string)jResponse["id"];
\r
220 if (!String.IsNullOrEmpty(reqId))
\r
222 lock (responsesLock)
\r
224 responses.Add(reqId, strMessage);
\r
231 StratumNotification jNotification = JsonConvert.DeserializeObject<StratumNotification>(strMessage);
\r
233 var NotifyProcessThread = new Thread(() => NotificationHandler(jNotification.Method, jNotification.Params));
\r
234 NotifyProcessThread.Start();
\r
237 catch (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
251 /// Notifications stub which is run in a separate thread. If you wish to implement real notification processing then just override this method in the derived class.
\r
253 /// <param name="NotificationMethod">Method name</param>
\r
254 /// <param name="NotificationData">Array of values</param>
\r
255 private static void NotificationHandler(string NotificationMethod, JArray NotificationData)
\r
257 Console.WriteLine("\nNotification: Method={0}, data={1}", NotificationMethod, NotificationData.ToString());
\r