续接上文,上文已经实现了一个可用的IOCP 完成端口服务了。

本来是准备用IOCP实现服务的,但是,在实现过程中出现了Bug,短时间搞不定。

就换另外一种技术 TcpListener 来实现。

这篇文章就根据此服务,来实现一个WebSocketServer。

虽然实现的效果不是很强大,我相信,对很多人来讲,还是具有很大的借鉴意义的。

这就是这篇文章的意义。

websocket lib

新建项目如下:

主要是对websocket 协议的实现

整体协议大致如下:

实际过程中 客户端请求的是http的get请求,然后,服务端,根据Upgrade (升级)机制,来实现一个请求的升级和切换。

握手协议

客户端请求

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 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13

大概是这样子的。

握手完毕,就开始用Socket开始通讯。

websocket 握手协议

    //GET / HTTP/1.1//Host: localhost:9999//Connection: Upgrade//Pragma: no-cache//Cache-Control: no-cache//Upgrade: websocket//Origin: file:////Sec-WebSocket-Version: 13//User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36//Accept-Encoding: gzip, deflate, br//Accept-Language: zh-CN,zh;q=0.9,ja;q=0.8,nl;q=0.7//Cookie: _ga=GA1.1.601168430.1540570802//Sec-WebSocket-Key: TV0AyXfLhtkIDU2OBa7cmw==//Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits/// <summary>/// 创建websocket所需要的头部握手协议/// </summary>public class WebSocketHeader{/// <summary>/// 协议头部信息/// </summary>private string Head = "GET {urladdress} HTTP/1.1";/// <summary>/// 头部请求信息/// </summary>private string HeadStr = string.Empty;/// <summary>/// 内置换行/// </summary>private string NewLine = "\r\n";/// <summary>/// 数值之间的分隔符/// </summary>private string splitStr = ": ";/// <summary>/// 魔数/// </summary>private const string MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";/// <summary>/// 所有可选的头部/// </summary>private Dictionary<string, string> Heads = new Dictionary<string, string>();/// <summary>/// 内置随机/// </summary>private static RandomNumberGenerator random = new RNGCryptoServiceProvider();/// <summary>/// websocketkey信息/// </summary>private string SecWebSocketKey { get; set; }/// <summary>/// 默认链接/// </summary>/// <param name="Host">localhost:9999</param>/// <param name=""></param>public WebSocketHeader(string url){SecWebSocketKey = CreateBase64Key();var URL = ToUri(url);HeadStr = Head.Replace("{urladdress}", URL.AbsolutePath);var port = URL.Port;var schm = URL.Scheme;string Host = (port == 80 && schm == "ws") ||(port == 443 && schm == "wss") ? URL.DnsSafeHost : URL.Authority;string Origin = URL.OriginalString;if (!string.IsNullOrEmpty(Host)) { Add("Host", Host); }if (!string.IsNullOrEmpty(Origin)) { Add("Origin", Origin); }Add("Sec-WebSocket-Key", SecWebSocketKey);Add("Connection", "Upgrade");Add("Upgrade", "websocket");Add("Sec-WebSocket-Version", "13");Add("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits");}/// <summary>/// 默认链接/// </summary>/// <param name="Host">localhost:9999</param>/// <param name=""></param>public WebSocketHeader(Uri URL){HeadStr = Head.Replace("{urladdress}", URL.AbsolutePath);SecWebSocketKey = CreateBase64Key();var port = URL.Port;var schm = URL.Scheme;string Host = (port == 80 && schm == "ws") ||(port == 443 && schm == "wss") ? URL.DnsSafeHost : URL.Authority;string Origin = URL.OriginalString;if (!string.IsNullOrEmpty(Host)) { Add("Host", Host); }if (!string.IsNullOrEmpty(Origin)) { Add("Origin", Origin); }Add("Sec-WebSocket-Key", SecWebSocketKey);Add("Connection", "Upgrade");Add("Upgrade", "websocket");Add("Sec-WebSocket-Version", "13");Add("Sec-WebSocket-Protocol", "chat, superchat");}/// <summary>/// 增加数据/// </summary>/// <param name="name"></param>/// <param name="value"></param>public void Add(string name, string value){if (Heads.ContainsKey(name)){Heads[name] = value;}else{Heads.Add(name, value);}}/// <summary>/// 获取需要发送的数据/// </summary>/// <returns></returns>public byte[] GetRequestByte(){StringBuilder sb = new StringBuilder();sb.Append(HeadStr + NewLine);foreach (var item in Heads){sb.Append(item.Key + splitStr + item.Value + NewLine);}sb.Append(NewLine);string html = sb.ToString();return Encoding.UTF8.GetBytes(html);}/// <summary>/// 直接生成websocket所需要的key/// </summary>/// <returns></returns>public static string CreateBase64Key(){var src = new byte[16];random.GetBytes(src);return Convert.ToBase64String(src);}/// <summary>/// 验证签名是否正确/// </summary>/// <returns></returns>public bool CheckSecWebSocketKey(string key){string temp = key;if (!string.IsNullOrEmpty(key)){if (key.IndexOf("Sec-WebSocket-Accept") > -1){string info = key;string[] list = info.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);foreach (var item in list.Reverse()){if (item.IndexOf("Sec-WebSocket-Accept") > -1){key = item.Split(new string[] { splitStr }, StringSplitOptions.None)[1];break;}}}var Akey = CreateResponseKey(SecWebSocketKey);if (key != Akey){return false;}if (temp.IndexOf("101") < 0){return false;}return true;}return false;}/// <summary>/// 与服务器进行验签/// </summary>/// <param name="base64Key"></param>/// <returns></returns>private static string CreateResponseKey(string base64Key){var buff = new StringBuilder(base64Key, 64);buff.Append(MagicKey);SHA1 sha1 = new SHA1CryptoServiceProvider();var src = sha1.ComputeHash(Encoding.ASCII.GetBytes(buff.ToString()));return Convert.ToBase64String(src);}/// <summary>/// 获取URL地址/// </summary>/// <param name="value"></param>/// <returns></returns>public static Uri ToUri(string value){Uri ret;Uri.TryCreate(value, MaybeUri(value) ? UriKind.Absolute : UriKind.Relative, out ret);return ret;}/// <summary>/// 查看是否是一个URL地址/// </summary>private static bool MaybeUri(string value){if (value == null)return false;if (value.Length == 0)return false;var idx = value.IndexOf(':');if (idx == -1)return false;if (idx >= 10)return false;var schm = value.Substring(0, idx);return IsPredefinedScheme(schm);}/// <summary>/// 对URL地址进行预处理/// </summary>private static bool IsPredefinedScheme(string value){if (value == null || value.Length < 2)return false;var c = value[0];if (c == 'h')return value == "http" || value == "https";if (c == 'w')return value == "ws" || value == "wss";if (c == 'f')return value == "file" || value == "ftp";if (c == 'g')return value == "gopher";if (c == 'm')return value == "mailto";if (c == 'n'){c = value[1];return c == 'e'? value == "news" || value == "net.pipe" || value == "net.tcp": value == "nntp";}return false;}}

websocket 通讯协议的实现

主要的核心就是实现Websocket协议的实现

    /// <summary>/// webSocket协议/// </summary>public class WebSocketProtocol{/// <summary>/// WebSocket 握手 key 魔数/// </summary>private const string MagicKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";/// <summary>/// 是否启动了掩码/// </summary>private const char charOne = '1';private const char charZero = '0';#region 协议之 握手/// <summary>/// 协议处理-http协议握手/// </summary>public static byte[] HandshakeMessage(string data){string key = string.Empty;string info = data;//一步一步来string[] list = info.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);foreach (var item in list.Reverse()){if (item.IndexOf("Sec-WebSocket-Key") > -1){key = item.Split(new string[] { ": " }, StringSplitOptions.None)[1];break;}}//获取标准的keykey = getResponseKey(key);//拼装返回的协议内容var responseBuilder = new StringBuilder();responseBuilder.Append("HTTP/1.1 101 Switching Protocols" + "\r\n");responseBuilder.Append("Upgrade: websocket" + "\r\n");responseBuilder.Append("Connection: Upgrade" + "\r\n");responseBuilder.Append("Sec-WebSocket-Accept: " + key + "\r\n\r\n");return Encoding.UTF8.GetBytes(responseBuilder.ToString());}/// <summary>/// 获取返回验证的key/// </summary>/// <param name="key"></param>/// <returns></returns>public static string getResponseKey(string key){if (string.IsNullOrEmpty(key)){return string.Empty;}else{key += MagicKey;key = Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key.Trim())));return key;}}#endregion#region 协议包解析#region 解码/// <summary>/// 判断数据是否足够/// </summary>/// <param name="data"></param>/// <returns></returns>public static bool DataEnough(long AllLength, MessageHeader header, byte[] data, ref long ReadLength){if (data == null || data.Length < 3) { return false; }//判断数据是否足够Byte[] buffer = data;if (AllLength > (long)header.Payloadlen){ReadLength = header.PayloadDataStartIndex + (long)header.Payloadlen;return true;}return false;}/// <summary>/// 解码数据/// </summary>/// <param name="data"></param>/// <returns></returns>public static Message Decode(Byte[] data, MessageHeader header){Message msg = new Message();msg.head = header;if (data == null || data.Length == 0){msg.Data = string.Empty;return msg;}Byte[] payload = null;if (header != null){//payload = new Byte[data.Length - header.PayloadDataStartIndex];if (header.Payloadlen > 0){payload = new Byte[(int)header.Payloadlen];}else{payload = new Byte[data.Length - header.PayloadDataStartIndex];}Buffer.BlockCopy(data, header.PayloadDataStartIndex, payload, 0, payload.Length);if (header.MASK == charOne){for (int i = 0; i < payload.Length; i++){payload[i] = (Byte)(payload[i] ^ header.Maskey[i % 4]);}}}else{msg.Data = Encoding.UTF8.GetString(data);return msg;}if (header.Opcode == OperType.Text){msg.Data = Encoding.UTF8.GetString(payload);}return msg;}/// <summary>/// 解码数据/// </summary>/// <param name="data"></param>/// <returns></returns>public static Message Decode(Byte[] data){Byte[] buffer = new Byte[14];if (data.Length >= 14){Buffer.BlockCopy(data, 0, buffer, 0, 14);}else{Buffer.BlockCopy(data, 0, buffer, 0, data.Length);}MessageHeader header = analyseHead(buffer);Message msg = new Message();msg.head = header;if (data == null || data.Length == 0){msg.Data = string.Empty;return msg;}Byte[] payload = null;if (header != null){//payload = new Byte[data.Length - header.PayloadDataStartIndex];if (header.Payloadlen > 0){payload = new Byte[(int)header.Payloadlen];}else{payload = new Byte[data.Length - header.PayloadDataStartIndex];}Buffer.BlockCopy(data, header.PayloadDataStartIndex, payload, 0, payload.Length);if (header.MASK == charOne){for (int i = 0; i < payload.Length; i++){payload[i] = (Byte)(payload[i] ^ header.Maskey[i % 4]);}}}else{msg.Data = Encoding.UTF8.GetString(data);return msg;}if (header.Opcode == OperType.Text){msg.Data = Encoding.UTF8.GetString(payload);}return msg;}/// <summary>/// 判断此数据是否是合格的头部信息/// </summary>/// <param name="data"></param>/// <returns></returns>public static bool IsHead(byte[] data, ref MessageHeader header){if (data != null && data.Length > 2){var head = analyseHead(data);header = head;if (head != null){if (head.RSV1 == '0' && head.RSV2 == '0' && head.RSV3 == '0'){if (head.Opcode == OperType.Binary || head.Opcode == OperType.Close || head.Opcode == OperType.Ping || head.Opcode == OperType.Pong || head.Opcode == OperType.Row || head.Opcode == OperType.Text){if (head.MASK == '0'){int lenSize = (int)head.Payloadlen;if (lenSize > 125){if (lenSize > 0xFFFF){lenSize = 127;}else{lenSize = 126;}}if (lenSize == head.Len){return true;}}}}}}return false;}/// <summary>/// 解析数据的头部信息/// </summary>/// <param name="buffer"></param>/// <returns></returns>private static MessageHeader analyseHead(Byte[] buffer){MessageHeader header = new MessageHeader();header.FIN = (buffer[0] & 0x80) == 0x80 ? charOne : charZero;header.RSV1 = (buffer[0] & 0x40) == 0x40 ? charOne : charZero;header.RSV2 = (buffer[0] & 0x20) == 0x20 ? charOne : charZero;header.RSV3 = (buffer[0] & 0x10) == 0x10 ? charOne : charZero;// 判断是否为结束针header.IsEof = (buffer[0] >> 7) > 0;if ((buffer[0] & 0xA) == 0xA)header.Opcode = OperType.Pong;else if ((buffer[0] & 0x9) == 0x9)header.Opcode = OperType.Ping;else if ((buffer[0] & 0x8) == 0x8)header.Opcode = OperType.Close;else if ((buffer[0] & 0x2) == 0x2)header.Opcode = OperType.Binary;else if ((buffer[0] & 0x1) == 0x1)header.Opcode = OperType.Text;else if ((buffer[0] & 0x0) == 0x0)header.Opcode = OperType.Row;header.MASK = (buffer[1] & 0x80) == 0x80 ? charOne : charZero;Int32 len = buffer[1] & 0x7F;header.Len = len;if (len == 126){header.Payloadlen = (UInt64)(buffer[2] << 8 | buffer[3]);if (header.MASK == charOne){header.Maskey = new Byte[4];Buffer.BlockCopy(buffer, 4, header.Maskey, 0, 4);header.PayloadDataStartIndex = 8;}elseheader.PayloadDataStartIndex = 4;}else if (len == 127){Byte[] byteLen = new Byte[8];Buffer.BlockCopy(buffer, 2, byteLen, 0, 8);Array.Reverse(byteLen);header.Payloadlen = BitConverter.ToUInt64(byteLen, 0);if (header.MASK == charOne){header.Maskey = new Byte[4];Buffer.BlockCopy(buffer, 10, header.Maskey, 0, 4);header.PayloadDataStartIndex = 14;}elseheader.PayloadDataStartIndex = 10;}else{header.Payloadlen = (ulong)len;if (header.MASK == charOne){header.Maskey = new Byte[4];Buffer.BlockCopy(buffer, 2, header.Maskey, 0, 4);header.PayloadDataStartIndex = 6;}elseheader.PayloadDataStartIndex = 2;}return header;}#endregion#region 编码/// <summary>/// 把客户端消息打包处理/// </summary>/// <returns>The data.</returns>/// <param name="message">Message.</param>public static byte[] PackageServerData(byte[] message, bool IsMask = true){byte[] bytData = message;byte[] MaskingKey = null;if (IsMask){MaskingKey = createMaskingKey();}UInt64 length = (UInt64)bytData.Length;byte len = (byte)length, lenSize = 0;if (length > 125){if (length > 0xFFFF){len = 127;lenSize = 8;}else{len = 126;lenSize = 2;}}bool Mask = (MaskingKey != null);UInt64 offset = (UInt64)(2 + lenSize);byte[] bytBuffer = new byte[2 + (UInt64)(Mask ? 4 : 0) + length + (UInt64)lenSize];bytBuffer[0] = (byte)(0x80 | (byte)1);bytBuffer[1] = (byte)((Mask ? 0x80 : 0) | len);if (lenSize == 2){bytBuffer[2] = (byte)(length >> 8 & 0xFF);bytBuffer[3] = (byte)(length & 0xFF);}else if (lenSize == 4){bytBuffer[2] = (byte)(length >> 56 & 0xFF);bytBuffer[3] = (byte)(length >> 48 & 0xFF);bytBuffer[4] = (byte)(length >> 40 & 0xFF);bytBuffer[5] = (byte)(length >> 32 & 0xFF);bytBuffer[6] = (byte)(length >> 24 & 0xFF);bytBuffer[7] = (byte)(length >> 16 & 0xFF);bytBuffer[8] = (byte)(length >> 8 & 0xFF);bytBuffer[9] = (byte)(length & 0xFF);}if (Mask){for (UInt64 i = 0; i < 4; i++){bytBuffer[offset + i] = MaskingKey[i];}offset += 4;}for (UInt64 i = 0; i < length; i++){if (Mask) bytData[i] ^= MaskingKey[i % 4];bytBuffer[offset + i] = bytData[i];}return bytBuffer;}/// <summary>/// 打包消息/// </summary>/// <param name="message"></param>/// <param name="IsMask">是否启用虚码,服务器给客户端 不可以用,客户端给服务器,必须用</param>/// <returns></returns>public static byte[] PackageServerData(string message, bool IsMask = true){byte[] bytData = Encoding.UTF8.GetBytes(message);byte[] MaskingKey = null;if (IsMask){MaskingKey = createMaskingKey();}UInt64 length = (UInt64)bytData.Length;byte len = (byte)length, lenSize = 0;if (length > 125){if (length > 0xFFFF){len = 127;lenSize = 8;}else{len = 126;lenSize = 2;}}bool Mask = (MaskingKey != null);UInt64 offset = (UInt64)(2 + lenSize);byte[] bytBuffer = new byte[2 + (UInt64)(Mask ? 4 : 0) + length + (UInt64)lenSize];bytBuffer[0] = (byte)(0x80 | (byte)1);bytBuffer[1] = (byte)((Mask ? 0x80 : 0) | len);if (lenSize == 2){bytBuffer[2] = (byte)(length >> 8 & 0xFF);bytBuffer[3] = (byte)(length & 0xFF);}else if (lenSize == 4){bytBuffer[2] = (byte)(length >> 56 & 0xFF);bytBuffer[3] = (byte)(length >> 48 & 0xFF);bytBuffer[4] = (byte)(length >> 40 & 0xFF);bytBuffer[5] = (byte)(length >> 32 & 0xFF);bytBuffer[6] = (byte)(length >> 24 & 0xFF);bytBuffer[7] = (byte)(length >> 16 & 0xFF);bytBuffer[8] = (byte)(length >> 8 & 0xFF);bytBuffer[9] = (byte)(length & 0xFF);}if (Mask){for (UInt64 i = 0; i < 4; i++){bytBuffer[offset + i] = MaskingKey[i];}offset += 4;}for (UInt64 i = 0; i < length; i++){if (Mask) bytData[i] ^= MaskingKey[i % 4];bytBuffer[offset + i] = bytData[i];}return bytBuffer;}/// <summary>/// 创建一个 Mask 掩码/// </summary>/// <returns></returns>private static byte[] createMaskingKey(){var key = new byte[4];new RNGCryptoServiceProvider().GetBytes(key);return key;}#endregion#endregion}

实现WebsocketServer

是基于 TcpListener 实现的, IOCP 的例子,暂时没了。估计也没人需要。

    /// <summary>/// WebSocketServer/// </summary>public class WebSocketServer{/// <summary>/// 核心监听方法/// </summary>TcpListener listener;/// <summary>/// 服务端监听的端口  作为服务端口/// </summary>public int ListenPort;/// <summary>/// 监听的端口/// </summary>/// <param name="port"></param>public WebSocketServer(int port){this.ListenPort = port;}/// <summary>/// websocket 事件/// </summary>/// <param name="UserToken"></param>public delegate void WebSocketHandler(Socket socket, string data);/// <summary>/// 新用户的事件/// </summary>public event WebSocketHandler OnOpen;/// <summary>/// 新用户的事件/// </summary>public event WebSocketHandler OnClose;/// <summary>/// 新用户的事件/// </summary>public event WebSocketHandler OnMessage;/// <summary>/// 开始监听/// </summary>/// <returns></returns>public WebSocketServer Listen(){listener = new TcpListener(IPAddress.Any, this.ListenPort);listener.Start();ServerStart();Task.Run(() =>{while (true){TcpClient s = listener.AcceptTcpClient();//来一个新的链接ThreadPool.QueueUserWorkItem(r => { Accept(s); });}});return this;}/// <summary>/// 一个新的连接/// </summary>/// <param name="s"></param>public void Accept(TcpClient s){BinaryReader rs = new BinaryReader(s.GetStream());var ReceiveBuffer = new byte[1024];List<byte> ReceiveList = new List<byte>();UserToken userToken = new UserToken();userToken.ConnectSocket = s.Client;userToken.ConnectTime = DateTime.Now;userToken.RemoteAddress = s.Client.RemoteEndPoint;userToken.IPAddress = ((IPEndPoint)(userToken.RemoteAddress)).Address;try{newAcceptHandler(userToken);while (s.Connected){int length = 0;try{length = rs.Read(ReceiveBuffer, 0, ReceiveBuffer.Length);//如果没有读完,就一直读for (int i = 0; i < length; i++){ReceiveList.Add(ReceiveBuffer[i]);}if (s.Client.Available == 0){var data = ReceiveList.ToArray();//接收完毕Task.Run(() => ReceiveHandler(userToken, data));ReceiveList.Clear();}}catch (Exception){break;}if (length == 0){break;}}}catch (Exception){}finally{s.Close();//客户端连接关闭newQuitHandler(userToken);}}/// <summary>/// 接收信息的处理/// </summary>/// <param name="UserToken"></param>public void ReceiveHandler(UserToken userToken, byte[] Receivedata){//说明第一次链接,先进行握手if (!userToken.temp.ContainsKey("WebSocket")){string info = Encoding.UTF8.GetString(Receivedata);if (info.IndexOf("websocket") > -1){var send = userToken.ConnectSocket.Send(WebSocketProtocol.HandshakeMessage(info));if (send > 0){userToken.temp.Add("WebSocket", true);}}}else{var data = WebSocketProtocol.Decode(Receivedata);if (data != null){if (data.head.Opcode == OperType.Close){userToken.ConnectSocket.Close();}else{if (OnMessage != null){OnMessage(userToken.ConnectSocket, data.Data);}else{//接管数据处理Console.WriteLine("收到数据");}}}}}/// <summary>/// 新的链接/// </summary>public void newAcceptHandler(UserToken userToken){if (OnOpen != null){OnOpen(userToken.ConnectSocket, null);}Console.WriteLine("一个新的用户:" + userToken.RemoteAddress.ToString());}/// <summary>/// 服务开始/// </summary>public void ServerStart(){Console.WriteLine("服务开启:local:" + this.ListenPort);}/// <summary>/// 用户退出/// </summary>public void newQuitHandler(UserToken userToken){if (OnClose != null){OnClose(userToken.ConnectSocket, null);}Console.WriteLine("用户退出:" + userToken.RemoteAddress.ToString());}/// <summary>/// 对客户发送数据/// </summary>/// <param name="socket"></param>/// <param name="data"></param>/// <returns></returns>public int SendMessage(Socket socket, string data){int length = -1;try{var bytes = WebSocketProtocol.PackageServerData(Encoding.UTF8.GetBytes(data), false);if (socket != null && socket.Connected){length = socket.Send(bytes);}}catch (Exception ex){ }return length;}}

服务端测试验证

    class Program{static WebSocketServer smartWebSocketServer;static void Main(string[] args){Console.Title = "WebSocket Server Demo 蓝总创精英团队!";smartWebSocketServer = new WebSocketServer(5000);smartWebSocketServer.OnMessage += WebSocketServer_OnMessage;smartWebSocketServer.Listen();Console.WriteLine("开始监听!");Console.ReadLine(); }private static void WebSocketServer_OnMessage(System.Net.Sockets.Socket socket, string data){Console.WriteLine("收到客户端的数据:" + data);smartWebSocketServer.SendMessage(socket, data);}}

客户端测试验证

    class Program{static async Task Main(string[] args){Console.Title = "WebSocket Client Demo 蓝总创精英团队!";var webSocket = await CreateAsync("ws://localhost:5000");if (webSocket != null){Console.WriteLine("服务开始执行!");_ = Task.Run(async () =>{var buffer = ArrayPool<byte>.Shared.Rent(1024);try{while (webSocket.State == WebSocketState.Open){var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);if (result.MessageType == WebSocketMessageType.Close){throw new WebSocketException(WebSocketError.ConnectionClosedPrematurely, result.CloseStatusDescription);}var text = Encoding.UTF8.GetString(buffer.AsSpan(0, result.Count));Console.WriteLine("来自服务端:" + text);}}finally{ArrayPool<byte>.Shared.Return(buffer);}});Console.WriteLine("开始输入:");Thread.Sleep(1000);var text = string.Empty;while (text != "exit"){text = Console.ReadLine();var sendStr = Encoding.UTF8.GetBytes(text);if (webSocket.State != WebSocketState.Open){Console.WriteLine("服务端自己关闭了连接!");}await webSocket.SendAsync(sendStr, WebSocketMessageType.Text, true, CancellationToken.None);}}else{Console.WriteLine("服务连接失败!");}Console.WriteLine("服务执行完毕!");Console.ReadLine();}/// <summary>/// 创建客户端实例/// </summary>/// <param name="cancellationToken"></param>/// <returns></returns>public static async Task<ClientWebSocket> CreateAsync(string ServerUri){var webSocket = new ClientWebSocket();webSocket.Options.RemoteCertificateValidationCallback = delegate { return true; };await webSocket.ConnectAsync(new Uri(ServerUri), CancellationToken.None);if (webSocket.State == WebSocketState.Open){return webSocket;}return null;}}

结果展示

先开启服务端,然后,在开启客户端

服务端已开启,然后开启客户端

这个时候已经看到服务端收到连接了。


结果还是很顺畅的。

总结

很多时候,深入底层甚至协议是有必要的,要不然,自己都不知道自己还知道点什么,毕竟大家都知道了。所以,知道一些别人不知道的,意义还是蛮大的。

代码地址

https://github.com/kesshei/CustomWebSocketServerDemo.git

https://gitee.com/kesshei/CustomWebSocketServerDemo.git

基于.Net TcpListener 实现 WebSocketServer 通讯相关推荐

  1. 用C#实现基于TCP协议的网络通讯

    TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...

  2. 用C#实现基于TCP协议的网络通讯(2)

    2008-09-09 19:36 作者: 出处:www.4oa.com ( 11 ) 砖   ( 12 ) 好  评论 ( 0 ) 条 进入论坛 更新时间:2005-09-29 14:54 关 键 词 ...

  3. 用C#实现基于TCP协议的网络通讯(1)

    2008-09-09 19:35 作者: 出处:www.4oa.com ( 11 ) 砖   ( 12 ) 好  评论 ( 0 ) 条 进入论坛 更新时间:2005-09-29 14:54 关 键 词 ...

  4. matlab中对伺服电机,基于Matlab的伺服电机Modbus通讯研究

    湖北汽车工业学院学报 JournalofHubeiUniversityofAutomotiveTechnology 第31卷第1期 2017 年3月 Vol.31 No.1 Mar.2017 doi: ...

  5. 基于SIP协议的视频通讯

    1.sip协议及其发展 sip(session initiation protocal)称为会话发起协议,是由ietf(internet engineering task force)组织于1999年 ...

  6. GoEasy小程序即时通讯源码 v1.1.0基于GoEasy提供的websocket通讯服务

    介绍: GoEasy小程序即时通讯源码是一个基于GoEasy提供的websocket通讯服务,实现的小程序即时通讯,支持一对一单聊.群聊.会话列表.上下线提醒.历史消息.离线消息,支持发送图片.视频. ...

  7. bs模式Java web,基于BS模式的即时通讯系统的设计与实现(MyEclipse)

    基于BS模式的即时通讯系统的设计与实现(MyEclipse)(包含选题审批表,任务书,开题报告,中期检查表,毕业论文13000字,答辩记录,成绩评定册,源程序) 摘  要:即时通讯(Instant M ...

  8. java 解析数据包_一种基于Java语言的网络通讯数据包解析方法与流程

    本发明涉及网络通讯领域,特别涉及一种基于Java语言的网络通讯数据包解析方法. 背景技术: 计算机系统和网络的大量普及使用使全球跨入了信息化时代.但是,正由于现代社会中几乎一切都在"计算机化 ...

  9. 基于Linux下的即时通讯聊天室项目(全代码 有注释 可直接运行)

    基于Linux下的即时通讯聊天室项目 一.序言 二.具体功能 三.系统客户要求 四.具体代码 1.服务器代码 2.客户端代码 一.序言 最近在写一个基于Linux下的聊天工具 它适合于局域网内所有人进 ...

最新文章

  1. springmvc工作流程简单易懂_三极管的结构和工作特性,简单易懂
  2. Linux rm 命令
  3. Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
  4. 【转】Magento2 安装系列一 虚拟机、CentOS7 安装
  5. flowable理论(一)工作流理论
  6. IDEA / webstorm 破解
  7. 人,总要敢于直面自己的惨淡,才能挺直腰杆
  8. 适合新手的python练习(9)
  9. 去除WMP10上面的东方宽屏图标
  10. iPhone13再现粉屏问题,同时出现闪退/卡顿/重启?
  11. 明天去不了第三届网志大会
  12. 《信息学奥赛一本通》宠物小精灵之收服
  13. 公有云 私有云 混合云
  14. RSRP RSRQ RSSI SNR的含义和区别
  15. 6.1儿童节,致我们回不去的童年
  16. TreeSet,海康威视java校招面试题
  17. 将数字0-9转为中文大写数字
  18. JS中onchange事件:域内容被改变的事件
  19. SpringBoot后台java下载文件及注意的地方
  20. oracle怎么查临时表,Oracle查询问题引发临时表使用

热门文章

  1. 对勾函数_对勾函数的图像及其性质1.pptx
  2. 基于希克斯需求价格弹性计算_#炳哥经济学临考密押DAY1#微观计算题考点大盘点(上)...
  3. 计算机设计核心思想,科学网—计算机设计的两种理念,颠覆os的计算机 - 姜咏江的博文...
  4. 学习没有动力的解决方法
  5. 阿哲学了就来聊——Java反射
  6. 动态时间规整DWT(Dynamic Time Warping)
  7. 谈谈我对SEO快排现象的观察及其背后原理的分析
  8. python写梦幻西游手游脚本辅助_深入解析Lua脚本加密技术,给游戏代码加上“紧箍咒”...
  9. 为什么西游记中要给孙悟空戴上紧箍儿?
  10. python智能写小说软件_Scratch编程实现智能自动创作写小说,自动生成文章,自动写材料...