创build一个“Hello World”WebSocket示例
我不明白为什么我不能使下面的代码工作。 我想用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并成功build立连接。 JavaScript不能发送数据。 每当我放置发送方法,即使build立连接也不会发送。 我怎样才能做这个工作?
WebSockets是依赖TCPstream连接的协议。 虽然WebSockets是基于消息的协议。
如果你想实现你自己的协议,那么我build议使用最新的和稳定的规范(适用于18/04/12) RFC 6455 。 本规范包含有关握手和成帧的所有必要信息。 以及从浏览器端和服务器端的行为场景的大部分描述。 强烈build议在执行代码时遵循关于服务器端的build议。
简而言之,我将描述如何使用WebSockets:
-
创build服务器套接字 (System.Net.Sockets)绑定到特定的端口,并保持监听asynchronous接受连接。 类似的东西:
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);
-
你应该有接受函数“OnAccept”将实现握手。 如果系统意味着每秒处理大量的连接,将来必须在另一个线程中。
private void OnAccept(IAsyncResult result){ 尝试{ 套接字客户端= null; if(serverSocket!= null && serverSocket.IsBound){ client = serverSocket.EndAccept(result); } if(client!= null){ / *握手和pipe理ClientSocket * / } catch(SocketExceptionexception){ 最后{ if(serverSocket!= null && serverSocket.IsBound){ serverSocket.BeginAccept(null,0,OnAccept,null); } } }
-
build立连接后,您必须握手 。 根据规范1.3打开握手 ,连接build立后,您将收到一些基本的HTTP请求信息。 例:
GET /聊天HTTP / 1.1 主机:server.example.com 升级:websocket 连接:升级 Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ == 起源:http://example.com 秒 - WebSocket协议:聊天,超级聊天 Sec-WebSocket-Version:13
这个例子是基于协议13的版本。请记住,旧版本有一些差异,但大多数最新版本是交叉兼容的。 不同的浏览器可能会向您发送一些其他数据。 例如浏览器和操作系统的细节,caching和其他。
基于提供的握手细节,您必须生成答案行,它们大部分是相同的,但将包含基于提供的Sec-WebSocket-Key的Accpet-Key。 在规范1.3中清楚地描述了如何生成响应密钥。 这是我一直用于V13的函数:
static private string guid =“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”; 私人stringAcceptKey(refstring键){ stringlongKey =键+ guid; SHA1 sha1 = SHA1CryptoServiceProvider.Create(); byte [] hashBytes = sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey)); 返回Convert.ToBase64String(hashBytes); }
握手答案看起来像这样:
HTTP / 1.1 101交换协议 升级:websocket 连接:升级 Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK + xOo =
但是接受密钥必须是基于从我提供的客户端和方法AcceptKey提供的密钥生成的。 同样,确保在接受密钥的最后一个字符后面放上两行新的“\ r \ n \ r \ n”。
- 从服务器发送握手应答后,客户端应该触发“ onopen ”function,这意味着你可以发送消息。
- 消息不是以原始格式发送的,但是它们具有数据组帧 。 并且从客户端到服务器以及在消息头中提供4字节的数据实现掩码。 虽然从服务器到客户端,您不需要对数据应用屏蔽。 阅读第5章规范中的数据组帧 。 这是从我自己的实施复制粘贴。 这是不准备使用的代码,并且必须被修改,我发布它只是为了给一个想法和WebSocket框架读/写的整体逻辑。 转到此链接 。
- 在成帧实施之后,确保您使用套接字以正确的方式接收数据。 例如,为了防止某些消息被合并为一个,因为TCP仍然是基于stream的协议。 这意味着您必须只读取特定数量的字节。 消息长度总是基于头部,并在头部自身提供数据长度详细信息。 所以当你从Socket接收数据时,首先接收2个字节,根据帧规范从头部得到详细信息,然后如果掩码提供了另外4个字节,则根据数据长度可能是1,4或8个字节。 数据之后,它自我。 读完之后,应用解除掩码,并且可以使用消息数据。
- 你可能想要使用一些数据协议 ,我build议使用JSON,因为stream量的经济性和在JavaScript的客户端使用方便。 对于服务器端,您可能需要检查一些parsing器。 有很多,谷歌可以真正有帮助。
实现自己的WebSockets协议肯定有一些好处和好的经验,以及控制自己的协议。 但是你必须花一些时间来做这件事情,并确保实施是高度可靠的。
在同一时间,你可能会看看准备好使用谷歌(再次)有足够的解决scheme。
WebSocket是通过涉及客户端和服务器握手 的协议来实现的 。 我不认为他们的工作非常像普通的套接字。 阅读协议,并让你的应用程序谈论它。 或者,使用现有的WebSocket库或具有WebSocket API的 .Net4.5beta。
问题
既然你使用的是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或谷歌上工作。
链接和资源
这是协议规范: http : //tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-1.3
工作示例列表:
(代表OP发表答复) 。
我现在能够发送数据。 这是我的新版本的程序感谢您的答案和@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>
当我运行该代码时,我能够发送和接收来自客户端和服务器的数据。 唯一的问题是消息到达服务器时被encryption。 以下是程序运行的步骤:
请注意来自客户端的消息是如何encryption的。