1

I am trying to use WebSockets on Windows 7. I trying to use System.Net.Sockets, I have tried following these examples, but each of them has one problem or another running on Windows 7:

WebSocket Server in C# ,Writing a WebSocket server in C# , Creating a “Hello World” WebSocket example , How to implement an asynchronous socket in C#, MULTI-THREADED TCP SERVER IN C# ,Paul Batum

I have looked into several 3rd party tools, but it looks like they are either no longer supported or in beta.

Here's the code I have so far:

public class WebSocketService {
    private TcpListener _listener;
    private TcpClient _tcpClient;
    public event Action<NetworkStream> OnLoadData;

    public WebSocketService() {
       Listen();
    }

    public void Listen(int port) {
       _listener = new TcpListener(IPAddress.Parse("127.0.0.1"), port);
       _listener.Start();
       _listener.BeginAcceptTcpClient(HandleAsyncConnection, null);
    }

    private void HandleAsyncConnection(IAsyncResult result)
    {
        try
        {
            _tcpClient = _listener.EndAcceptTcpClient(result);
            _tcpClient.NoDelay = true;
            NetworkStream stream = _tcpClient.GetStream();

            // extract the connection details and use those details to build a connection
            ConnectionDetails connectionDetails = GetConnectionDetails(stream, _tcpClient);
            PerformHandshake(stream, connectionDetails.Header);

            if (connectionDetails.Path.Equals("/myApiPath"))
            {
                OnLoadData?.Invoke(stream);
            }

        }
        catch (Exception ex)
        {
            //write ex to log
            throw;
        }
    }

    private static ConnectionDetails GetConnectionDetails(NetworkStream stream, TcpClient tcpClient)
    {
        // read the header and check that it is a GET request
        string header = HttpHelper.ReadHttpHeader(stream);
        var getRegex = new Regex(@"^GET(.*)HTTP\/1\.1", RegexOptions.IgnoreCase);

        Match getRegexMatch = getRegex.Match(header);
        if (getRegexMatch.Success)
        {
            // extract the path attribute from the first line of the header
            string path = getRegexMatch.Groups[1].Value.Trim();

            // check if this is a web socket upgrade request
            var webSocketUpgradeRegex = new Regex("Upgrade: websocket", RegexOptions.IgnoreCase);
            Match webSocketUpgradeRegexMatch = webSocketUpgradeRegex.Match(header);

            if (webSocketUpgradeRegexMatch.Success)
            {
                return new ConnectionDetails(stream, tcpClient, path, ConnectionType.WebSocket, header);
            }

            return new ConnectionDetails(stream, tcpClient, path, ConnectionType.Http, header);
        }

        return new ConnectionDetails(stream, tcpClient, string.Empty, ConnectionType.Unknown, header);
    }

    private void PerformHandshake(Stream stream, string header)
    {
       try
        {
            Regex webSocketKeyRegex = new Regex("Sec-WebSocket-Key: (.*)");
            Regex webSocketVersionRegex = new Regex("Sec-WebSocket-Version: (.*)");

            // check the version. Support version 13 and above
            const int webSocketVersion = 13;
            int secWebSocketVersion = Convert.ToInt32(webSocketVersionRegex.Match(header).Groups[1].Value.Trim());
            if (secWebSocketVersion < webSocketVersion)
            {
                //throw some exception
            }

            string secWebSocketKey = webSocketKeyRegex.Match(header).Groups[1].Value.Trim();
            string setWebSocketAccept = ComputeSocketAcceptString(secWebSocketKey);
            var newLine = "\r\n";

            string response = ("HTTP/1.1 101 Switching Protocols" + newLine
                               + "Connection: Upgrade" + newLine
                               + "Upgrade: websocket" + newLine
                               + "Sec-WebSocket-Accept: " + setWebSocketAccept + newLine + newLine);

            WriteHttpHeader(response, stream);

        }
        catch (Exception ex)
        {
            // Write Log Entry(ex);
            WriteHttpHeader("HTTP/1.1 400 Bad Request", stream);
            throw;
        }
    }
}

When the OnLoadData event fires it calls another class which simply has:

var jsonSerializerSettings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            };

            var message = JsonConvert.SerializeObject(someData, jsonSerializerSettings);
            var messageData = Encoding.UTF8.GetBytes(message);


            _clientStream.Write(messageData, 0, messageData.Length);
            _clientStream.Flush();

I then get this error:

An established connection was aborted by the software in your host machine at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) at System.Net.Sockets.NetworkStream.Write(Byte[] buffer, Int32 offset, Int32 size) NativeErrorCode 10053 int SocketErrorCode ConnectionAborted System.Net.Sockets.SocketError

I am looking for a simple C# WebSockets implementation that works on Windows 7. I cannot use SignalR or ASP.NET because I am using OWIN to self-host Web API.

I took a look at this post, but I can't figure out how the OP made it work. Anyone know how to do this?

GamerDev
  • 2,006
  • 4
  • 24
  • 31
  • You are using asynchronous receive so you must use asynchronous send (not synchronous send). See the msdn examples. The examples use socket but can be replace with any class that inherits sockets like TcpListener or TcpClient : https://learn.microsoft.com/en-us/dotnet/framework/network-programming/socket-code-examples – jdweng Nov 25 '17 at 02:15
  • I tried using BeginWrite method on NetworkStream, but I still get the same error "Unable to write data to the transport connection: An established connection was aborted by the software in your host machine." – GamerDev Nov 25 '17 at 11:59
  • You have a TcpListener so why aren't you using BeginSend() like the msdn examples? – jdweng Nov 25 '17 at 12:32
  • I am using a NetworkStream object and it doesn't have a BeginSend method only a BeginWrite method. Also, I do not see a BeginSend method on the TcpListener object. – GamerDev Nov 25 '17 at 12:35
  • You transport layer on the receive end is using TcpListener so you must write to the same TcpListener. The write must be done in a BeginSend. So if you are using a NetworkStream in the Begin Send you must read the NetworkStream and write to the TcpListener (the opposite of what you are doing when reading). When you are reading you are reading from the TcpListener and writing to the Network Stream. – jdweng Nov 25 '17 at 12:45
  • I don't see a BeginSend method on the TcpListner class. Do you have any code you can share? TcpListner see https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener(v=vs.110).aspx – GamerDev Nov 25 '17 at 12:49
  • It is a casting issue. The TcpListener is a subset of the base class socket. It only exposes some of the socket properties. To get the reset of the properties you have to do a cast : ((Socket)_listener).BeginSend – jdweng Nov 25 '17 at 12:59
  • I am unable to cast TcpListner object to a Socket object. I get this error: Cannot convert type 'System.Net.Sockets.TcpListener' to 'System.Net.Sockets.Socket' – GamerDev Nov 25 '17 at 13:08
  • Sorry about the mistake. The socket for the Listener is _listener.Server while the socket for the TcpClient is _client.Client. So use _listener.Server.BeginSend. – jdweng Nov 25 '17 at 13:15
  • Using _listener.Server.BeginSend(data, 0, data.Length, SocketFlags.None, WriteCallback, null); throws SocketException A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied. I don't think using the underlying socket will work. Do you have any code you can share? – GamerDev Nov 25 '17 at 13:47
  • You cannot send from server until a client connects. – jdweng Nov 25 '17 at 20:53
  • SignalR is well supported on OWIN, so I wonder why you ask for raw WebSockets. As Windows 7 does not support WebSockets at all, you need to use a third party library, such as https://github.com/sta/websocket-sharp – Lex Li Nov 25 '17 at 21:11
  • @jdweng Yes, I know I can't send from server until client connects. This is what needs to be sent to the client once they connect. – GamerDev Nov 26 '17 at 11:45
  • @Lex Li I do not wish to use SignalR because I have an Angular 4 application. – GamerDev Nov 26 '17 at 11:46
  • the error message indicates either you are sending before the client connects or sending after the client disconnects. – jdweng Nov 26 '17 at 12:31

0 Answers0