Je ne comprends pas pourquoi je ne peux pas faire fonctionner le code suivant. Je souhaite me connecter avec JavaScript à mon application de console serveur. Et puis envoyez des données au serveur.
Voici le code serveur:
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));
}
}
et voici le 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...");
};
Lorsque j'exécute ce code, voici ce qui se passe:
Notez que lorsque j'exécute le JavaScript, le serveur accepte et établit avec succès une connexion. JavaScript ne peut cependant pas envoyer de données. Chaque fois que je place la méthode d'envoi, elle n'enverra pas même si une connexion est établie. Comment puis-je faire fonctionner cela?
c#
javascript
websocket
Tono Nam
la source
la source
Réponses:
WebSockets est un protocole qui repose sur une connexion TCP en continu. Bien que WebSockets soit un protocole basé sur les messages.
Si vous souhaitez implémenter votre propre protocole, je vous recommande d'utiliser la dernière spécification stable (pour le 18/04/12) RFC 6455 . Cette spécification contient toutes les informations nécessaires concernant la prise de contact et le cadrage. Ainsi que la plupart des descriptions de scénarios de comportement du côté du navigateur ainsi que du côté du serveur. Il est fortement recommandé de suivre les recommandations concernant le côté serveur lors de la mise en œuvre de votre code.
En quelques mots, je décrirais travailler avec WebSockets comme ceci:
Créez un serveur Socket (System.Net.Sockets) pour le lier à un port spécifique et continuez à écouter avec l'acceptation asynchrone des connexions. Quelque chose comme ca:
Vous devriez avoir la fonction d' acceptation "OnAccept" qui implémentera l'établissement de liaison. À l'avenir, il doit être dans un autre thread si le système est censé gérer une énorme quantité de connexions par seconde.
Une fois la connexion établie, vous devez établir une poignée de main . Basé sur la spécification 1.3 Ouverture de la poignée de main , une fois la connexion établie, vous recevrez une requête HTTP de base avec quelques informations. Exemple:
Cet exemple est basé sur la version du protocole 13. Gardez à l'esprit que les anciennes versions présentent quelques différences, mais que la plupart des dernières versions sont compatibles entre elles. Différents navigateurs peuvent vous envoyer des données supplémentaires. Par exemple, les détails du navigateur et du système d'exploitation, le cache et autres.
Sur la base des détails de prise de contact fournis, vous devez générer des lignes de réponse, elles sont pour la plupart identiques, mais contiendront Accpet-Key, qui est basé sur Sec-WebSocket-Key fourni. Dans la spécification 1.3, il est clairement décrit comment générer une clé de réponse. Voici ma fonction que j'utilise pour V13:
La réponse de la poignée de main ressemble à ça:
Mais la clé d'acceptation doit être celle générée en fonction de la clé fournie par le client et de la méthode AcceptKey que j'ai fournie auparavant. De même, assurez-vous qu'après le dernier caractère de la clé d'acceptation, vous mettez deux nouvelles lignes "\ r \ n \ r \ n".
La mise en œuvre de son propre protocole WebSockets présente certainement des avantages et une grande expérience, ainsi que le contrôle du protocole lui-même. Mais vous devez passer du temps à le faire et vous assurer que la mise en œuvre est extrêmement fiable.
Dans le même temps, vous pourriez jeter un œil aux solutions prêtes à l'emploi que google a (encore) assez.
la source
(Réponse publiée au nom du PO) .
Je suis en mesure d'envoyer des données maintenant. C'est ma nouvelle version du programme grâce à vos réponses et au code de @Maksims Mihejevs.
Serveur
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>
Lorsque j'exécute ce code, je suis capable d'envoyer et de recevoir des données à la fois du client et du serveur. Le seul problème est que les messages sont chiffrés lorsqu'ils arrivent sur le serveur. Voici les étapes de fonctionnement du programme:
Notez comment le message du client est chiffré.
la source
Les WebSockets sont implémentés avec un protocole qui implique une négociation entre le client et le serveur . Je n'imagine pas qu'ils fonctionnent beaucoup comme des prises normales. Renseignez-vous sur le protocole et demandez à votre application d'en parler. Vous pouvez également utiliser une bibliothèque WebSocket existante ou .Net4.5beta qui possède une API WebSocket .
la source
Je n'ai pas trouvé d'exemple de travail simple nulle part (au 19 janvier), voici donc une version mise à jour. J'ai la version chrome 71.0.3578.98.
Serveur 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; } } }
Client html et 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>
la source
Problème
Puisque vous utilisez WebSocket, Spender est correct. Après avoir reçu les données initiales du WebSocket, vous devez envoyer le message de prise de contact du serveur C # avant que d'autres informations puissent circuler.
HTTP/1.1 101 Web Socket Protocol Handshake Upgrade: websocket Connection: Upgrade WebSocket-Origin: example WebSocket-Location: something.here WebSocket-Protocol: 13
Quelque chose de ce genre.
Vous pouvez faire des recherches supplémentaires sur le fonctionnement de WebSocket sur w3 ou google.
Liens et ressources
Voici une spécifcation de protocole: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3
Liste d'exemples de travail:
la source