Создание "Привет мировой" пример вебсокетов
Я не понимаю, почему я не могу сделать следующий код работать. Я хочу подключиться с помощью 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-это не в состоянии отправить данные, хотя. Всякий раз, когда я размещаю метод отправки, он не будет отправлять, даже если соединение установлено. Как я могу сделать эту работу?
4 ответов:
WebSockets-это протокол, который использует потоковое соединение TCP. Хотя WebSockets-это протокол на основе сообщений.
если вы хотите реализовать свой собственный протокол, то я рекомендую использовать последнюю и стабильную спецификацию (для 18/04/12) RFC 6455. Эта спецификация содержит всю необходимую информацию относительно рукопожатия и кадрирование. Как и большинство описаний сценариев поведения со стороны браузера, так и со стороны сервера. Настоятельно рекомендуется следовать какие рекомендации говорит о стороне сервера во время реализации вашего кода.
в нескольких словах я бы описал работу с WebSockets следующим образом:
создать сервер-сокет (System.Net.Sockets) свяжите его с определенным портом и продолжайте слушать с асинхронным принятием соединений. Что-то вроде этого:
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);вы должны есть!--16-->принимать функция "OnAccept", которая будет осуществлять рукопожатие. В будущем он должен быть в другой ветке, если система предназначена для обработки огромного количества соединений в секунду.
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); } } }после установления соединения, вы должны сделать рукопожатие. На основе спецификации 1.3 Открытие Рукопожатие, после установления соединения вы получите основной HTTP-запрос с некоторой информацией. Пример:
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этот пример основан на версии протокола 13. Иметь в виду что старые версии имеют некоторые различия, но в основном последние версии являются кросс-совместимыми. Различные браузеры могут отправить вам некоторые дополнительные данные. Например, детали браузера и ОС, кэш и другие.
на основе предоставленных сведений о рукопожатии вы должны генерировать строки ответа, они в основном одинаковы, но будут содержать Accpet-Key, который основан на предоставленном Sec-WebSocket-Key. В спецификации 1.3 четко описано, как генерировать ключ ответа. Вот моя функция, которую я использую для 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); }рукопожатие ответ выглядит так:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=но ключ accept должен быть сгенерирован на основе предоставленного ключа от клиента и метода AcceptKey, который я предоставил ранее. Кроме того, убедитесь, что после последнего символа ключа accept вы поставили две новые строки "\r\n\r\n".
- после того, как ответ на рукопожатие отправляется с сервера, клиент должен вызвать"onopen функция", что означает, что вы можете отправлять сообщения после.
- сообщения не отправляются в формате RAW, но у них есть Обрамление Данных. И от клиента к серверу, а также реализовать маскировку для данных на основе предоставленных 4 байт в заголовке сообщения. Хотя от сервера к клиенту вам не нужно применять маскировку данных. Читайте раздел 5. Обрамление Данных в спецификации. Вот copy-paste из моей собственной реализации. Он не готов к использованию кода и должен быть изменен, я публикую его только для того, чтобы дать представление и общую логику чтения/записи с помощью WebSocket обрамление. Перейти к этой ссылке.
- после того, как кадрирование реализовано, убедитесь, что вы получаете данные правильно, используя сокеты. Например, чтобы предотвратить слияние некоторых сообщений в одно, потому что TCP по-прежнему является потоковым протоколом. Это означает, что вы должны прочитать только определенное количество байтов. Длина сообщения всегда основана на заголовке и предоставляет сведения о длине данных в заголовке IT self. Поэтому, когда вы получаете данные из сокета, сначала получите 2 байта, получите детали из заголовка на основе спецификации кадрирования, то если маска предоставила еще 4 байта, а затем длина, которая может быть 1, 4 или 8 байт на основе длины данных. И после данных он сам. После того, как вы прочитаете его, примените демаскировку, и ваши данные сообщения будут готовы к использованию.
- вы можете использовать некоторые Протокол, я рекомендую использовать JSON из-за экономии трафика и прост в использовании на стороне клиента в JavaScript. На стороне сервера вы можете проверить некоторые из парсеров. Существует много им google может быть очень полезен.
реализация собственного протокола 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, которая имеет WebSocket API.
вопрос
Так как вы используете WebSocket, spender является правильным. После получения исходных данных из 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
список рабочих примеры:

Comments