From 68fc8e96a9629e73469ed457abd955e548ec670c Mon Sep 17 00:00:00 2001 From: Florian Merz Date: Thu, 11 Feb 2021 14:49:58 +0100 Subject: unslug pt-br: move --- .../writing_websocket_server/index.html | 237 +++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 files/pt-br/web/api/websockets_api/writing_websocket_server/index.html (limited to 'files/pt-br/web/api/websockets_api/writing_websocket_server/index.html') diff --git a/files/pt-br/web/api/websockets_api/writing_websocket_server/index.html b/files/pt-br/web/api/websockets_api/writing_websocket_server/index.html new file mode 100644 index 0000000000..553ba11aec --- /dev/null +++ b/files/pt-br/web/api/websockets_api/writing_websocket_server/index.html @@ -0,0 +1,237 @@ +--- +title: 'Escrevendo um servidor WebSocket em C #' +slug: WebSockets/Writing_WebSocket_server +translation_of: Web/API/WebSockets_API/Writing_WebSocket_server +--- +

Introdução

+ +

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. 

+ +

Primeiros passos

+ +

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.

+
+ +

TcpListener

+ +

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.");
+    }
+}
+
+ +

TcpClient

+ +

Métodos:

+ + + +

Propriedades:

+ + + +

NetworkStream

+ +

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

Handshaking

+ +

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,

+ +
    +
  1. Obter o valor do cabeçalho da requisição Sec-WebSocket-Key sem qualquer espaço à direita e à esquerda;
  2. +
  3. Concatenar com "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  4. +
  5. Calcular o código SHA-1 e Base64 dele;
  6. +
  7. Reescreva no cabeçalho de resposta o valor de Sec-WebSocket-Accept como parte de uma resposta HTTP.
  8. +
+ +
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);
+}
+
+ +

Decodificação de mensagens

+ +

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:

+ + + + + + + + + + + + + + + +
129131618435611216109
+ +

- 129:

+ + + + + + + + + + + + + + + + + + + + +
FIN (Esta é toda a mensagem?)RSV1RSV2RSV3Opcode
10000x1=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.

+ +

Algoritmo de decodificação

+ +

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]);
+}
+ + + + + +
 
-- cgit v1.2.3-54-g00ecf