Frage Erstellen eines "Hallo Welt" -WebSocket-Beispiels


Ich verstehe nicht, warum ich den folgenden Code nicht funktionieren lassen kann. Ich möchte eine Verbindung mit JavaScript zu meiner Serverkonsolenanwendung herstellen. Und dann senden Sie Daten an den Server.

Hier ist der Servercode:

    static void Main(string[] args)
    {            
        TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 9998);
        server.Start();
        var client = server.AcceptTcpClient();
        var stream = client.GetStream();

        while (true)
        {
            var buffer = new byte[1024]; 
            // wait for data to be received
            var bytesRead = stream.Read(buffer, 0, buffer.Length);                
            var r = System.Text.Encoding.UTF8.GetString(buffer);
            // write received data to the console
            Console.WriteLine(r.Substring(0, bytesRead));
        }
    }

und hier ist das JavaScript:

        var ws = new WebSocket("ws://localhost:9998/service");
        ws.onopen = function () {
            ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
        };

        ws.onmessage = function (evt) {
            var received_msg = evt.data;
            alert("Message is received...");
        };
        ws.onclose = function () {
            // websocket is closed.
            alert("Connection is closed...");
        };

Wenn ich diesen Code ausführe, passiert Folgendes:

Beachten Sie, dass der Server beim Ausführen von JavaScript eine Verbindung akzeptiert und erfolgreich herstellt. JavaScript kann jedoch keine Daten senden. Immer wenn ich die send-Methode platziere, wird sie nicht gesendet, obwohl eine Verbindung hergestellt wurde. Wie kann ich das schaffen?


72
2018-04-18 00:01


Ursprung


Antworten:


WebSockets ist ein Protokoll, das auf der TCP-Streaming-Verbindung beruht. Obwohl WebSockets Message-basiertes Protokoll ist.

Wenn Sie Ihr eigenes Protokoll implementieren möchten, empfehle ich die Verwendung der neuesten und stabilsten Spezifikation (für 18/04/12) RFC 6455. Diese Spezifikation enthält alle notwendigen Informationen bezüglich Handshake und Framing. Sowie die meisten der Beschreibung von Szenarien des Verhaltens von Browser-Seite sowie von Server-Seite. Es wird dringend empfohlen, bei der Implementierung Ihres Codes den Empfehlungen zu folgen, die sich auf die Serverseite beziehen.

In wenigen Worten würde ich die Arbeit mit WebSockets wie folgt beschreiben:

  1. Erstellen Sie einen Server-Socket (System.Net.Sockets) binden Sie es an bestimmten Port und hören Sie weiter mit asynchroner Annahme von Verbindungen. So ähnlich:

    Socket serverSocket = neuer Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    serverSocket.Bind (neuer IPEndPoint (IPAddress.Any, 8080));
    serverSocket.Listen (128);
    serverSocket.BeginAccept (null, 0, OnAccept, null);
  2. Du solltest haben akzeptieren Funktion "OnAccept", die Handshake implementieren wird. In Zukunft muss es in einem anderen Thread sein, wenn das System eine große Anzahl von Verbindungen pro Sekunde verarbeiten soll.

    private void OnAccept (IAsyncResult-Ergebnis) {
    Versuchen {
        Socket-Client = null;
        if (serverSocket! = null && serverSocket.IsBound) {
            client = serverSocket.EndAccept (Ergebnis);
        }
        if (Client! = null) {
            / * Handshake und Verwaltung von ClientSocket * /
        }
    } catch (SocketException-Ausnahme) {
    
    } endlich {
        if (serverSocket! = null && serverSocket.IsBound) {
            serverSocket.BeginAccept (null, 0, OnAccept, null);
        }
    }
    }
  3. Nachdem die Verbindung hergestellt wurde, müssen Sie es tun Händedruck. Basierend auf Spezifikation 1.3 Öffnen des HandshakesNach dem Verbindungsaufbau erhalten Sie eine grundlegende HTTP-Anfrage mit einigen Informationen. Beispiel:

    GET / Chat HTTP / 1.1
    Gastgeber: server.example.com
    Upgrade: Websocket
    Verbindung: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ ==
    Herkunft: http://example.com
    Sec-WebSocket-Protokoll: Chat, Superchat
    Sec-WebSocket-Version: 13

    Dieses Beispiel basiert auf der Version von Protokoll 13. Bedenken Sie, dass ältere Versionen einige Unterschiede aufweisen, aber die meisten aktuellen Versionen sind kreuzkompatibel. Verschiedene Browser senden Ihnen möglicherweise zusätzliche Daten. Zum Beispiel Browser- und Betriebssystemdetails, Cache und andere.

    Basierend auf den bereitgestellten Handshake-Details müssen Sie Antwortzeilen generieren, diese sind meistens gleich, enthalten aber den Accpet-Key, der auf dem bereitgestellten Sec-WebSocket-Key basiert. In der Spezifikation 1.3 ist klar beschrieben, wie der Antwortschlüssel erzeugt wird. Hier ist meine Funktion, die ich für V13 verwendet habe:

    statische private Zeichenkette guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    private Zeichenfolge AcceptKey (Ref-Zeichenfolgenschlüssel) {
        Zeichenfolge langKey = Schlüssel + GUID;
        SHA1 sha1 = SHA1CryptoServiceProvider.Create ();
        byte [] hashBytes = sha1.ComputeHash (System.Text.Encoding.ASCII.GetBytes (longKey));
        Rückgabe Convert.ToBase64String (hashBytes);
    }
    

    Handshake-Antwort sieht so aus:

    HTTP / 1.1 101 Switching-Protokolle
    Upgrade: Websocket
    Verbindung: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK + xOo =

    Aber accept key muss der generierte sein, basierend auf dem bereitgestellten Schlüssel von client und Methode AcceptKey, den ich zuvor bereitgestellt habe. Stellen Sie außerdem sicher, dass Sie nach dem letzten Zeichen von accept key zwei neue Zeilen "\ r \ n \ r \ n" einfügen.

  4. Nachdem die Antwort des Handshakes vom Server gesendet wurde, sollte der Client "offen"Funktion, das heißt, Sie können Nachrichten nach senden.
  5. Nachrichten werden nicht im Rohformat gesendet, aber sie haben Datenrahmen. Und von Client zu Server ebenso implementieren Maskierung für Daten basierend auf bereitgestellten 4 Bytes im Nachrichtenkopf. Obwohl von Server zu Client müssen Sie keine Maskierung über Daten anwenden. Abschnitt lesen 5. Datenrahmen in der Spezifikation. Hier ist Kopieren-Einfügen von meiner eigenen Implementierung. Es ist kein gebrauchsfertiger Code und muss geändert werden, ich poste es nur, um eine Idee und eine allgemeine Logik von Lesen / Schreiben mit WebSocket-Framing zu geben. Gehe zu dieser Link.
  6. Nachdem das Framing implementiert wurde, stellen Sie sicher, dass Sie die Daten richtig mit Sockets erhalten. Zum Beispiel, um zu verhindern, dass einige Nachrichten zu einem zusammengeführt werden, weil TCP noch ein Stream-basiertes Protokoll ist. Das bedeutet, dass Sie NUR eine bestimmte Menge an Bytes lesen müssen. Länge der Nachricht basiert immer auf Header und bereitgestellten Daten Länge Details in Header selbst. Wenn Sie also Daten von Socket empfangen, erhalten Sie zuerst 2 Bytes, erhalten Details aus dem Header basierend auf der Framing-Spezifikation, wenn die Maske weitere 4 Bytes bereitstellt, und dann die Länge, die basierend auf der Länge der Daten 1, 4 oder 8 Bytes sein kann. Und nach Daten selbst. Nachdem Sie es gelesen haben, wenden Sie Demasking an und Ihre Nachrichtendaten können jetzt verwendet werden.
  7. Vielleicht möchten Sie einige verwenden Datenprotokoll, Ich empfehle, JSON aufgrund der Verkehrswirtschaftlichkeit zu verwenden und auf der Client-Seite in JavaScript einfach zu verwenden. Für Serverseite möchten Sie vielleicht einige Parser überprüfen. Es gibt viele von ihnen, Google kann wirklich hilfreich sein.

Das Implementieren eines eigenen WebSockets-Protokolls hat definitiv einige Vorteile und große Erfahrung, die Sie erhalten, sowie die Kontrolle über das Protokoll selbst. Aber Sie müssen einige Zeit damit verbringen und sicherstellen, dass die Implementierung sehr zuverlässig ist.

In der gleichen Zeit können Sie nach Lösungen suchen, die Google (wieder) genug haben.


57
2018-04-18 09:46



WebSockets sind implementiert mit einem Protokoll das beinhaltet Handshake zwischen Client und Server. Ich kann mir nicht vorstellen, dass sie sehr wie normale Steckdosen funktionieren. Lesen Sie das Protokoll und holen Sie sich Ihre Bewerbung. Verwenden Sie alternativ eine vorhandene WebSocket-Bibliothek oder .Net4.5beta mit a WebSocket-API.


4
2018-04-18 00:05



(Gepostet im Namen des OP).

Ich kann jetzt Daten senden. Dies ist meine neue Version des Programms dank Ihrer Antworten und dem Code von @Maksims Mihejevs.

Server

using System;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static Socket serverSocket = new Socket(AddressFamily.InterNetwork, 
        SocketType.Stream, ProtocolType.IP);
        static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

        static void Main(string[] args)
        {            
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
            serverSocket.Listen(128);
            serverSocket.BeginAccept(null, 0, OnAccept, null);            
            Console.Read();
        }

        private static void OnAccept(IAsyncResult result)
        {
            byte[] buffer = new byte[1024];
            try
            {
                Socket client = null;
                string headerResponse = "";
                if (serverSocket != null && serverSocket.IsBound)
                {
                    client = serverSocket.EndAccept(result);
                    var i = client.Receive(buffer);
                    headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0,i);
                    // write received data to the console
                    Console.WriteLine(headerResponse);

                }
                if (client != null)
                {
                    /* Handshaking and managing ClientSocket */

                    var key = headerResponse.Replace("ey:", "`")
                              .Split('`')[1]                     // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
                              .Replace("\r", "").Split('\n')[0]  // dGhlIHNhbXBsZSBub25jZQ==
                              .Trim();

                    // key should now equal dGhlIHNhbXBsZSBub25jZQ==
                    var test1 = AcceptKey(ref key);

                    var newLine = "\r\n";

                    var response = "HTTP/1.1 101 Switching Protocols" + newLine
                         + "Upgrade: websocket" + newLine
                         + "Connection: Upgrade" + newLine
                         + "Sec-WebSocket-Accept: " + test1 + newLine + newLine
                         //+ "Sec-WebSocket-Protocol: chat, superchat" + newLine
                         //+ "Sec-WebSocket-Version: 13" + newLine
                         ;

                    // which one should I use? none of them fires the onopen method
                    client.Send(System.Text.Encoding.UTF8.GetBytes(response));

                    var i = client.Receive(buffer); // wait for client to send a message

                    // once the message is received decode it in different formats
                    Console.WriteLine(Convert.ToBase64String(buffer).Substring(0, i));                    

                    Console.WriteLine("\n\nPress enter to send data to client");
                    Console.Read();

                    var subA = SubArray<byte>(buffer, 0, i);
                    client.Send(subA);
                    Thread.Sleep(10000);//wait for message to be send


                }
            }
            catch (SocketException exception)
            {
                throw exception;
            }
            finally
            {
                if (serverSocket != null && serverSocket.IsBound)
                {
                    serverSocket.BeginAccept(null, 0, OnAccept, null);
                }
            }
        }

        public static T[] SubArray<T>(T[] data, int index, int length)
        {
            T[] result = new T[length];
            Array.Copy(data, index, result, 0, length);
            return result;
        }

        private static string AcceptKey(ref string key)
        {
            string longKey = key + guid;
            byte[] hashBytes = ComputeHash(longKey);
            return Convert.ToBase64String(hashBytes);
        }

        static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
        private static byte[] ComputeHash(string str)
        {
            return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
        }
    }
}

JavaScript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
        function connect() {
            var ws = new WebSocket("ws://localhost:8080/service");
            ws.onopen = function () {
                alert("About to send data");
                ws.send("Hello World"); // I WANT TO SEND THIS MESSAGE TO THE SERVER!!!!!!!!
                alert("Message sent!");
            };

            ws.onmessage = function (evt) {
                alert("About to receive data");
                var received_msg = evt.data;
                alert("Message received = "+received_msg);
            };
            ws.onclose = function () {
                // websocket is closed.
                alert("Connection is closed...");
            };
        };


    </script>
</head>
<body style="font-size:xx-large" >
    <div>
    <a href="#" onclick="connect()">Click here to start</a></div>
</body>
</html>

Wenn ich diesen Code ausführe, kann ich sowohl vom Client als auch vom Server Daten senden und empfangen. Das einzige Problem ist, dass die Nachrichten verschlüsselt werden, wenn sie auf dem Server ankommen. Hier sind die Schritte, wie das Programm läuft:

enter image description here

Beachten Sie, wie die Nachricht vom Client verschlüsselt wird.


4
2018-04-18 00:14



Problem

Da Sie WebSocket verwenden, ist Spender korrekt. Nachdem Sie die Ausgangsdaten vom WebSocket erhalten haben, müssen Sie die Handshake-Nachricht vom C # -Server senden, bevor weitere Informationen fließen können.

HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: websocket
Connection: Upgrade
WebSocket-Origin: example
WebSocket-Location: something.here
WebSocket-Protocol: 13

Etwas in dieser Richtung.

Sie können mehr darüber herausfinden, wie WebSocket auf w3 oder google funktioniert.

Links und Ressourcen

Hier ist eine Protokollspezifikation: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

Liste von Arbeitsbeispielen:


2