Я не понимаю, почему я не могу заставить работать следующий код. Я хочу подключиться с помощью JavaScript к моему консольному приложению сервера. А затем отправьте данные на сервер.
Вот код сервера:
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));
}
}
а вот и 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...");
};
Когда я запускаю этот код, происходит следующее:
Обратите внимание, что когда я запускаю JavaScript, сервер принимает и успешно устанавливает соединение. Однако JavaScript не может отправлять данные. Всякий раз, когда я размещаю метод отправки, он не отправляется, даже если соединение установлено. Как я могу заставить это работать?
c#
javascript
websocket
Тоно Нам
источник
источник
Ответы:
WebSockets - это протокол, основанный на потоковом соединении TCP. Хотя WebSockets - это протокол на основе сообщений.
Если вы хотите реализовать свой собственный протокол, я рекомендую использовать последнюю и стабильную спецификацию (на 18.04.12) RFC 6455 . Эта спецификация содержит всю необходимую информацию о квитировании и кадрировании. А также большая часть описания сценариев поведения как со стороны браузера, так и со стороны сервера. При внедрении кода настоятельно рекомендуется следовать рекомендациям относительно серверной части.
В двух словах я бы описал работу с WebSockets так:
Создайте серверный Socket (System.Net.Sockets), привяжите его к определенному порту и продолжайте прослушивание с асинхронным приемом соединений. Что-то такое:
У вас должна быть принимающая функция «OnAccept», которая реализует рукопожатие. В будущем он должен быть в другом потоке, если система предназначена для обработки огромного количества соединений в секунду.
После установления соединения необходимо выполнить рукопожатие . В соответствии со спецификацией 1.3 «Открытие рукопожатия» после установления соединения вы получите базовый HTTP-запрос с некоторой информацией. Пример:
Этот пример основан на версии протокола 13. Имейте в виду, что старые версии имеют некоторые отличия, но в основном последние версии перекрестно совместимы. Различные браузеры могут отправлять вам дополнительные данные. Например, сведения о браузере и ОС, кеш и другие.
На основе предоставленных деталей рукопожатия вам необходимо сгенерировать строки ответа, они в основном одинаковы, но будут содержать Accpet-Key, который основан на предоставленном Sec-WebSocket-Key. В спецификации 1.3 четко описано, как сгенерировать ответный ключ. Вот моя функция, которую я использовал для V13:
Ответ рукопожатия выглядит так:
Но ключ принятия должен быть сгенерирован на основе предоставленного ключа от клиента и метода AcceptKey, который я предоставил ранее. Также убедитесь, что после последнего символа клавиши принятия вы поместили две новые строки «\ r \ n \ r \ n».
Внедрение собственного протокола WebSockets определенно имеет некоторые преимущества и большой опыт, который вы получаете, а также контроль над самим протоколом. Но на это нужно потратить некоторое время и убедиться, что реализация очень надежна.
В то же время вы можете взглянуть на готовые к использованию решения, которых у Google (опять же) достаточно.
источник
(Размещен ответ от имени ОП) .
Теперь я могу отправлять данные. Это моя новая версия программы благодаря вашим ответам и коду @Maksims Mihejevs.
Сервер
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>
Когда я запускаю этот код, я могу отправлять и получать данные как от клиента, так и от сервера. Единственная проблема в том, что сообщения поступают на сервер в зашифрованном виде. Вот шаги, как работает программа:
Обратите внимание, как зашифровано сообщение от клиента.
источник
WebSockets реализованы с помощью протокола, который включает рукопожатие между клиентом и сервером . Я не думаю, что они работают как обычные розетки. Ознакомьтесь с протоколом и попросите ваше приложение обсудить его. В качестве альтернативы используйте существующую библиотеку WebSocket или .Net4.5beta с API WebSocket .
источник
Я нигде не мог найти простой рабочий пример (по состоянию на 19 января), поэтому вот обновленная версия. У меня хром версия 71.0.3578.98.
Сервер 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; } } }
Клиентские html и 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>
источник
Проблема
Поскольку вы используете WebSocket, спонсор правильный. После получения начальных данных от WebSocket вам необходимо отправить сообщение подтверждения с сервера C #, прежде чем будет передаваться какая-либо дополнительная информация.
HTTP/1.1 101 Web Socket Protocol Handshake Upgrade: websocket Connection: Upgrade WebSocket-Origin: example WebSocket-Location: something.here WebSocket-Protocol: 13
Что-то в этом роде.
Вы можете узнать больше о том, как WebSocket работает на w3 или google.
Ссылки и ресурсы
Вот спецификация протокола: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3
Список рабочих примеров:
источник