0

I'm writing a websocket server using .NET's HttpListener class.

Essentially, I've got a HandleListener() function which wait for clients to connect and yield each client to HandleClient(WebSocket client). So I currently have:

 private async void HandleListener() { try { while (listener != null && listener.IsListening) { HttpListenerContext listenerContext = await listener.GetContextAsync(); WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null); WebSocket webSocket = webSocketContext.WebSocket; clients.Add(webSocket); await HandleClient(webSocket); } } catch (HttpListenerException) { } // Got here probably because StopWSServer() was called } private async Task HandleClient(WebSocket client) { ... } 

Problem is, I can't seem to process more then one client. It looks like the execution of HandleListener() halts as long as the first client is connected.

I tried removing the await from the call to HandleClient(), but I get the "because this call is not awaited..." error. I can make HandleClient() a async void method, but this is not an event handler.
BTW, the reason that HandleClient() is async Task is because it's doing, all over in a loop until the listener is dead:

recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None); 

From what I understand, a fire-and-forget approach is bad overall, and I can't seem to achieve it with async-await implementation. But HandleClient() is a fire-and-forget method, and I don't see any other way of achieving what I need.


EDIT: Added current implementation of HandleClient():

 private async Task HandleClient(WebSocket client) { try { ArraySegment<byte> recievedBuffer = new ArraySegment<byte>(new byte[BUFFER_SIZE]); while (listener != null && listener.IsListening && client.State == WebSocketState.Open) { WebSocketReceiveResult recieveResult; using (var ms = new MemoryStream()) { do { recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None); ms.Write(recievedBuffer.Array, recievedBuffer.Offset, recieveResult.Count); } while (!recieveResult.EndOfMessage); switch (recieveResult.MessageType) { case WebSocketMessageType.Close: RemoveClient(client, WebSocketCloseStatus.NormalClosure, string.Empty); break; case WebSocketMessageType.Binary: RemoveClient(client, WebSocketCloseStatus.InvalidMessageType, "Cannot accept binary frame"); break; case WebSocketMessageType.Text: OnRecieve?.Invoke(client, System.Text.Encoding.UTF8.GetString(ms.ToArray())); break; } } } } catch (WebSocketException ex) { RemoveClient(client, WebSocketCloseStatus.InternalServerError, ex.Message); } } 
4
  • What does HandleClient do? You say it's fire and forget. But why is it async then? Commented Nov 30, 2017 at 9:20
  • It's async because it does ...await client.ReceiveAsync... all over in a loop. I've edited the question to include this. Commented Nov 30, 2017 at 9:26
  • But that's not fire and forget. When you are waiting for the receive to complete, you are not forgetting. It seems the issue is in the structure of your code. One line from HandleClient is not enough to help to resolve that. Commented Nov 30, 2017 at 10:02
  • I've added the HandleClient() implementation. Commented Nov 30, 2017 at 10:14

2 Answers 2

3

To prevent compiler warning, use method like this:

public static class TaskExtensions { public static void Forget(this Task task) { } } 

then just do

HandleClient(webSocket).Forget() 

If you go this route, ensure that you handle all exceptions inside HandleClient somehow (wrap whole thing into try-catch for example). There is nothing inherently "bad" in this approach in this particular case.

Alternative approach would be:

HandleClient(webSocket).ContinueWith(task => { if (task.IsFaulted && task.Exception != null) { // handle it here } }); 

awaiting HandleClient is not an option in this case, as you see yourself.

Sign up to request clarification or add additional context in comments.

Comments

1

it will do like that because you wrote code for it, my mean to say you wrote method as below.

 private async void HandleListener() { try { while (listener != null && listener.IsListening) { HttpListenerContext listenerContext = await listener.GetContextAsync(); WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null); WebSocket webSocket = webSocketContext.WebSocket; clients.Add(webSocket); await HandleClient(webSocket); } } catch (HttpListenerException) { } // Got here probably because StopWSServer() was called } 

In this method when it encounter await control will get return to orignal caller ,till you await part got completed and next call start after it.

Check below image this how await and async works

enter image description here

If you just want fire and forget than try like this

 private void HandleListener() { try { while (listener != null && listener.IsListening) { HttpListenerContext listenerContext = await listener.GetContextAsync(); WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null); WebSocket webSocket = webSocketContext.WebSocket; clients.Add(webSocket); HandleClient(webSocket); } } catch (HttpListenerException) { } // Got here probably because StopWSServer() was called } 

which means dont wait for completion of task

6 Comments

This is a highly risky approach. You don't know whether HandleClient is async itself and what it does.
Actually, HandleClient is async
@Sefe - know but he wants fire and forget scenario that why i wrote code
@galah92 - in that case you just remove Task.Factory.StartNew( this call
But then I get "because this call is not awaited..." warning, and it feels like it's not a good practice.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.