\feffusing System;\r
using System.Net;\r
+using System.Threading;\r
+using System.Net.Sockets;\r
+using Newtonsoft.Json;\r
using Newtonsoft.Json.Linq;\r
\r
using System.Text;\r
-using System.Threading;\r
-using System.Net.Sockets;\r
+using System.Collections.Generic;\r
\r
namespace Stratum\r
{\r
{\r
private Socket client;\r
\r
+ object responsesLock = new object();\r
+ private Dictionary<string, string> responses = new Dictionary<string, string>();\r
+ ManualResetEvent gotResponse = new ManualResetEvent(false);\r
+\r
/// <summary>\r
/// Constructor of Stratum interface class\r
/// </summary>\r
ConnectCallback = (IAsyncResult ar) =>\r
{\r
// Retrieve socket from the state object\r
- Socket cli = (Socket)ar.AsyncState;\r
+ Socket arClient = (Socket)ar.AsyncState;\r
\r
// Complete the connection\r
- cli.EndConnect(ar);\r
+ arClient.EndConnect(ar);\r
\r
// Signal that connection has been established\r
connectDone.Set();\r
\r
// Wait for signal\r
connectDone.WaitOne();\r
+\r
+ // Start receive handler\r
+ Receiver(client);\r
}\r
\r
~Stratum()\r
SendCallback = (IAsyncResult ar) =>\r
{\r
// Retrieve the socket from the state object\r
- Socket cli = (Socket)ar.AsyncState;\r
+ Socket arClient = (Socket)ar.AsyncState;\r
\r
// Complete sending the data to the remote device\r
- int bytesSent = cli.EndSend(ar);\r
+ int bytesSent = arClient.EndSend(ar);\r
\r
// Signal that all bytes have been sent\r
sendDone.Set();\r
/// </summary>\r
/// <typeparam name="T">Return type</typeparam>\r
/// <param name="method">Method name</param>\r
+ /// <returns>StratumResponse object</returns>\r
+ public StratumResponse<T> Invoke<T>(string method)\r
+ {\r
+ var req = new StratumRequest()\r
+ {\r
+ Method = method,\r
+ Params = new object[] { }\r
+ };\r
+ return Invoke<T>(req);\r
+ }\r
+\r
+ /// <summary>\r
+ /// Invoke remote method\r
+ /// </summary>\r
+ /// <typeparam name="T">Return type</typeparam>\r
+ /// <param name="method">Method name</param>\r
/// <param name="arg">Argument</param>\r
/// <returns>StratumResponse object</returns>\r
public StratumResponse<T> Invoke<T>(string method, object arg)\r
\r
private StratumResponse<T> Invoke<T>(StratumRequest stratumReq)\r
{\r
- StratumResponse<T> rjson = null;\r
-\r
// Serialize stratumReq into JSON string\r
- var reqJSON = Newtonsoft.Json.JsonConvert.SerializeObject(stratumReq) + '\n';\r
+ var reqJSON = JsonConvert.SerializeObject(stratumReq) + '\n';\r
\r
// Send JSON data to the remote device.\r
Send(client, reqJSON);\r
\r
+ // Wait for response\r
+ gotResponse.WaitOne();\r
+\r
+ var strResponse = string.Empty;\r
+ lock (responsesLock)\r
+ {\r
+ // Deserialize the response\r
+ strResponse = responses[stratumReq.Id];\r
+ responses.Remove(stratumReq.Id);\r
+ }\r
+\r
+ // Deserialize response into new instance of StratumResponse<T> \r
+ StratumResponse<T> responseObj = JsonConvert.DeserializeObject<StratumResponse<T>>(strResponse);\r
+\r
+ // Reset the state\r
+ gotResponse.Reset();\r
+\r
+ if (responseObj == null)\r
+ {\r
+ try\r
+ {\r
+ JObject jResponseObj = JsonConvert.DeserializeObject(strResponse) as JObject;\r
+ throw new Exception(jResponseObj["Error"].ToString());\r
+ }\r
+ catch (JsonSerializationException)\r
+ {\r
+ throw new Exception("Inconsistent or empty response");\r
+ }\r
+ }\r
+\r
+ return responseObj;\r
+ }\r
+\r
+ private void Receiver(Socket client)\r
+ {\r
// Create the reading state object.\r
StratumReadState state = new StratumReadState(client);\r
\r
- // Receive event\r
- ManualResetEvent receiveDone = new ManualResetEvent(false);\r
-\r
Action<IAsyncResult> ReceiveCallback = null;\r
ReceiveCallback = (IAsyncResult ar) =>\r
{\r
// Retrieve the state object and the client socket \r
// from the asynchronous state object.\r
- StratumReadState st = (StratumReadState)ar.AsyncState;\r
- Socket ci = st.workSocket;\r
+ StratumReadState arStatus = (StratumReadState)ar.AsyncState;\r
+ Socket arClient = arStatus.workSocket;\r
\r
// Read data from the remote device.\r
- int bytesRead = ci.EndReceive(ar);\r
+ int bytesRead = arClient.EndReceive(ar);\r
\r
if (bytesRead <= 0)\r
return;\r
\r
- lock (st.sb)\r
+ lock (arStatus.sb)\r
{\r
// There might be more data, so store the data received so far.\r
- st.sb.Append(Encoding.ASCII.GetString(st.buffer, 0, bytesRead));\r
+ arStatus.sb.Append(Encoding.ASCII.GetString(arStatus.buffer, 0, bytesRead));\r
\r
- if (st.buffer[bytesRead - 1] != '\n')\r
+ if (arStatus.buffer[bytesRead - 1] == '\n')\r
{\r
- // No EOL at the end of buffer, going to get the rest of data\r
- ci.BeginReceive(st.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), st);\r
- }\r
- else\r
- {\r
- string strResponse = st.sb.ToString();\r
-\r
- // Deserialize the response\r
- rjson = Newtonsoft.Json.JsonConvert.DeserializeObject<StratumResponse<T>>(strResponse);\r
+ var strMessage = arStatus.sb.ToString();\r
+ arStatus.sb.Clear();\r
\r
- if (rjson == null)\r
+ try\r
{\r
- try\r
+ JObject jResponse = JsonConvert.DeserializeObject(strMessage) as JObject;\r
+ var reqId = (string)jResponse["id"];\r
+\r
+ if (!String.IsNullOrEmpty(reqId))\r
{\r
- JObject jo = Newtonsoft.Json.JsonConvert.DeserializeObject(strResponse) as JObject;\r
- throw new Exception(jo["Error"].ToString());\r
+ lock (responsesLock)\r
+ {\r
+ responses.Add(reqId, strMessage);\r
+ }\r
+\r
+ gotResponse.Set();\r
}\r
- catch (Newtonsoft.Json.JsonSerializationException)\r
+ else\r
{\r
- throw new Exception("Inconsistent or empty response");\r
+ StratumNotification jNotification = JsonConvert.DeserializeObject<StratumNotification>(strMessage);\r
+\r
+ var NotifyProcessThread = new Thread(() => NotificationHandler(jNotification.Method, jNotification.Params));\r
+ NotifyProcessThread.Start();\r
}\r
}\r
-\r
- // Signal that all bytes have been received.\r
- receiveDone.Set();\r
+ catch (JsonSerializationException e)\r
+ {\r
+ // TODO: handle parse error\r
+ }\r
}\r
}\r
+\r
+ arClient.BeginReceive(arStatus.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), arStatus);\r
};\r
\r
- // Begin receiving the data from the remote device.\r
client.BeginReceive(state.buffer, 0, StratumReadState.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);\r
- receiveDone.WaitOne();\r
+ }\r
\r
- return rjson;\r
+ /// <summary>\r
+ /// 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
+ /// </summary>\r
+ /// <param name="NotificationMethod">Method name</param>\r
+ /// <param name="NotificationData">Array of values</param>\r
+ private static void NotificationHandler(string NotificationMethod, JArray NotificationData)\r
+ {\r
+ Console.WriteLine("\nNotification: Method={0}, data={1}", NotificationMethod, NotificationData.ToString());\r
}\r
}\r
+\r
}
\ No newline at end of file