--- title: 'Escrevendo um servidor WebSocket em C #' slug: WebSockets/Writing_WebSocket_server translation_of: Web/API/WebSockets_API/Writing_WebSocket_server ---
Se você quiser usar uma API WebSocket, você precisara ter um servidor. Neste artigo vou mostrar como escrever um WebSocket em C#. Você pode fazer isso em qualquer linguagem server-side, mas para manter as coisas simples e mais compreensíveis eu escolhi uma linguagem Microsoft.
Este servidor está em conformidade com a RFC 6455, por isso irá tratar apenas as conexões com os navegadores Chrome versão 16, Firefox 11, IE 10 ou superior.
Os WebSocket´s se comunicam através de uma conexão TCP (Transmission Control Protocol), felizmente o C# possui a classe TcpListener que, como o nome sugere, tem a função de escutar (Listener) as comunicações via TCP. A classe TcpListener está no namespace System.Net.Sockets.
É uma boa idéia usar a palavra chave using para escrever menos. Isso significa que não é preciso você reescrever o namespace toda vez que usar uma classe dele.
Construtor:
TcpListener(System.Net.IPAddress localaddr, int port)
Aqui você define onde o servidor será acessível.
Para setar facilmente o tipo esperado no primeiro parâmetro, use o método estático Parse da classe IPAddress.
Métodos:
Veja como usar o que aprendemos:
using System.Net.Sockets; using System.Net; using System; class Server { public static void Main() { TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80); server.Start(); Console.WriteLine("Server has started on 127.0.0.1:80.{0}Waiting for a connection...", Environment.NewLine); TcpClient client = server.AcceptTcpClient(); Console.WriteLine("A client connected."); } }
Métodos:
System.Net.Sockets.NetworkStream GetStream()
Propriedades:
int Available
Métodos:
Write(Byte[] buffer, int offset, int size)
Grava bytes do buffer, offset e size determinam o tamanho da mensagem.
Read(Byte[] buffer, int offset, int size)
Lê bytes para o buffer, offset e size determinam o tamanho da mensagem.
Vamos estender nosso exemplo.
TcpClient client = server.AcceptTcpClient(); Console.WriteLine("A client connected."); NetworkStream stream = client.GetStream(); //enter to an infinite cycle to be able to handle every change in stream while (true) { while (!stream.DataAvailable); Byte[] bytes = new Byte[client.Available]; stream.Read(bytes, 0, bytes.Length); }
Quando um cliente se conecta a um servidor, ele envia uma solicitação GET para atualizar a conexão com o WebSocket a partir de uma simples requisição HTTP. Isto é conhecido como handshaking (aperto de mão).
Este código tem um defeito. Digamos que a propriedade client.Available
retorna o valor 2 porque somente a requisição GET está disponível até agora. a expressão regular iria falhar mesmo que os dados recebidos sejam perfeitamente válidos.
using System.Text; using System.Text.RegularExpressions; Byte[] bytes = new Byte[client.Available]; stream.Read(bytes, 0, bytes.Length); //translate bytes of request to string String data = Encoding.UTF8.GetString(bytes); if (new Regex("^GET").IsMatch(data)) { } else { }
Criar a resposta é mais fácil do que entender porque você deve fazê-lo desta forma.
Você deve,
if (new Regex("^GET").IsMatch(data)) { Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine + "Connection: Upgrade" + Environment.NewLine + "Upgrade: websocket" + Environment.NewLine + "Sec-WebSocket-Accept: " + Convert.ToBase64String ( SHA1.Create().ComputeHash ( Encoding.UTF8.GetBytes ( new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ) ) ) + Environment.NewLine + Environment.NewLine); stream.Write(response, 0, response.Length); }
Após um handshake de sucesso o cliente ponde enviar mensagens ao servidor, mas agora estas mensagens são codificadas.
Se nós enviarmos "MDN", nós obtemos estes bytes:
129 | 131 | 61 | 84 | 35 | 6 | 112 | 16 | 109 |
- 129:
FIN (Esta é toda a mensagem?) | RSV1 | RSV2 | RSV3 | Opcode |
---|---|---|---|---|
1 | 0 | 0 | 0 | 0x1=0001 |
FIN: Você pode enviar sua mensagem em quadros (frames), mas agora as coisas ficaram mais simples.
Opcode 0x1 significa que este é um texto. Veja aqui a lista completa de Opcodes.
- 131:
Se o segundo byte menos 128 estiver entre 0 e 125, este é o tamanho da mensagem. Se for 126, os 2 bytes seguintes (16-bit inteiro sem sinal) e se 127, os 8 bytes seguintes (64-bit inteiro sem sinal) são o comprimento.
Eu posso escolher 128, porque o primeiro bit sempre será 1.
- 61, 84, 35 e 6 são os bytes de chave para decodificar. Sempre mudam.
- O restante dos bytes codificados são a mensagem.
byte decodificado = [byte codificado XOR (posição do byte codificado MOD 4º byte da chave)]
Exemplo em C#:
Byte[] decoded = new Byte[3]; Byte[] encoded = new Byte[3] {112, 16, 109}; Byte[] key = Byte[4] {61, 84, 35, 6}; for (int i = 0; i < encoded.Length; i++) { decoded[i] = (Byte)(encoded[i] ^ key[i % 4]); }