Skip to content

Getting Started (Websockets)

Nikita Moshkalov edited this page Apr 7, 2021 · 3 revisions

Here is production code that was sent to us by @CLTanuki for hosing a websocket server.

Something is cut out of here, but we are using this implementation for 18 months. Main logic is in this part: string returnString = await JsonRpcProcessor.Process(message, connectionId);

internalclassRPCServer:IDisposable{privatestaticCancellationTokenSourcem_cancellation;privatestaticHttpListenerm_listener;privatestaticreadonlystringUrl=$"http://{Settings.Default.Host}:{Settings.Default.Port}/";publicstaticDictionary<Guid,Tuple<HttpListenerWebSocketContext,Guid?>>Clients=newDictionary<Guid,Tuple<HttpListenerWebSocketContext,Guid?>>();staticreadonlyobject[]Services={newDirectoryService(),newOrderService(),newCommonService(),newAuthService(),newSettingsService(),newGuestService()};publicRPCServer(){m_listener=newHttpListener();m_listener.Prefixes.Add(Url);m_listener.Start();m_cancellation=newCancellationTokenSource();Task.Run(()=>AcceptWebSocketClientsAsync(m_listener,m_cancellation.Token));}
#region Private Methods
privatestaticasyncTaskAcceptWebSocketClientsAsync(HttpListenerlistener,CancellationTokentoken){while(!token.IsCancellationRequested){try{varcontext=awaitlistener.GetContextAsync();if(!context.Request.IsWebSocketRequest){HttpListenerResponseresponse=context.Response;// Construct a response.StringBuildermessage=newStringBuilder();message.Append("<HTML><BODY>");message.Append("<p>HTTP NOT ALLOWED</p>");message.Append("</BODY></HTML>");byte[]buffer=Encoding.UTF8.GetBytes(message.ToString());// Get a response stream and write the response to it.response.ContentLength64=buffer.Length;response.StatusCode=403;Streamoutput=response.OutputStream;output.Write(buffer,0,buffer.Length);// You must close the output stream.output.Close();response.Close();}else{varws=awaitcontext.AcceptWebSocketAsync(null,newTimeSpan(0,0,3)).ConfigureAwait(false);if(ws!=null){GuidconnectionId=Guid.NewGuid();Clients.Add(connectionId,newTuple<HttpListenerWebSocketContext,Guid?>(ws,null));Task.Run(()=>HandleConnectionAsync(ws.WebSocket,token,connectionId));}else{}}}catch(Exceptionex){}}}privatestaticasyncTaskHandleConnectionAsync(WebSocketws,CancellationTokencancellation,GuidconnectionId){try{while(ws.State==WebSocketState.Open&&!cancellation.IsCancellationRequested){Stringmessage=awaitReadString(ws).ConfigureAwait(false);if(message.Contains("method")){stringreturnString=awaitJsonRpcProcessor.Process(message,connectionId);if(returnString.Length!=0){ArraySegment<byte>outputBuffer=newArraySegment<byte>(Encoding.UTF8.GetBytes(returnString));if(ws.State==WebSocketState.Open){awaitws.SendAsync(outputBuffer,WebSocketMessageType.Text,true,cancellation).ConfigureAwait(false);}}}}awaitws.CloseAsync(WebSocketCloseStatus.NormalClosure,"Done",CancellationToken.None);}catch(Exceptionex){try{awaitws.CloseAsync(WebSocketCloseStatus.InternalServerError,"Done",CancellationToken.None).ConfigureAwait(false);}catch{// Do nothing}}finally{Tuple<HttpListenerWebSocketContext,Guid?>client;Clients.TryGetValue(connectionId,outclient);if(client!=null){Clients.Remove(connectionId);}ws.Dispose();}}privatestaticasyncTask<String>ReadString(WebSocketws){ArraySegment<Byte>buffer=newArraySegment<byte>(newByte[8192]);WebSocketReceiveResultresult=null;using(varms=newMemoryStream()){do{result=awaitws.ReceiveAsync(buffer,CancellationToken.None);ms.Write(buffer.Array,buffer.Offset,result.Count);}while(!result.EndOfMessage);ms.Seek(0,SeekOrigin.Begin);using(varreader=newStreamReader(ms,Encoding.UTF8)){returnreader.ReadToEnd();}}}
#endregion
publicstaticSSTConfigTryAuthorizeConnection(GuidconnectionId,GuidsstId,stringmac,WorkingState[]states){varconfig=ConfigStorage.SstConfigs.FirstOrDefault(c =>c.Id==sstId);if(config==null)returnnull;if(!Clients.TryGetValue(connectionId,outvarclient))returnnull;client=newTuple<HttpListenerWebSocketContext,Guid?>(client.Item1,config.Id);Clients[connectionId]=client;returnconfig;}publicstaticasyncTaskNotify(stringrpcMethod,objectrpcParams){JsonNotificationrequest=newJsonNotification{Method=rpcMethod,Params=rpcParams};stringnotification=JsonConvert.SerializeObject(request);foreach(varclientinClients){ArraySegment<byte>outputBuffer=newArraySegment<byte>(Encoding.UTF8.GetBytes(notification));varcontext=client.Value.Item1;if(context.WebSocket.State==WebSocketState.Open){try{awaitclient.Value.Item1.WebSocket.SendAsync(outputBuffer,WebSocketMessageType.Text,true,CancellationToken.None);}}}}publicstaticasyncTaskNotifyClient(GuidclientId,stringrpcMethod,objectrpcParams){JsonNotificationrequest=newJsonNotification{Method=rpcMethod,Params=rpcParams};stringnotification=JsonConvert.SerializeObject(request);foreach(varclientinClients.Where(p=>p.Key==clientId)){ArraySegment<byte>outputBuffer=newArraySegment<byte>(Encoding.UTF8.GetBytes(notification));varcontext=client.Value.Item1;if(context.WebSocket.State==WebSocketState.Open){try{awaitclient.Value.Item1.WebSocket.SendAsync(outputBuffer,WebSocketMessageType.Text,true,CancellationToken.None);}}}}publicvoidDispose(){if(m_listener!=null&&m_cancellation!=null){try{m_cancellation.Cancel();m_listener.Stop();m_listener=null;m_cancellation=null;}catch{// Log error}}}}internalclassJsonNotification{publicJsonNotification(){}[JsonProperty("jsonrpc")]publicstringJsonRpc=>"2.0";[JsonProperty("method")]publicstringMethod{get;set;}[JsonProperty("params",NullValueHandling=NullValueHandling.Ignore)]publicobjectParams{get;set;}}

Clone this wiki locally