aboutsummaryrefslogtreecommitdiff
path: root/files/pt-br/web/api/websockets_api/writing_websocket_server/index.html
blob: 9592eeeb584d501032dc6627598d7f968fd055ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
---
title: 'Escrevendo um servidor WebSocket em C #'
slug: Web/API/WebSockets_API/Writing_WebSocket_server
translation_of: Web/API/WebSockets_API/Writing_WebSocket_server
original_slug: WebSockets/Writing_WebSocket_server
---
<h2 id="Introdução">Introdução</h2>

<p>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.</p>

<p>Este servidor está em conformidade com a <a href="http://tools.ietf.org/html/rfc6455" title="http://tools.ietf.org/html/rfc6455">RFC 6455</a>, por isso irá tratar apenas as conexões com os navegadores Chrome versão 16, Firefox 11, IE 10 ou superior. </p>

<h2 id="Primeiros_passos">Primeiros passos</h2>

<p>Os WebSocket´s se comunicam através de uma conexão TCP (Transmission Control Protocol), felizmente o C# possui a classe <a href="https://msdn.microsoft.com/pt-br/library/system.net.sockets.tcplistener.aspx">TcpListener</a> 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.</p>

<div class="note">
<p><span style="line-height: 1.572;">É 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.</span></p>
</div>

<h3 id="TcpListener">TcpListener</h3>

<p>Construtor:</p>

<pre class="brush: cpp">TcpListener(System.Net.IPAddress localaddr, int port)</pre>

<p>Aqui você define onde o servidor será acessível.</p>

<div class="note">
<p><span style="line-height: 1.572;">Para setar facilmente o tipo esperado no primeiro parâmetro, use o método estático Parse da classe IPAddress.</span></p>
</div>

<p><span style="line-height: 1.572;">Métodos</span><span style="line-height: 1.572;">:</span></p>

<ul>
 <li><span style="line-height: 1.572;">Start()</span></li>
 <li><span style="line-height: 1.572;">System.Net.Sockets.<a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.aspx" title="http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.aspx">TcpClient</a> AcceptTcpClient()<br>
  Espera por uma conexão TCP, aceita a conexão e retorna um objeto TcpClient.</span></li>
</ul>

<p><span style="line-height: 1.572;">Veja como usar o que aprendemos:</span></p>

<pre class="brush: cpp">​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.");
    }
}
</pre>

<h3 id="TcpClient"><span style="line-height: 1.572;">TcpClient</span></h3>

<p>Métodos:</p>

<ul>
 <li><code>System.Net.Sockets.<a href="http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.aspx" title="http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.aspx">NetworkStream</a> GetStream()</code><br>
  Obtém o fluxo (stream) do canal de comunicação. Ambos os lados do canal de comunicação possuem a capacidade de ler e escrever.</li>
</ul>

<p>Propriedades:</p>

<ul>
 <li><code>int Available</code><br>
  Este é o numero de bytes de dados que foram enviados. o valor é zero enquanto <em>NetworkStream.DataAvailable</em> for <em>falso</em>.</li>
</ul>

<h3 id="NetworkStream">NetworkStream</h3>

<p>Métodos:</p>

<pre class="brush: cpp">Write(Byte[] buffer, int offset, int size)</pre>

<p>Grava bytes do buffer, <em>offset </em>e <em>size </em>determinam o tamanho da mensagem.</p>

<pre><span class="brush: cpp" style="line-height: 1.572;">Read(Byte[] buffer, int offset, int size)</span></pre>

<p>Lê bytes para o <em>buffer, offset e size </em>determinam o tamanho da mensagem.<em> </em></p>

<p>Vamos estender nosso exemplo.</p>

<pre class="brush: cpp">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);
}</pre>

<h2 id="Handshaking">Handshaking</h2>

<p>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).</p>

<div class="warning">
<p>Este código tem um defeito. Digamos que a propriedade client.<code>Available</code> 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.</p>
</div>

<pre class="brush: cpp">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 {

}</pre>

<p>Criar a resposta é mais fácil do que entender porque você deve fazê-lo desta forma.</p>

<p>Você deve,</p>

<ol>
 <li>Obter o valor do cabeçalho da requisição <em>Sec-WebSocket-Key</em> sem qualquer espaço à direita e à esquerda;</li>
 <li>Concatenar com "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";</li>
 <li>Calcular o código SHA-1 e Base64 dele;</li>
 <li>Reescreva no cabeçalho de resposta o valor de <em>Sec-WebSocket-Accept</em> como parte de uma resposta HTTP.</li>
</ol>

<pre class="brush: cpp">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);
}
</pre>

<h2 id="Decodificação_de_mensagens">Decodificação de mensagens</h2>

<p>Após um <em>handshake</em> de sucesso o cliente ponde enviar mensagens ao servidor, mas agora estas mensagens são codificadas.</p>

<p>Se nós enviarmos "MDN", nós obtemos estes bytes:</p>

<table>
 <tbody>
  <tr>
   <td>129</td>
   <td>131</td>
   <td>61</td>
   <td>84</td>
   <td>35</td>
   <td>6</td>
   <td>112</td>
   <td>16</td>
   <td>109</td>
  </tr>
 </tbody>
</table>

<p>- 129:</p>

<table>
 <thead>
  <tr>
   <th scope="col">FIN (Esta é toda a mensagem?)</th>
   <th scope="col">RSV1</th>
   <th scope="col">RSV2</th>
   <th scope="col">RSV3</th>
   <th scope="col">Opcode</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>1</td>
   <td>0</td>
   <td>0</td>
   <td>0</td>
   <td>0x1=0001</td>
  </tr>
 </tbody>
</table>

<p>FIN: Você pode enviar sua mensagem em quadros (frames), mas agora as coisas ficaram mais simples.<br>
 <span style="line-height: 1.572;">Opcode </span><em>0x1</em><span style="line-height: 1.572;"> significa que este é um texto. Veja aqui a </span><a href="http://tools.ietf.org/html/rfc6455#section-5.2" style="line-height: 1.572;" title="http://tools.ietf.org/html/rfc6455#section-5.2">lista completa de Opcodes</a>.</p>

<p>- 131:</p>

<p>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.</p>

<div class="note">
<p>Eu posso escolher 128, porque o primeiro bit sempre será 1.</p>
</div>

<p>- 61, 84, 35 e 6 são os bytes de chave para decodificar. Sempre mudam.</p>

<p>- O restante dos bytes codificados são a mensagem<span style="line-height: 1.572;">.</span></p>

<h3 id="Algoritmo_de_decodificação">Algoritmo de decodificação</h3>

<p>byte decodificado = [byte codificado XOR (posição do byte codificado MOD 4º byte da chave)]</p>

<p>Exemplo em C#:</p>

<pre class="brush: cpp">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 &lt; encoded.Length; i++) {
    decoded[i] = (Byte)(encoded[i] ^ key[i % 4]);
}</pre>

<h2 id="Link_Relacionado">Link Relacionado</h2>

<ul>
 <li><a href="/en-US/docs/WebSockets/Writing_WebSocket_servers">Writing WebSocket servers</a></li>
</ul>