How to work with System.Net.WebSockets without ASP.NET?

29,848

Solution 1

Yes.

The easiest way is to use an HTTPListener. If you search for HTTPListener WebSocket you'll find plenty of examples.

In a nutshell (pseudo-code)

HttpListener httpListener = new HttpListener();
httpListener.Prefixes.Add("http://localhost/");
httpListener.Start();

HttpListenerContext context = await httpListener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
    HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
    WebSocket webSocket = webSocketContext.WebSocket;
    while (webSocket.State == WebSocketState.Open)
    {
        await webSocket.SendAsync( ... );
    }
}

Requires .NET 4.5 and Windows 8 or later.

Solution 2

I just stumbled on this link that shows how to implement a IHttpHandler using just the System.Net.WebSockets implementation. The handler is required as the .NET WebSocket implementation is dependent on IIS 8+.

using System;
using System.Web;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.WebSockets;

namespace AspNetWebSocketEcho
{
    public class EchoHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            if (context.IsWebSocketRequest)
                context.AcceptWebSocketRequest(HandleWebSocket);
            else
                context.Response.StatusCode = 400;
        }

        private async Task HandleWebSocket(WebSocketContext wsContext)
        {
            const int maxMessageSize = 1024;
            byte[] receiveBuffer = new byte[maxMessageSize];
            WebSocket socket = wsContext.WebSocket;

            while (socket.State == WebSocketState.Open)
            {
                WebSocketReceiveResult receiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);

                if (receiveResult.MessageType == WebSocketMessageType.Close)
                {
                    await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                }
                else if (receiveResult.MessageType == WebSocketMessageType.Binary)
                {
                    await socket.CloseAsync(WebSocketCloseStatus.InvalidMessageType, "Cannot accept binary frame", CancellationToken.None);
                }
                else
                {
                    int count = receiveResult.Count;

                    while (receiveResult.EndOfMessage == false)
                    {
                        if (count >= maxMessageSize)
                        {
                            string closeMessage = string.Format("Maximum message size: {0} bytes.", maxMessageSize);
                            await socket.CloseAsync(WebSocketCloseStatus.MessageTooLarge, closeMessage, CancellationToken.None);
                            return;
                        }

                        receiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer, count, maxMessageSize - count), CancellationToken.None);
                        count += receiveResult.Count;
                    }

                    var receivedString = Encoding.UTF8.GetString(receiveBuffer, 0, count);
                    var echoString = "You said " + receivedString;
                    ArraySegment<byte> outputBuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(echoString));

                    await socket.SendAsync(outputBuffer, WebSocketMessageType.Text, true, CancellationToken.None);
                }
            }
        }

        public bool IsReusable
        {
            get { return true; }
        }
    }
}

Hope it helped!

Solution 3

Ian's answer definitely was good, but I needed a loop process. The mutex was key for me. This is a working .net core 2 example based on his. I can't speak to scalability of this loop.

using System;
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Threading;


namespace WebSocketServerConsole
{
    public class Program
    {
        static HttpListener httpListener = new HttpListener();
        private static Mutex signal = new Mutex();
        public static void Main(string[] args)
        {
            httpListener.Prefixes.Add("http://localhost:8080/");
            httpListener.Start();
            while (signal.WaitOne())
            {
                ReceiveConnection();
            }

        }

        public static async System.Threading.Tasks.Task ReceiveConnection()
        {
            HttpListenerContext context = await 
            httpListener.GetContextAsync();
            if (context.Request.IsWebSocketRequest)
            {
                HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
                WebSocket webSocket = webSocketContext.WebSocket;
                while (webSocket.State == WebSocketState.Open)
                {
                    await webSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello world")),
                        WebSocketMessageType.Text, true, CancellationToken.None);
                }
            }
            signal.ReleaseMutex();
        }
    }
}

and a test html page for it.

<!DOCTYPE html>
  <meta charset="utf-8" />
  <title>WebSocket Test</title>
  <script language="javascript" type="text/javascript">

  var wsUri = "ws://localhost:8080/";
  var output;

  function init()
  {
    output = document.getElementById("output");
    testWebSocket();
  }

  function testWebSocket()
  {
    websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }

  function onOpen(evt)
  {
    writeToScreen("CONNECTED");
    doSend("WebSocket rocks");
  }

  function onClose(evt)
  {
    writeToScreen("DISCONNECTED");
  }

  function onMessage(evt)
  {
    writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
  }

  function onError(evt)
  {
    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
  }

  function doSend(message)
  {
    writeToScreen("SENT: " + message);
    websocket.send(message);
  }

  function writeToScreen(message)
  {
    var pre = document.createElement("p");
    pre.style.wordWrap = "break-word";
    pre.innerHTML = message;
    output.appendChild(pre);
  }

  window.addEventListener("load", init, false);

  </script>

  <h2>WebSocket Test</h2>

  <div id="output"></div>
Share:
29,848
Syroot
Author by

Syroot

Not here to do your homework or debugging.

Updated on January 23, 2022

Comments

  • Syroot
    Syroot over 2 years

    I want to implement a simple chat server with the new System.Net.WebSockets classes in .NET 4.5 and later (on Windows 8.1). However, I only find examples making use of those classes in an ASP.NET environment (especially the ones here: http://www.codemag.com/Article/1210051)

    I don't have such one, and would like to implement the websocket server as "raw" as possible, but without having to reimplement all the websocket protocol as Microsoft hopefully already did that in .NET 4.5.

    I thought of simply instantiating a new WebSocket class like I'd do with a normal Socket, but the constructor is protected. So I went to create a class inheriting from it, but then I noticed I had to implement so many abstract methods and properties that it looked like I'm rewriting the whole logic (especially because I had to implement things like State or SendAsync).

    I'm afraid that the MSDN documentation didn't help me. The documentation there has a pre-release status and many comments just say "TBD" or "when its implemented".