No entiendo por qué no puedo hacer que el siguiente código funcione. Quiero conectarme con JavaScript a la aplicación de mi consola de servidor. Y luego envía 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 JavaScript, el servidor acepta y establece correctamente una conexión. Sin embargo, JavaScript no puede enviar datos. Siempre que coloco el método de envío, no enviará aunque se establezca una conexión. ¿Cómo puedo hacer que esto funcione?
c#
javascript
websocket
Tono Nam
fuente
fuente
Respuestas:
WebSockets es un protocolo que se basa en una conexión por streaming TCP. Aunque WebSockets es un protocolo basado en mensajes.
Si desea implementar su propio protocolo, le recomiendo utilizar la especificación más reciente y estable (para el 18/04/12) RFC 6455 . Esta especificación contiene toda la información necesaria sobre el protocolo de enlace y el encuadre. Además de la mayor parte de la descripción sobre escenarios de comportamiento desde el lado del navegador y desde el lado del servidor. Es muy recomendable seguir las recomendaciones sobre el lado del servidor durante la implementación de su código.
En pocas palabras, describiría trabajar con WebSockets así:
Cree un socket de servidor (System.Net.Sockets), vincúlelo a un puerto específico y siga escuchando con la aceptación asíncrona de conexiones. Algo como eso:
Debería tener la función de aceptación "OnAccept" que implementará el protocolo de enlace. En el futuro, tendrá que estar en otro hilo si el sistema está destinado a manejar una gran cantidad de conexiones por segundo.
Una vez establecida la conexión, debe realizar un apretón de manos . Según 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:
Este ejemplo se basa en la versión del protocolo 13. Tenga en cuenta que las versiones anteriores tienen algunas diferencias, pero la mayoría de las versiones más recientes son compatibles entre sí. Es posible que diferentes navegadores le envíen algunos datos adicionales. Por ejemplo, detalles del navegador y del sistema operativo, caché y otros.
Según los detalles del protocolo de enlace proporcionados, debe generar líneas de respuesta, en su mayoría son iguales, pero contendrán Accpet-Key, que se basa en Sec-WebSocket-Key proporcionado. 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:
La respuesta del apretón de manos se ve así:
Pero la clave de aceptación tiene que ser la generada según la clave proporcionada por el cliente y el método AcceptKey que proporcioné antes. Además, asegúrese de que después del último carácter de la tecla de aceptación ponga dos líneas nuevas "\ r \ n \ r \ n".
La implementación del propio protocolo WebSockets definitivamente tiene algunos beneficios y una gran experiencia que obtiene, así como el control sobre el protocolo en sí. Pero debe dedicar algo de tiempo a hacerlo y asegurarse de que la implementación sea altamente confiable.
Al mismo tiempo, es posible que eche un vistazo a las soluciones listas para usar que Google (nuevamente) tiene suficientes.
fuente
(Respuesta publicada en nombre del OP) .
Ahora puedo enviar datos. Esta es mi nueva versión del programa gracias a tus 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 están encriptados cuando llegan al servidor. Estos son los pasos de cómo se ejecuta el programa:
Tenga en cuenta cómo se cifra el mensaje del cliente.
fuente
Los WebSockets se implementan con un protocolo que implica un protocolo de enlace entre el cliente y el servidor . No creo que funcionen de forma muy parecida a los enchufes normales. Lea sobre el protocolo y obtenga su aplicación para hablarlo. Alternativamente, use una biblioteca WebSocket existente o .Net4.5beta que tenga una API WebSocket .
fuente
No pude encontrar un ejemplo de trabajo simple en ninguna parte (a partir del 19 de enero), así que aquí hay una versión actualizada. Tengo la versión de Chrome 71.0.3578.98.
Servidor C # Websocket:
using System; using System.Text; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; namespace WebSocketServer { 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(1); //just one socket 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); Console.WriteLine("====================="); } 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 ; client.Send(System.Text.Encoding.UTF8.GetBytes(response)); var i = client.Receive(buffer); // wait for client to send a message string browserSent = GetDecodedData(buffer, i); Console.WriteLine("BrowserSent: " + browserSent); Console.WriteLine("====================="); //now send message to client client.Send(GetFrameFromString("This is message from server to client.")); System.Threading.Thread.Sleep(10000);//wait for message to be sent } } 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)); } //Needed to decode frame public static string GetDecodedData(byte[] buffer, int length) { byte b = buffer[1]; int dataLength = 0; int totalLength = 0; int keyIndex = 0; if (b - 128 <= 125) { dataLength = b - 128; keyIndex = 2; totalLength = dataLength + 6; } if (b - 128 == 126) { dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0); keyIndex = 4; totalLength = dataLength + 8; } if (b - 128 == 127) { dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0); keyIndex = 10; totalLength = dataLength + 14; } if (totalLength > length) throw new Exception("The buffer length is small than the data length"); byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] }; int dataIndex = keyIndex + 4; int count = 0; for (int i = dataIndex; i < totalLength; i++) { buffer[i] = (byte)(buffer[i] ^ key[count % 4]); count++; } return Encoding.ASCII.GetString(buffer, dataIndex, dataLength); } //function to create frames to send to client /// <summary> /// Enum for opcode types /// </summary> public enum EOpcodeType { /* Denotes a continuation code */ Fragment = 0, /* Denotes a text code */ Text = 1, /* Denotes a binary code */ Binary = 2, /* Denotes a closed connection */ ClosedConnection = 8, /* Denotes a ping*/ Ping = 9, /* Denotes a pong */ Pong = 10 } /// <summary>Gets an encoded websocket frame to send to a client from a string</summary> /// <param name="Message">The message to encode into the frame</param> /// <param name="Opcode">The opcode of the frame</param> /// <returns>Byte array in form of a websocket frame</returns> public static byte[] GetFrameFromString(string Message, EOpcodeType Opcode = EOpcodeType.Text) { byte[] response; byte[] bytesRaw = Encoding.Default.GetBytes(Message); byte[] frame = new byte[10]; int indexStartRawData = -1; int length = bytesRaw.Length; frame[0] = (byte)(128 + (int)Opcode); if (length <= 125) { frame[1] = (byte)length; indexStartRawData = 2; } else if (length >= 126 && length <= 65535) { frame[1] = (byte)126; frame[2] = (byte)((length >> 8) & 255); frame[3] = (byte)(length & 255); indexStartRawData = 4; } else { frame[1] = (byte)127; frame[2] = (byte)((length >> 56) & 255); frame[3] = (byte)((length >> 48) & 255); frame[4] = (byte)((length >> 40) & 255); frame[5] = (byte)((length >> 32) & 255); frame[6] = (byte)((length >> 24) & 255); frame[7] = (byte)((length >> 16) & 255); frame[8] = (byte)((length >> 8) & 255); frame[9] = (byte)(length & 255); indexStartRawData = 10; } response = new byte[indexStartRawData + length]; int i, reponseIdx = 0; //Add the frame bytes to the reponse for (i = 0; i < indexStartRawData; i++) { response[reponseIdx] = frame[i]; reponseIdx++; } //Add the data bytes to the response for (i = 0; i < length; i++) { response[reponseIdx] = bytesRaw[i]; reponseIdx++; } return response; } } }
Cliente html y 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"> var socket = new WebSocket('ws://localhost:8080/websession'); socket.onopen = function() { // alert('handshake successfully established. May send data now...'); socket.send("Hi there from browser."); }; socket.onmessage = function (evt) { //alert("About to receive data"); var received_msg = evt.data; alert("Message received = "+received_msg); }; socket.onclose = function() { alert('connection closed'); }; </script> </head> <body> </body> </html>
fuente
Problema
Dado que está utilizando WebSocket, el gastador es correcto. Después de recibir los datos iniciales de WebSocket, debe enviar el mensaje de reconocimiento desde el servidor C # antes de que pueda fluir 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 ese sentido.
Puede investigar más sobre cómo funciona WebSocket en w3 o google.
Enlaces y recursos
Aquí hay una especifcación de protocolo: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3
Lista de ejemplos de trabajo:
fuente