Criar um exemplo de WebSocket "Hello World"

Não entendo porque não consigo fazer o seguinte código funcionar. Quero ligar-me com o JavaScript à minha aplicação na consola do servidor. E depois envia dados para o servidor.

aqui está o código do servidor:

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

e aqui está o 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...");
        };
Quando eu executar o código, isto é o que acontece.

Note que quando eu executar o JavaScript o servidor aceita e estabelece com sucesso uma conexão. JavaScript não é capaz de enviar dados embora. Sempre que eu colocar o método de envio ele não vai enviar mesmo que uma conexão é estabelecida. Como posso fazer isto funcionar?

Author: Umair M, 2012-04-18

4 answers

WebSockets é um protocolo que depende da ligação TCP streamed. Embora WebSockets seja um protocolo baseado em mensagens.

Se você quer implementar o seu próprio protocolo, então eu recomendo usar a especificação mais recente e estável (para 18/04/12) RFC 6455 . Esta especificação contém todas as informações necessárias sobre aperto de mão e enquadramento. Bem como a maior parte da descrição em cenários de comportamento do lado do navegador, bem como do lado do servidor. Recomenda - se recomendações diz sobre o lado do servidor durante a implementação do seu código.

Em poucas palavras, descreveria o trabalho com WebSockets como este:
  1. Criar o 'Socket' do servidor (System.Net.Sockets) liga-o a um porto específico e continua a ouvir com a aceitação assíncrona das ligações. Algo do género:

    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. {[[6]} você deve teraceitando função "Onacept" que irá implementar aperto de mão. No futuro, terá de ser noutra. thread if system is meant to handle huge amount of connections per second.
    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. Depois da ligação estabelecida, tens de fazero aperto de mão . Com base na especificação 1.3 Abertura de aperto de mão, após a ligação estabelecida, irá receber um pedido HTTP básico com alguma informação. Exemplo:

    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

    Este exemplo baseia-se na versão do protocolo 13. Tenha em mente que as versões mais antigas têm algumas diferenças, mas na maioria das versões mais recentes são compatível. Diferentes navegadores podem enviar-lhe alguns dados adicionais. Por exemplo, Detalhes do navegador e do so, cache e outros.

    Com base nos detalhes fornecidos do aperto de mão, você tem que gerar linhas de resposta, elas são na sua maioria iguais, mas conterão Accpet-Key, que é baseado na chave Sec-WebSocket fornecida. Na Especificação 1.3 descreve-se claramente como gerar a chave de resposta. Aqui está a minha função que tenho usado para o 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);
    }
    
    A resposta do aperto de mão parece assim:
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

    Mas aceitar chave tem que ser a gerada com base na chave fornecida do cliente e método AcceptKey I fornecido antes. Além disso, certifique-se que, após o último carácter da tecla accept, colocou duas novas linhas "\r\n\r\n".

  4. Depois da resposta do aperto de mão ser enviada do servidor, o cliente deverá activar a função "onopen", o que significa que poderá enviar mensagens depois.
  5. As mensagens não são enviadas em formato bruto, mas têm imagens de Dados . E de cliente a servidor também implementar mascaramento para dados baseados em 4 bytes fornecidos no cabeçalho da mensagem. Embora de servidor para cliente você não precisa aplicar mascaramento sobre os dados. Leia a secção 5. Enquadramento dos dados Em especificação. Aqui está copy-paste da minha própria implementação. Ele não está pronto para usar código, e tem que ser modificado, estou postando-o apenas para dar uma ideia e lógica geral de leitura/escrita com framing WebSocket. Go to this link .
  6. após a implementação do enquadramento, certifique-se de que recebe os dados correctamente usando tomadas. Por exemplo, para evitar que algumas mensagens sejam fundidas em uma, porque o TCP ainda é um protocolo baseado em stream. Isso significa que você tem que ler apenas uma quantidade específica de bytes. O comprimento da mensagem é sempre baseado no cabeçalho e forneceu detalhes de comprimento de dados no cabeçalho que ele próprio. Então, quando você receber dados do Socket, primeiro receber 2 bytes, obter detalhes do cabeçalho com base na especificação de enquadramento, em seguida, se Máscara forneceu outros 4 bytes, e, em seguida, o comprimento que pode ser 1, 4 ou 8 bytes com base no comprimento de dados. E depois do data, ele mesmo. Depois de lê-lo, aplicar demasking e seus dados de mensagem está pronto para usar.
  7. você pode querer usar alguns Data Protocol , eu recomendo usar JSON devido economia de tráfego e fácil de usar no lado do cliente em JavaScript. Para o lado do servidor você pode querer verificar alguns dos parsers. Há muitos deles, o google pode ser realmente útil.

Implementar o próprio protocolo WebSockets definitivamente tem alguns benefícios e grande experiência que você tem, bem como controle sobre o protocolo. Mas temos de passar algum tempo a fazê-lo e garantir que a implementação é altamente fiável.

Ao mesmo tempo você pode ter um olhar em Pronto para usar soluções que o google (novamente) tem o suficiente.

 57
Author: moka, 2012-04-19 09:30:30

(resposta postada em nome do OP) .

Já posso enviar dados. Esta é a minha nova versão do programa graças às suas respostas e ao código de @Maksims Mihejevs.

Servidor

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>

Quando eu executar esse código eu sou capaz de enviar e receber dados tanto do cliente e do servidor. O único problema é que as mensagens são criptografadas quando chegam ao servidor. Aqui estão os passos de como o programa execução:

enter image description here

Repare como a mensagem do cliente está encriptada.

 6
Author: halfer, 2017-07-27 10:39:54

Os WebSocket são implementados com um protocolo que envolve aperto de mão entre o cliente e o servidor. Imagino que não funcionem muito como as órbitas normais. Lê o protocolo e pede à tua candidatura para falar sobre isso. Alternativamente, use uma biblioteca WebSocket existente, ou. Net4. 5beta que tem uma WebSocket API .

 4
Author: spender, 2012-04-18 00:15:19

Emissão

Uma vez que está a usar o WebSocket, o spender está correcto. Depois de receber os dados iniciais do WebSocket, você precisa enviar a mensagem de aperto de mão do servidor C# antes que qualquer informação adicional possa fluir.

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

Pode fazer mais alguma pesquisa sobre como o WebSocket funciona no W3 ou no google.

Ligações e recursos

Aqui está uma especificação do protocolo.: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

Lista de exemplos de trabalho:

 2
Author: caesay, 2017-05-23 12:02:17