Création d'un exemple WebSocket" Hello World"

Je ne comprends pas pourquoi je ne peux pas faire fonctionner le code suivant. Je veux me connecter avec JavaScript à mon application de console de serveur. Et puis envoyez des données au serveur.

Voici le code du serveur:

    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));
        }
    }

Et voici le 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...");
        };

Lorsque j'exécute ce code c'est ce qui se passe:

Notez que lorsque J'exécute le JavaScript, le serveur accepte et établit avec succès une connexion. JavaScript n'est pas capable d'envoyer des données. Chaque fois que je place la méthode send, elle n'enverra pas même si une connexion est établie. Comment puis-je faire ce travail?

72
demandé sur Umair M 2012-04-18 04:01:59

4 réponses

WebSockets est un protocole qui repose sur une connexion TCP en streaming. Bien que WebSockets soit un protocole basé sur les messages.

Si vous voulez implémenter votre propre protocole, je recommande d'utiliser les spécifications les plus récentes et stables (pour 18/04/12) RFC 6455 . Cette spécification contient toutes les informations nécessaires concernant la poignée de main et le cadrage. Ainsi plus de description sur les scénarios de comportement de votre navigateur ainsi que du côté serveur. Il est fortement recommandé de suivre ce les recommandations indiquent en ce qui concerne le côté serveur lors de l'implémentation de votre code.

En quelques mots, je décrirais travailler avec WebSockets comme ceci:

  1. Create server Socket (System. Net. Sockets) le lie à un port spécifique et continue d'écouter avec l'acceptation asynchrone des connexions. Quelque chose comme ça:

    Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
    serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
    serverSocket.Listen(128);
    serverSocket.BeginAccept(null, 0, OnAccept, null);
  2. Vous devriez avoir accepter fonction "OnAccept" qui implémentera handshake. À l'avenir il doit être dans un autre thread si le système est destiné à gérer une énorme quantité de connexions par seconde.

    private void OnAccept(IAsyncResult result) {
    try {
        Socket client = null;
        if (serverSocket != null && serverSocket.IsBound) {
            client = serverSocket.EndAccept(result);
        }
        if (client != null) {
            /* Handshaking and managing ClientSocket */
        }
    } catch(SocketException exception) {
    
    } finally {
        if (serverSocket != null && serverSocket.IsBound) {
            serverSocket.BeginAccept(null, 0, OnAccept, null);
        }
    }
    }
  3. Une fois la connexion établie, vous devez faire handshake . Basé sur la spécification 1.3 ouverture de la poignée de main , après la connexion établie, vous recevrez une requête HTTP de base avec quelques informations. Exemple:

    GET /chat HTTP/1.1
    Host: server.example.com
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Protocol: chat, superchat
    Sec-WebSocket-Version: 13

    Cet exemple est basé sur la version du protocole 13. Gardez à l'esprit que les anciennes versions ont quelques différences, mais la plupart des dernières versions sont compatibles entre elles. Différents navigateurs peuvent vous envoyer des données supplémentaires. Par exemple, les détails du navigateur et du système d'exploitation, le cache et d'autres.

    En fonction des détails de la poignée de main fournis, vous devez générer des lignes de réponse, elles sont pour la plupart les mêmes, mais contiendront Accpet-Key, qui est basé sur Sec-WebSocket-Key fourni. Dans la spécification 1.3 il est décrit clairement comment générer une réponse clé. Voici ma fonction que j'ai utilisée pour V13:

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

    La réponse de la poignée de main ressemble à ceci:

    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

    Mais accepter clé ont être celui généré en fonction de la clé fournie par le client et la méthode AcceptKey que j'ai fournie auparavant. Ainsi, assurez-vous après le dernier caractère de la touche accept vous mettez deux nouvelles lignes "\r\n\r\n".

  4. Après que la réponse de handshake soit envoyée du serveur, le client devrait déclencher la fonction" onopen", cela signifie que vous pouvez envoyer des messages après.
  5. Les Messages ne sont pas envoyés au format raw, mais ils ont Data Framing . Et du client au serveur implémentez également le masquage pour les données sur fourni 4 octets dans l'en-tête du message. Bien que du serveur au client, vous n'avez pas besoin d'appliquer un masquage sur les données. Lire la section 5. Cadrage des données dans la spécification. Voici copier-coller de ma propre implémentation. Ce n'est pas du code prêt à l'emploi, et doit être modifié, je le poste juste pour donner une idée et une logique globale de lecture/écriture avec WebSocket framing. Aller à ce lien.
  6. Une fois le cadrage implémenté, assurez-vous de recevoir les données correctement à l'aide de sockets. Pour exemple pour empêcher que certains messages soient fusionnés en un seul, car TCP est toujours un protocole basé sur un flux. Cela signifie que vous devez lire uniquement une quantité spécifique d'octets. La longueur du message est toujours basée sur l'en-tête et les détails de longueur de données fournis dans l'en-tête lui-même. Ainsi, lorsque vous recevez des données de Socket, recevez d'abord 2 octets, obtenez les détails de l'en-tête en fonction des spécifications de cadrage, puis si le masque fournit 4 octets supplémentaires, puis la longueur qui peut être de 1, 4 ou 8 octets en fonction de la longueur des données. Et après de données elle-même. Après l'avoir lu, appliquez le démasquage et vos données de message sont prêtes à l'emploi.
  7. Vous pouvez utiliser un protocole de données , je recommande d'utiliser JSON due traffic economy et facile à utiliser côté client en JavaScript. Pour le côté serveur, vous pouvez vérifier certains analyseurs. Il y en a beaucoup, google peut être vraiment utile.

Implémenter son propre protocole WebSockets a certainement des avantages et une grande expérience que vous obtenez ainsi que le contrôle sur le protocole lui-même. Mais vous devez passer du temps à le faire, et assurez-vous que la mise en œuvre est très fiable.

En même temps, vous pourriez jeter un oeil dans des solutions prêtes à l'emploi que google (encore) a assez.

57
répondu moka 2012-04-19 09:30:30

(Réponse affichée au nom du PO) .

Je suis en mesure d'envoyer des données maintenant. Ceci est ma nouvelle version du programme grâce à vos réponses et le code de @ Maksims Mihejevs.

Serveur

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>

Lorsque j'exécute ce code, je suis capable d'envoyer et de recevoir des données à la fois du client et du serveur. Le seul problème est que les messages sont cryptés lorsqu'ils arrivent sur le serveur. Voici les étapes de la façon dont le programme fonctionne:

entrez la description de l'image ici

Notez comment le message du client est chiffré.

6
répondu halfer 2017-07-27 10:39:54

Les WebSockets sont implémentés avec un protocole qui implique la liaison entre le client et le serveur. Je n'imagine pas qu'ils fonctionnent très bien comme des sockets normaux. Lisez sur le protocole, et obtenez votre application pour le parler. Vous pouvez également utiliser une bibliothèque WebSocket existante, ou. Net4.5beta qui a une API WebSocket.

4
répondu spender 2012-04-18 00:15:19

Question

Puisque vous utilisez WebSocket, spender est correct. Après avoir reçu les données initiales du WebSocket, vous devez envoyer le message de prise de contact à partir du serveur C# avant que d'autres informations puissent circuler.

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

Quelque chose dans ce sens.

Vous pouvez faire d'autres recherches sur le fonctionnement de WebSocket sur W3 ou google.

Liens et Ressources

Voici une spécification de protocole: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

Liste des exemples de travail:

2
répondu caesay 2017-05-23 12:02:17