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

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

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

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

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

这就是这篇文章的意义。

websocket lib

新建项目如下:

基于.Net TcpListener 实现 WebSocketServer 通讯_服务端

主要是对websocket 协议的实现

整体协议大致如下:

基于.Net TcpListener 实现 WebSocketServer 通讯_服务端_02

实际过程中 客户端请求的是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;
}
}
//获取标准的key
key = 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;
}
else
header.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;
}
else
header.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;
}
else
header.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;
}
}

结果展示

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

基于.Net TcpListener 实现 WebSocketServer 通讯_http_03

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

基于.Net TcpListener 实现 WebSocketServer 通讯_网络_04

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

基于.Net TcpListener 实现 WebSocketServer 通讯_HTTP_05


结果还是很顺畅的。

总结

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

代码地址

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

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