it-swarm.com.de

WebSocket-Beispiel "Hello World" erstellen

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

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 Sendemethode platziere, wird sie nicht gesendet, obwohl eine Verbindung hergestellt wurde. Wie kann ich diese Arbeit machen?

74
Tono Nam

WebSockets ist ein Protokoll, das auf einer TCP gestreamten Verbindung basiert. Obwohl WebSockets ein Message-basiertes Protokoll ist.

Wenn Sie ein eigenes Protokoll implementieren möchten, empfehle ich die Verwendung der neuesten und stabilen Spezifikation (für 18/04/12) RFC 6455 . Diese Spezifikation enthält alle erforderlichen Informationen zu Handshake und Framing. Die meisten Beschreibungen zu Szenarien des Verhaltens sowohl auf der Browserseite als auch auf der Serverseite . Es wird dringend empfohlen, die Empfehlungen bezüglich der Serverseite während der Implementierung Ihres Codes zu befolgen.

In wenigen Worten würde ich die Arbeit mit WebSockets folgendermaßen beschreiben:

  1. Server-Socket erstellen (System.Net.Sockets) binden Sie es an einen bestimmten Port und überwachen Sie es weiterhin, indem Sie asynchrone Verbindungen annehmen. Sowas in der Art:

    Socket serverSocket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); 
     ServerSocket.Bind (new IPEndPoint (IP-Adresse.Any, 8080)); 
     ServerSocket.Listen (128); 
     serverSocket.BeginAccept (null, 0, OnAccept, null);
  2. Sie sollten Annehmen Funktion "OnAccept" haben, die Handshake implementiert. 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) {
     try {
     Socket-Client = null; 
     if (serverSocket! = null && serverSocket.IsBound) {
     Client = ServerSocket.EndAccept (Ergebnis); 
     } 
     if (client! = null) {
    /* Handshaking und Verwalten von ClientSocket */
     } 
    } catch (SocketException-Ausnahme) {
    
    } Schließlich {
     if (serverSocket! = null && serverSocket.IsBound) {
     serverSocket.BeginAccept (null, 0, OnAccept, null); 
     } 
    } 
    }
  3. Nachdem die Verbindung hergestellt wurde, müssen Sie Handshake ausführen. Basierend auf den Angaben 1.3 Opening Handshake erhalten Sie nach dem Herstellen der Verbindung eine grundlegende HTTP-Anforderung mit einigen Informationen. Beispiel:

    GET/chat HTTP/1.1 
     Host: server.example.com 
     Upgrade: Websocket 
     Verbindung: Upgrade 
     Sec-WebSocket-Schlüssel: dGhlIHNhbXBsZSBub25jZQ == 
     Ursprung: http: // .com 
     Sec-WebSocket-Protokoll: chat, superchat 
     Sec-WebSocket-Version: 13

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

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

    statische private Zeichenfolge guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 
     private Zeichenfolge AcceptKey (Referenzzeichenfolge) {
     Zeichenfolge longKey = key + guid; 
     SHA1 sha1 = SHA1CryptoServiceProvider.Create (); 
     byte [] hashBytes = sha1.ComputeHash (System.Text.Encoding.ASCII.GetBytes (longKey)); 
     return Convert.ToBase64String (hashBytes); 
    } 
    

    Die Handshake-Antwort sieht so aus:

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

    Der Accept-Schlüssel muss jedoch der generierte Schlüssel sein, der auf dem vom Client und der Methode AcceptKey I bereitgestellten Schlüssel basiert. Stellen Sie außerdem sicher, dass Sie nach dem letzten Zeichen der Übernahmetaste zwei neue Zeilen "\ r\n\r\n" einfügen.

  4. Nachdem die Handshake-Antwort vom Server gesendet wurde, sollte der Client die Funktion "onopen" auslösen, dh, Sie können Nachrichten danach senden.
  5. Nachrichten werden nicht im Rohformat gesendet, sie verfügen jedoch über Data Framing. Und implementieren Sie die Maskierung für Daten von Client zu Server basierend auf den bereitgestellten 4 Byte im Nachrichtenkopf. Obwohl von Server zu Client keine Maskierung über Daten erforderlich ist. Lesen Sie Abschnitt 5. Datenrahmen in Spezifikation ..__ Hier ist das Kopieren und Einfügen aus meiner eigenen Implementierung. Es ist kein Code, der sofort verwendet werden kann, und muss geändert werden. Ich poste ihn nur, um eine Idee und eine allgemeine Logik zum Lesen/Schreiben mit WebSocket-Framing zu geben. Gehe zu diesem Link .
  6. Stellen Sie nach der Implementierung des Frames sicher, dass Sie Daten mithilfe von Sockets richtig empfangen. Um beispielsweise zu verhindern, dass einige Nachrichten zu einer Nachricht zusammengefügt werden, da TCP immer noch ein Stream-basiertes Protokoll ist. Das heißt, Sie müssen NUR eine bestimmte Anzahl von Bytes lesen. Die Länge der Nachricht basiert immer auf dem Header und den angegebenen Datenlängenangaben im Header. Wenn Sie also Daten von Socket empfangen, erhalten Sie zunächst 2 Bytes, erhalten Sie basierend auf der Framing-Spezifikation Details aus dem Header. Wenn die Maske weitere 4 Bytes bereitstellt, dann die Länge, die je nach Länge der Daten 1, 4 oder 8 Bytes betragen kann. Und nach Daten selbst. Wenden Sie demasking nach dem Lesen an, und Ihre Nachrichtendaten sind einsatzbereit.Vielleicht möchten Sie Data Protocol verwenden. Ich empfehle die Verwendung von JSON, da dies aus Gründen des Verkehrsaufkommens einfach ist und auf Clientseite in JavaScript einfach zu verwenden ist. Auf der Serverseite sollten Sie einige Parser überprüfen. Es gibt viele davon, Google kann sehr hilfreich sein.
  7. Die Implementierung des eigenen WebSockets-Protokolls hat definitiv einige Vorteile und eine großartige Erfahrung, die Sie erhalten, sowie die Kontrolle über das Protokoll selbst. Sie müssen jedoch einige Zeit damit verbringen und sicherstellen, dass die Implementierung äußerst zuverlässig ist.

In der gleichen Zeit können Sie einen Blick auf die gebrauchsfertigen Lösungen werfen, die Google (wieder) genug hat.

In same time you might have a look in ready to use solutions that google (again) have enough.

58
moka

(Gepostete Antwort im Namen des OP) .

Ich kann jetzt Daten senden. Dies ist meine neue Version des Programms dank Ihrer Antworten und des Codes 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ühren, kann ich Daten sowohl vom Client als auch vom Server senden und empfangen. Das einzige Problem ist, dass die Nachrichten verschlüsselt werden, wenn sie beim 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. 

6
halfer

WebSockets werden mit einem Protokoll implementiert , das Handshake zwischen Client und Server beinhaltet. Ich kann mir nicht vorstellen, dass sie wie normale Steckdosen funktionieren. Informieren Sie sich über das Protokoll und lassen Sie Ihre Anwendung darüber sprechen. Verwenden Sie alternativ eine vorhandene WebSocket-Bibliothek oder .Net4.5beta, die über eine WebSocket-API verfügt.

5
spender

Problem

Da Sie WebSocket verwenden, ist Spender korrekt. Nachdem Sie die ursprünglichen Daten von WebSocket erhalten haben, müssen Sie die Handshake-Nachricht vom C # -Server senden, bevor weitere Informationen übertragen werden 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 diese Richtung.

Sie können weitere Untersuchungen zur Funktionsweise von WebSocket auf W3 oder Google durchführen.

Links und Ressourcen

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

Liste der Arbeitsbeispiele:

2
caesay

Sie können diese einfachen Beispiele ausprobieren ...

0
Rahamath

Ich konnte nirgendwo ein einfaches Arbeitsbeispiel finden (ab 19. Januar), daher hier eine aktualisierte Version. Ich habe Chromversion 71.0.3578.98.

C # Websocket-Server:  

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

namespace WebSocketServer
{
    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(1); //just one socket
        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);
                Console.WriteLine("=====================");
            }
            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
                     ;

                client.Send(System.Text.Encoding.UTF8.GetBytes(response));
                var i = client.Receive(buffer); // wait for client to send a message
                string browserSent = GetDecodedData(buffer, i);
                Console.WriteLine("BrowserSent: " + browserSent);

                Console.WriteLine("=====================");
                //now send message to client
                client.Send(GetFrameFromString("This is message from server to client."));
                System.Threading.Thread.Sleep(10000);//wait for message to be sent
            }
        }
        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));
    }

    //Needed to decode frame
    public static string GetDecodedData(byte[] buffer, int length)
    {
        byte b = buffer[1];
        int dataLength = 0;
        int totalLength = 0;
        int keyIndex = 0;

        if (b - 128 <= 125)
        {
            dataLength = b - 128;
            keyIndex = 2;
            totalLength = dataLength + 6;
        }

        if (b - 128 == 126)
        {
            dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0);
            keyIndex = 4;
            totalLength = dataLength + 8;
        }

        if (b - 128 == 127)
        {
            dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
            keyIndex = 10;
            totalLength = dataLength + 14;
        }

        if (totalLength > length)
            throw new Exception("The buffer length is small than the data length");

        byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] };

        int dataIndex = keyIndex + 4;
        int count = 0;
        for (int i = dataIndex; i < totalLength; i++)
        {
            buffer[i] = (byte)(buffer[i] ^ key[count % 4]);
            count++;
        }

        return Encoding.ASCII.GetString(buffer, dataIndex, dataLength);
    }

    //function to create  frames to send to client 
    /// <summary>
    /// Enum for opcode types
    /// </summary>
    public enum EOpcodeType
    {
        /* Denotes a continuation code */
        Fragment = 0,

        /* Denotes a text code */
        Text = 1,

        /* Denotes a binary code */
        Binary = 2,

        /* Denotes a closed connection */
        ClosedConnection = 8,

        /* Denotes a ping*/
        Ping = 9,

        /* Denotes a pong */
        Pong = 10
    }

    /// <summary>Gets an encoded websocket frame to send to a client from a string</summary>
    /// <param name="Message">The message to encode into the frame</param>
    /// <param name="Opcode">The opcode of the frame</param>
    /// <returns>Byte array in form of a websocket frame</returns>
    public static byte[] GetFrameFromString(string Message, EOpcodeType Opcode = EOpcodeType.Text)
    {
        byte[] response;
        byte[] bytesRaw = Encoding.Default.GetBytes(Message);
        byte[] frame = new byte[10];

        int indexStartRawData = -1;
        int length = bytesRaw.Length;

        frame[0] = (byte)(128 + (int)Opcode);
        if (length <= 125)
        {
            frame[1] = (byte)length;
            indexStartRawData = 2;
        }
        else if (length >= 126 && length <= 65535)
        {
            frame[1] = (byte)126;
            frame[2] = (byte)((length >> 8) & 255);
            frame[3] = (byte)(length & 255);
            indexStartRawData = 4;
        }
        else
        {
            frame[1] = (byte)127;
            frame[2] = (byte)((length >> 56) & 255);
            frame[3] = (byte)((length >> 48) & 255);
            frame[4] = (byte)((length >> 40) & 255);
            frame[5] = (byte)((length >> 32) & 255);
            frame[6] = (byte)((length >> 24) & 255);
            frame[7] = (byte)((length >> 16) & 255);
            frame[8] = (byte)((length >> 8) & 255);
            frame[9] = (byte)(length & 255);

            indexStartRawData = 10;
        }

        response = new byte[indexStartRawData + length];

        int i, reponseIdx = 0;

        //Add the frame bytes to the reponse
        for (i = 0; i < indexStartRawData; i++)
        {
            response[reponseIdx] = frame[i];
            reponseIdx++;
        }

        //Add the data bytes to the response
        for (i = 0; i < length; i++)
        {
            response[reponseIdx] = bytesRaw[i];
            reponseIdx++;
        }

        return response;
    }
}
}

Client HTML und 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">
        var socket = new WebSocket('ws://localhost:8080/websession');
        socket.onopen = function() {
           // alert('handshake successfully established. May send data now...');
		   socket.send("Hi there from browser.");
        };
		socket.onmessage = function (evt) {
                //alert("About to receive data");
                var received_msg = evt.data;
                alert("Message received = "+received_msg);
            };
        socket.onclose = function() {
            alert('connection closed');
        };
    </script>
</head>
<body>
</body>
</html>

0
Deepak Garud