MDN项目指南:使用C编写WebSocket服务器完整教程
2025-07-07 03:13:24作者:裴麒琰
WebSocket作为一种全双工通信协议,在现代Web应用中扮演着重要角色。本文将基于MDN技术文档,深入讲解如何使用C#语言从零开始构建一个符合RFC 6455标准的WebSocket服务器。
一、WebSocket服务器基础架构
1.1 TCP监听器搭建
WebSocket底层基于TCP协议,C#提供了TcpListener
类来实现TCP通信:
using System.Net;
using System.Net.Sockets;
class Server {
public static void Main() {
// 创建监听本地80端口的TCP监听器
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
server.Start();
Console.WriteLine("服务器已启动,等待客户端连接...");
// 接受客户端连接
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("客户端已连接");
}
}
1.2 网络数据流处理
建立连接后,需要通过NetworkStream
进行数据传输:
NetworkStream stream = client.GetStream();
while (true) {
// 等待数据到达
while (!stream.DataAvailable);
// 读取可用数据
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
// 后续处理逻辑...
}
二、WebSocket握手协议实现
2.1 识别握手请求
客户端连接时会发送HTTP升级请求,我们需要识别并处理:
string data = Encoding.UTF8.GetString(bytes);
if (Regex.IsMatch(data, "^GET")) {
// 处理WebSocket握手请求
HandleHandshake(stream, data);
} else {
// 其他类型请求处理
}
2.2 构造握手响应
握手响应需要遵循RFC 6455规范:
void HandleHandshake(NetworkStream stream, string request) {
// 提取Sec-WebSocket-Key
string swk = Regex.Match(request, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim();
// 拼接GUID并计算SHA1哈希
string swkAndSalt = swk + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(swkAndSalt));
string base64Hash = Convert.ToBase64String(hash);
// 构造响应头
byte[] response = Encoding.UTF8.GetBytes(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
$"Sec-WebSocket-Accept: {base64Hash}\r\n\r\n");
stream.Write(response, 0, response.Length);
}
三、WebSocket消息解析
3.1 消息帧结构分析
WebSocket消息采用特定帧格式:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
3.2 消息解码实现
bool fin = (bytes[0] & 0b10000000) != 0;
bool mask = (bytes[1] & 0b10000000) != 0; // 客户端消息必须带掩码
int opcode = bytes[0] & 0b00001111; // 操作码
ulong msgLen = bytes[1] & 0b01111111; // 有效载荷长度
ulong offset = 2;
// 处理扩展长度
if (msgLen == 126) {
msgLen = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
offset = 4;
} else if (msgLen == 127) {
msgLen = BitConverter.ToUInt64(new byte[] {
bytes[9], bytes[8], bytes[7], bytes[6],
bytes[5], bytes[4], bytes[3], bytes[2]
}, 0);
offset = 10;
}
// 解码掩码数据
if (mask && msgLen > 0) {
byte[] masks = new byte[4] {
bytes[offset], bytes[offset+1],
bytes[offset+2], bytes[offset+3]
};
offset += 4;
byte[] decoded = new byte[msgLen];
for (ulong i = 0; i < msgLen; ++i) {
decoded[i] = (byte)(bytes[offset+i] ^ masks[i % 4]);
}
string text = Encoding.UTF8.GetString(decoded);
Console.WriteLine("收到消息: " + text);
}
四、完整服务器实现
将上述组件整合后,完整的WebSocket服务器代码如下:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
class WebSocketServer {
static void Main() {
var server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
server.Start();
Console.WriteLine("WebSocket服务器已启动");
var client = server.AcceptTcpClient();
var stream = client.GetStream();
while (true) {
while (!stream.DataAvailable);
while (client.Available < 3);
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
string data = Encoding.UTF8.GetString(bytes);
if (Regex.IsMatch(data, "^GET")) {
// 握手处理
string swk = Regex.Match(data, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim();
string swka = swk + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] hash = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(swka));
string base64 = Convert.ToBase64String(hash);
byte[] response = Encoding.UTF8.GetBytes(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
$"Sec-WebSocket-Accept: {base64}\r\n\r\n");
stream.Write(response, 0, response.Length);
} else {
// 消息处理
ProcessWebSocketMessage(bytes, stream);
}
}
}
static void ProcessWebSocketMessage(byte[] bytes, NetworkStream stream) {
// 消息解码逻辑...
// 可在此添加消息响应逻辑
}
}
五、客户端测试方案
5.1 HTML测试页面
<!DOCTYPE html>
<html>
<head>
<title>WebSocket测试</title>
<script>
const ws = new WebSocket("ws://localhost:80");
ws.onopen = () => {
console.log("连接已建立");
ws.send("Hello WebSocket!");
};
ws.onmessage = (e) => {
console.log("收到消息:", e.data);
};
</script>
</head>
<body>
<h1>WebSocket测试客户端</h1>
</body>
</html>
六、进阶开发建议
- 多客户端支持:当前实现仅处理单个客户端,可通过异步编程支持多客户端
- 错误处理:添加完善的异常处理机制
- 协议扩展:支持WebSocket子协议和扩展
- 性能优化:使用缓冲区池减少内存分配
通过本文的详细讲解,您应该已经掌握了使用C#实现WebSocket服务器的核心技术。实际开发中可根据需求进行扩展和优化,构建更强大的实时通信应用。