Escribiendo un servidor WebSocket en C#
=======================================

Introducción
------------

Si deseas utilizar la API WebSocket, es conveniente si tienes un servidor. En este artículo te mostraré como puedes escribir uno en C#. Tú puedes hacer esto en cualquier lenguaje del lado del servidor, pero para mantener las cosas simples y más comprensibles, elegí el lenguaje de Microsoft.

Este servidor se ajusta a RFC 6455 por lo que solo manejará las conexiones de Chrome version 16, Firefox 11, IE 10 and superiores.

Primeros pasos
--------------

WebSocket se comunica a través de conexiones TCP (Transmission Control Protocol), afortunadamente C# tiene una clase TcpListener la cual hace lo que su nombre sugiere. Esta se encuentra en el namespace *System.Net.Sockets*.

**Nota:** Es una buena idea usar la instrucción `using` para escribir menos. Eso significa que no tendrás que re escribir el namespace de nuevo en cada ocasión.

### TcpListener

Constructor:


```cpp
TcpListener(System.Net.IPAddress localaddr, int port)
```


`localaddr` especifica la IP a escuchar y `port` especifica el puerto.

**Nota:** Para crear un objeto `IPAddress` desde un `string`, usa el método estático `Parse` de `IPAddres.`

Métodos:

* `Start()`
* `System.Net.Sockets.TcpClient AcceptTcpClient()`
 Espera por una conexión TCP, la acepta y la devuelve como un objeto TcpClient.

Aquí está como utilizar lo que hemos aprendido:


```cs
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("El server se ha iniciado en 127.0.0.1:80.{0}Esperando una conexión...", Environment.NewLine);

        TcpClient client = server.AcceptTcpClient();

        Console.WriteLine("Un cliente conectado.");
    }
}
```

### TcpClient

Métodos:

* `System.Net.Sockets.NetworkStream GetStream()`
 Obtiene el stream del canal de comunicación. Ambos lados del canal tienen capacidad de lectura y escritura.

Propiedades:

* `int Available`
 Este es el número de bytes de datos que han sido enviados. El valor es cero hasta que `NetworkStream.DataAvailable` es `true`.
### NetworkStream

Métodos:


```cpp
Write(Byte[] buffer, int offset, int size)
```


Escribe bytes desde el *buffer;* el *offset* y el *size* determinan la longitud del mensaje.
```

Read(Byte[] buffer, int offset, int size)
```


Lee bytes al *buffer;* el *offset* y el *size* determinan la longitud del mensaje.

Ampliemos nuestro ejemplo anterior.


```cpp
TcpClient client = server.AcceptTcpClient();

Console.WriteLine("Un cliente conectado.");

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
-----------

Cuando un cliente se conecta al servidor, envía una solicitud GET para actualizar la conexión al WebSocket desde una simple petición HTTP. Esto es conocido como *handshaking*.

Este código de ejemplo detecta el GET desde el cliente. Nota que esto bloqueará hasta los 3 primeros bytes del mensaje disponible. Soluciones alternativas deben ser investigadas para ambientes de producción.
```

using System.Text;
using System.Text.RegularExpressions;

while(client.Available < 3)
{
   // wait for enough bytes to be available
}

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 (Regex.IsMatch(data, "^GET")) {

} else {

}
```


Esta respuesta es fácil de construir, pero puede ser un poco díficil de entender. La explicación completa del *handshake* al servidor puede encontrarse en RFC 6455, section 4.2.2. Para nuestros propósitos, solo construiremos una respuesta simple.

Debes:

1. Obtener el valor de "*Sec-WebSocket-Key"* sin espacios iniciales ni finales de el encabezado de la solicitud
2. Concatenarlo con "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
3. Calcular el código SHA-1 y Base64
4. Escribe el valor *Sec-WebSocket-Accept* en el encabezado como parte de la respuesta HTTP.


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

Decoding messages
-----------------

Luego de un *handshake* exitoso el cliente puede enviar mensajes al servidor, pero estos serán codificados.

Si nosotros enviamos "MDN", obtendremos estos bytes:

| 129 | 131 | 61 | 84 | 35 | 6 | 112 | 16 | 109 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |

- 129:

| FIN (¿Es el mensaje completo?) | RSV1 | RSV2 | RSV3 | Opcode |
| --- | --- | --- | --- | --- |
| 1 | 0 | 0 | 0 | 0x1=0001 |

 FIN: Puedes enviar tu mensaje en marcos, pero ahora debe mantener las cosas simples.
 Opcode *0x1* significa que es un texto. Lista completa de Opcodes

- 131:

Si el segundo byte menos 128 se encuentra entre 0 y 125, esta es la longitud del mensaje. Si es 126, los siguientes 2 bytes (entero sin signo de 16 bits), si es 127, los siguientes 8 bytes (entero sin signo de 64 bits) son la longitud.

**Nota:** Puedo tomar 128, porque el primer bit siempre es 1.

- 61, 84, 35 y 6 son los bytes de la clave a decodificar. Cambian en cada oportunidad.

- Los bytes codificados restantes son el mensaje.

### Algoritmo de decodificación

byte decodificado = byte codificado XOR (posición del byte codificado Mod 4) byte de la clave

Ejemplo en C#:


```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 < encoded.Length; i++) {
    decoded[i] = (Byte)(encoded[i] ^ key[i % 4]);
}
```

Relacionado
-----------

* Escribiendo servidores WebSocket (en-US)