websockets servidor mvc example clientwebsocket c# javascript websocket

c# - servidor - websocket mvc 5



Creando un ejemplo de WebSocket "Hello World" (4)

Problema

Como está usando WebSocket, spender es correcto. Después de recibir los datos iniciales de WebSocket, debe enviar el mensaje de saludo desde el servidor C # antes de que fluya más información.

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

Algo en esa línea.

Puede investigar un poco más sobre cómo funciona WebSocket en w3 o google.

Enlaces y recursos

Aquí hay una especificación de protocolo: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

Lista de ejemplos de trabajo:

No entiendo por qué no puedo hacer que el siguiente código funcione. Quiero conectarme con JavaScript a la aplicación de la consola de mi servidor. Y luego envíe datos al servidor.

Aquí está el código del 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)); } }

y aquí está el 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..."); };

Cuando ejecuto ese código esto es lo que sucede:

Tenga en cuenta que cuando ejecuto el JavaScript, el servidor acepta y establece una conexión con éxito. JavaScript no puede enviar datos. Cada vez que coloco el método de envío, no se enviará aunque se haya establecido una conexión. ¿Cómo puedo hacer que esto funcione?



WebSockets es un protocolo que depende de la conexión transmitida por TCP. Aunque WebSockets es un protocolo basado en mensajes.

Si desea implementar su propio protocolo, le recomiendo usar la especificación más reciente y estable (para 18/04/12) RFC 6455 . Esta especificación contiene toda la información necesaria sobre el apretón de manos y el encuadre. Además de la mayoría de la descripción sobre los escenarios de comportamiento desde el lado del navegador, así como desde el lado del servidor. Se recomienda encarecidamente seguir las recomendaciones sobre el lado del servidor durante la implementación de su código.

En pocas palabras, describiría trabajar con WebSockets como este:

  1. Crear socket del servidor (System.Net.Sockets) lo vincula al puerto específico y sigue escuchando con la aceptación asincrónica de las conexiones. Algo como eso:

    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. Debería haber aceptado la función "OnAccept" que implementará el protocolo de enlace. En el futuro tiene que estar en otro hilo si el sistema está destinado a manejar una gran cantidad de conexiones por segundo.

    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. Una vez establecida la conexión, debe realizar un apretón de manos . En función de la especificación 1.3 Apertura del protocolo de enlace, una vez establecida la conexión, recibirá una solicitud HTTP básica con cierta información. Ejemplo:

    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 ejemplo se basa en la versión del protocolo 13. Tenga en cuenta que las versiones anteriores tienen algunas diferencias, pero en su mayoría las últimas versiones son compatibles. Diferentes navegadores pueden enviarle algunos datos adicionales. Por ejemplo, detalles del navegador y sistema operativo, caché y otros.

    En función de los detalles de saludo proporcionados, debe generar líneas de respuesta, en su mayoría son las mismas, pero contendrá Accpet-Key, que se basa en la Sec-WebSocket-Key proporcionada. En la especificación 1.3 se describe claramente cómo generar la clave de respuesta. Aquí está mi función que he estado usando para 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); }

    La respuesta del apretón de manos se ve así:

    HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

    Pero la clave de aceptación debe ser la generada en función de la clave provista por el cliente y el método AcceptKey que proporcioné anteriormente. Además, asegúrese de que después del último carácter de la tecla Aceptar, inserte dos líneas nuevas "/ r / n / r / n".

  4. Después de enviar la respuesta de handshake desde el servidor, el cliente debe activar la función " onopen ", lo que significa que puede enviar mensajes después.
  5. Los mensajes no se envían en formato sin formato, pero tienen Data Framing . Y desde el cliente al servidor, así como implementar el enmascaramiento para los datos basados ​​en 4 bytes proporcionados en el encabezado del mensaje. Aunque de servidor a cliente no es necesario aplicar enmascaramiento sobre los datos. Lea la sección 5. Data Framing en la especificación. Aquí está copiar y pegar de mi propia implementación. No es un código listo para usar, y debe modificarse. Lo estoy publicando solo para dar una idea y una lógica general de lectura / escritura con WebSocket framing. Ve a este enlace .
  6. Después de que se implemente el encuadre, asegúrese de recibir datos de manera correcta utilizando sockets. Por ejemplo, para evitar que algunos mensajes se combinen en uno, porque TCP sigue siendo un protocolo basado en la transmisión. Eso significa que debe leer SOLO la cantidad específica de bytes. La longitud del mensaje siempre se basa en el encabezado y proporciona detalles de la longitud de los datos en el encabezado. Entonces, cuando recibe datos de Socket, primero reciba 2 bytes, obtenga detalles del encabezado según la especificación de Framing, luego, si la máscara proporcionó otros 4 bytes y luego la longitud que podría ser 1, 4 u 8 bytes según la longitud de los datos. Y después de los datos en sí mismo. Después de leerlo, aplique el desenmascarado y los datos de su mensaje estarán listos para su uso.
  7. Es posible que desee utilizar algún protocolo de datos , le recomiendo usar JSON debido a la economía del tráfico y fácil de usar en el lado del cliente en JavaScript. Por el lado del servidor es posible que desee verificar algunos de los analizadores. Hay muchos de ellos, google puede ser realmente útil.

Implementar su propio protocolo WebSockets definitivamente tiene algunos beneficios y una gran experiencia, así como también un control sobre el protocolo. Pero debe dedicar un tiempo a hacerlo y asegurarse de que la implementación sea altamente confiable.

Al mismo tiempo, puede echarle un vistazo a las soluciones listas para usar que google (una vez más) tiene suficiente.


(Respuesta publicada en nombre del OP) .

Puedo enviar datos ahora. Esta es mi nueva versión del programa gracias a sus respuestas y al 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>

Cuando ejecuto ese código, puedo enviar y recibir datos tanto del cliente como del servidor. El único problema es que los mensajes se cifran cuando llegan al servidor. Estos son los pasos de cómo se ejecuta el programa:

Observe cómo el mensaje del cliente está encriptado.