C# 创建一个;“你好,世界”;WebSocket示例

C# 创建一个;“你好,世界”;WebSocket示例,c#,javascript,websocket,C#,Javascript,Websocket,我不明白为什么我不能让下面的代码工作。我想用JavaScript连接到我的服务器控制台应用程序。然后将数据发送到服务器 以下是服务器代码: static void Main(string[] args) { TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 9998); server.Start(); var client =

我不明白为什么我不能让下面的代码工作。我想用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无法发送数据。每当我放置send方法时,即使建立了连接,它也不会发送。我怎样才能做到这一点呢?

WebSocket就是其中的一部分。我认为它们的工作原理与普通插座不同。仔细阅读协议,让你的应用程序来讨论它。或者,使用现有的WebSocket库或.Net4.5beta,它有一个。

问题 因为您使用的是WebSocket,所以spender是正确的。在从WebSocket接收到初始数据后,您需要从C#server发送握手消息,然后才能传递任何进一步的信息

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上工作

链接和资源 以下是协议规范:

工作示例列表:


WebSockets是一种依赖于TCP流连接的协议。尽管WebSocket是基于消息的协议

如果您想实现自己的协议,那么我建议您使用最新且稳定的规范(适用于12月18日)。 本规范包含有关握手和帧的所有必要信息。以及从浏览器端和服务器端对行为场景的大部分描述。 强烈建议在代码实现过程中遵循关于服务器端的建议

简而言之,我会这样描述如何使用WebSocket:

  • 创建服务器套接字(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); HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Socket serverSocket=新套接字(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP); Bind(新的IPEndPoint(IPAddress.Any,8080)); serverSocket.Listen(128); serverSocket.BeginAccept(null,0,OnAccept,null)
  • 您应该具有接受功能“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); } } } 接受时私有无效(IAsyncResult结果){ 试一试{ socketclient=null; if(serverSocket!=null&&serverSocket.IsBound){ client=serverSocket.EndAccept(结果); } 如果(客户端!=null){ /*握手和管理ClientSocket*/ } }捕获(SocketException异常){ }最后{ if(serverSocket!=null&&serverSocket.IsBound){ serverSocket.BeginAccept(null,0,OnAccept,null); } } }
  • 建立连接后,您必须进行握手。根据规范,建立连接后,您将收到包含一些信息的基本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 获取/聊天HTTP/1.1 主机:server.example.com 升级:websocket 连接:升级 Sec WebSocket密钥:DGHLIHNHBXBSZSUB25JZQ== 来源:http://example.com Sec WebSocket协议:聊天,超级聊天 Sec WebSocket版本:13 此示例基于协议13的版本。请记住,旧版本有一些差异,但大多数最新版本是交叉兼容的。不同的浏览器可能会向您发送一些附加数据。例如浏览器和操作系统详细信息、缓存等

    根据提供的握手详细信息,您必须生成应答行,它们基本相同,但将包含Accpet密钥,即基于提供的Sec WebSocket密钥。规范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); } 静态专用字符串guid=“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”; 私有字符串接受密钥(参考字符串密钥){ 字符串longKey=key+guid; SHA1 SHA1=SHA1CryptoServiceProvider.Create(); byte[]hashBytes=sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey)); 返回Convert.ToBase64String(hashBytes); } 握手回答如下所示:

    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); HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= HTTP/1.1 101交换协议 升级:websocket 连接:升级 Sec WebSocket接受:S3pplMBITXAQ9KYGZHZRBK+xOo= 但是accept key必须是基于客户端提供的key和我之前提供的AcceptKey方法生成的。另外,请确保在accept键的最后一个字符后添加两行“\r\n\r\n”

  • 从服务器发送握手应答后,客户端应触发“onopen”功能,这意味着您可以在之后发送消息
  • 消息不是以原始格式发送的,但它们具有数据帧。从客户端到服务器,并根据消息头中提供的4个字节实现数据屏蔽。尽管从服务器到客户端,您不需要对数据应用屏蔽。阅读说明书中的章节。 这是我自己实现的复制粘贴。它还没有准备好使用代码,必须进行修改,我发布它只是为了给出一个想法和WebSocket框架读/写的总体逻辑。去
  • 实现成帧后,请确保使用套接字以正确的方式接收数据。例如,为了防止某些消息合并为一个消息,因为TCP仍然是基于流的协议。这意味着您必须只读取特定数量的字节。消息的长度始终基于标头,并在标头中提供数据长度详细信息。所以,当您从套接字接收数据时,首先接收2个字节,根据帧规范从报头获取详细信息,然后如果mask提供了另外4个字节,然后根据数据长度确定长度可能是1、4或8个字节。之后呢
    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;
        }
    }
    }