Creazione di un esempio WebSocket “Hello World”

Non capisco perché non riesco a far funzionare il seguente codice. Voglio connettermi con JavaScript alla mia applicazione console server. E quindi inviare dati al server.

Ecco il codice del server:

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

e qui è il 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..."); }; 

Quando eseguo quel codice, questo è ciò che accade:

Si noti che quando eseguo JavaScript il server accetta e stabilisce con successo una connessione. JavaScript non è in grado di inviare dati però. Ogni volta che inserisco il metodo di invio non verrà inviato anche se viene stabilita una connessione. Come posso fare questo lavoro?

WebSockets è un protocollo che si basa sulla connessione in streaming TCP. Sebbene WebSockets sia un protocollo basato su messaggi.

Se si desidera implementare il proprio protocollo, si consiglia di utilizzare le specifiche più recenti e stabili (per il 18/04/12) RFC 6455 . Questa specifica contiene tutte le informazioni necessarie riguardanti l’handshake e l’inquadratura. Così come la maggior parte della descrizione su scenari di comportamento dal lato del browser e dal lato del server. Si consiglia vivamente di seguire le raccomandazioni fornite per quanto riguarda il lato server durante l’implementazione del codice.

In poche parole, descriverei di lavorare con WebSocket in questo modo:

  1. Creare il socket del server (System.Net.Sockets) collegarlo a una porta specifica e continuare ad ascoltare con l’accettazione asincrona delle connessioni. Qualcosa del genere:

      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. Dovresti accettare la funzione “OnAccept” che implementerà l’handshake. In futuro deve essere in un altro thread se il sistema è pensato per gestire enormi quantità di connessioni al secondo.

      annullamento privato OnAccept (risultato IAsyncResult) {
     provare {
         Socket client = null;
         if (serverSocket! = null && serverSocket.IsBound) {
             client = serverSocket.EndAccept (risultato);
         }
         if (client! = null) {
             / * Handshaking e gestione ClientSocket * /
         }
     } catch (eccezione SocketException) {
    
     } finalmente {
         if (serverSocket! = null && serverSocket.IsBound) {
             serverSocket.BeginAccept (null, 0, OnAccept, null);
         }
     }
     } 
  3. Dopo aver stabilito la connessione, devi fare l’ handshake . Basato sulla specifica 1.3 Opening Handshake , dopo aver stabilito la connessione, riceverai una richiesta HTTP di base con alcune informazioni. Esempio:

      OTTIENI / chat HTTP / 1.1
     Host: server.example.com
     Aggiornamento: websocket
     Connessione: aggiornamento
     Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ ==
     Origine: http://example.com
     Sec-WebSocket-Protocol: chat, superchat
     Sec-WebSocket-Version: 13 

    Questo esempio si basa sulla versione del protocollo 13. Ricorda che le versioni precedenti presentano alcune differenze, ma per la maggior parte le versioni più recenti sono cross-compatibili. Browser diversi potrebbero inviarti alcuni dati aggiuntivi. Ad esempio i dettagli del browser e del sistema operativo, la cache e altri.

    Sulla base dei dettagli forniti dall’handshake, è necessario generare le linee di risposta, sono per lo più identiche, ma conterrà Accpet-Key, che si basa sulla Sec-WebSocket-Key fornita. Nella specifica 1.3 è descritto chiaramente come generare la chiave di risposta. Ecco la mia funzione che sto usando per V13:

      static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
     stringa privata AcceptKey (chiave stringa ref) {
         stringa longKey = key + guid;
         SHA1 sha1 = SHA1CryptoServiceProvider.Create ();
         byte [] hashBytes = sha1.ComputeHash (System.Text.Encoding.ASCII.GetBytes (longKey));
         return Convert.ToBase64String (hashBytes);
     }
    

    La risposta della stretta di mano sembra così:

      Protocolli di commutazione HTTP / 1.1 101
     Aggiornamento: websocket
     Connessione: aggiornamento
     Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK + xOo = 

    Ma accettare la chiave deve essere quella generata in base alla chiave fornita dal client e dal metodo AcceptKey che ho fornito in precedenza. Inoltre, assicurati che dopo l’ultimo carattere del tasto di accettazione inserisci due nuove righe “\ r \ n \ r \ n”.

  4. Dopo che la risposta di handshake è stata inviata dal server, il client deve triggersre la funzione ” onopen “, il che significa che è ansible inviare messaggi dopo.
  5. I messaggi non vengono inviati in formato raw, ma hanno frame dati . E dal client al server implementa anche il masking per i dati in base ai 4 byte forniti nell’intestazione del messaggio. Sebbene dal server al client non è necessario applicare il mascheramento sui dati. Leggi la sezione 5. Data Framing nella specifica. Ecco copia-incolla dalla mia stessa implementazione. Non è un codice pronto per l’uso e deve essere modificato, lo sto pubblicando solo per dare un’idea e una logica generale di lettura / scrittura con il frame di WebSocket. Vai a questo link .
  6. Dopo aver implementato il framing, assicurati di ricevere i dati correttamente utilizzando le prese. Ad esempio, per impedire che alcuni messaggi vengano uniti in uno solo, poiché il protocollo TCP è ancora basato sul stream. Ciò significa che devi leggere SOLO una quantità specifica di byte. La lunghezza del messaggio si basa sempre sull’intestazione e fornisce i dettagli sulla lunghezza dei dati nell’intestazione stessa. Quindi, quando si ricevono dati da Socket, prima ricevono 2 byte, ottengono dettagli dall’intestazione in base alla specifica Framing, quindi se la maschera fornisce altri 4 byte e quindi la lunghezza che può essere 1, 4 o 8 byte in base alla lunghezza dei dati. E dopo i dati si auto. Dopo averlo letto, applica demasking e i dati del messaggio sono pronti per l’uso.
  7. Si potrebbe desiderare di utilizzare alcuni protocolli di dati , mi raccomando di usare JSON dovuto economia del traffico e facile da usare sul lato client in JavaScript. Per il lato server potresti voler controllare alcuni parser. Ce ne sono molti, Google può essere davvero d’aiuto.

L’implementazione del proprio protocollo WebSockets offre sicuramente alcuni vantaggi e una grande esperienza, oltre al controllo sul protocollo stesso. Ma devi passare un po ‘di tempo a farlo e assicurarti che l’implementazione sia altamente affidabile.

Nello stesso tempo potresti avere un aspetto in soluzioni pronte all’uso che google (di nuovo) hanno abbastanza.

(Risposta inviata per conto del PO) .

Sono in grado di inviare dati ora. Questa è la mia nuova versione del programma grazie alle tue risposte e al codice di @Maksims Mihejevs.

server

 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(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[] 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:

          

Quando eseguo quel codice, sono in grado di inviare e ricevere dati sia dal client che dal server. L’unico problema è che i messaggi vengono crittografati quando arrivano al server. Ecco i passaggi di esecuzione del programma:

inserisci la descrizione dell'immagine qui

Nota come viene crittografato il messaggio dal client.

I WebSocket sono implementati con un protocollo che prevede l’ handshake tra client e server . Non immagino che funzionino molto come le normali prese. Leggi il protocollo e chiedi alla tua applicazione di parlarne. In alternativa, utilizzare una libreria WebSocket esistente o .Net4.5beta che dispone di un’API WebSocket .

Problema

Dato che stai usando WebSocket, lo spender è corretto. Dopo aver ricevuto i dati iniziali dal WebSocket, è necessario inviare il messaggio di handshake dal server C # prima che possano fluire ulteriori informazioni.

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

Qualcosa in questo senso.

Puoi fare ulteriori ricerche su come funziona WebSocket su w3 o google.

Link e risorse

Ecco una specifica del protocollo: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3

Elenco di esempi di lavoro: