feat:更新网络编程文档

This commit is contained in:
2026-04-23 19:42:24 +08:00
parent 0ea90ccccf
commit 306f8df886

View File

@@ -433,45 +433,45 @@ class TcpMessageProcessor:
:::
# HTTP
## 定义
# 二、HTTP
## 2.1 定义
  HTTPHyperText Transfer Protocol超文本传输协议是互联网的核心协议之一用于**客户端如浏览器、App与服务器之间的通信**是万维网WWW数据交换的基础。它定义了请求 / 响应的格式、传输规则和状态码等核心机制,支持从简单文本到复杂多媒体(图片、视频、文件)的传输,也是现代 Web 应用的底层通信标准。
## 特性
### 请求 - 响应模式
## 2.2 特性
### 2.2.1 请求 - 响应模式
  通信由客户端主动发起请求服务器接收后处理并返回响应不存在服务器主动向客户端推送数据的情况HTTP/2 引入 Server Push 扩展,可主动推送关联资源)。
  一次完整通信流程:**客户端建立连接 → 发送请求 → 服务器处理 → 返回响应 → 连接关闭**HTTP/1.1 默认开启长连接 Keep-Alive
### 无状态
### 2.2.2 无状态
  **服务器不会保存客户端的会话状态**(如登录状态、浏览记录),每次请求都是独立的,服务器无法通过协议本身识别连续请求是否来自同一客户端。通过 Cookie、Session、Token如 JWT等机制补充状态管理。
## 版本
### HTTP/1.01996 年)
## 2.3 版本
### 2.3.1 HTTP/1.01996 年)
- 基础版本,支持 GET、POST、HEAD 三种请求方法。
- 每次请求都需要建立新的 TCP 连接(短连接),连接建立和关闭的开销大,性能较低。
- 不支持长连接、管线化请求,仅支持简单的文本传输。
### HTTP/1.11999 年)
### 2.3.2 HTTP/1.11999 年)
- 默认开启 长连接Keep-Alive同一 TCP 连接可处理多个请求,减少连接开销。
- 支持 管线化请求:客户端可连续发送多个请求,无需等待前一个响应返回(部分浏览器未完全支持)。
- 新增请求方法PUT、DELETE、OPTIONS、TRACE、CONNECT。
- 支持 chunked 编码分块传输、缓存控制Cache-Control、内容协商等核心功能。
### HTTP/22015 年)
### 2.3.3 HTTP/22015 年)
  基于 SPDY 协议优化,核心目标是提升性能:
- 二进制帧传输:将请求 / 响应数据拆分为二进制帧,而非 HTTP/1.x 的文本格式,解析效率更高。
- 多路复用:同一 TCP 连接中可并发处理多个请求(通过帧的 Stream ID 区分),解决 HTTP/1.1 的 “队头阻塞” 问题。
- 服务器推送Server Push服务器可主动向客户端推送关联资源如 HTML 引用的 CSS/JS减少客户端请求次数。
- 头部压缩HPACK对请求头和响应头进行压缩减少传输体积HTTP/1.x 头部重复传输开销大)。
### HTTP/32022 年)
### 2.3.4 HTTP/32022 年)
  基于 SPDY 协议优化,核心目标是提升性能:
- 解决 TCP 队头阻塞UDP 无连接特性,单个流的阻塞不影响其他流。
- 更快的连接建立QUIC 集成 TLS 1.3减少握手次数1-RTT 甚至 0-RTT 建立连接)。
- 更好的移动网络支持:支持连接迁移(如手机切换 WiFi/4G 时,连接不中断)。
## 组成
### 请求消息Request
## 2.4 组成
### 2.4.1 请求消息Request
  客户端向服务器发送的请求格式,由 请求行、请求头、空行、请求体 四部分组成:
```http
GET /api/courses/1 HTTP/1.1 # 请求行
@@ -506,7 +506,7 @@ Content-Type: application/json
- 文件上传multipart/form-data如 LMS 系统的作业文件上传)。
- 纯文本text/plain。
### 响应消息Response
### 2.4.2 响应消息Response
  服务器向客户端返回的响应格式,由 状态行、响应头、空行、响应体 四部分组成:
```http
HTTP/1.1 200 OK # 状态行
@@ -549,8 +549,8 @@ Set-Cookie: sessionId=abc123; Path=/
- 图片 / 视频:二进制流(如 image/jpeg、video/mp4
- 纯文本text/plain。
# WebSocket
## 定义
# 三、WebSocket
## 3.1 定义
  WebSocket 是一种**全双工、双向、持久化的网络通信协议**(属于应用层协议),由 HTML5 规范定义,专门解决 HTTP 协议无法实现服务器主动向客户端推送数据的问题。
  它的核心特点是:**一旦客户端与服务器建立连接,双方就可以在这个连接上实时、双向地发送数据**,无需像 HTTP 那样每次通信都由客户端发起请求,非常适合实时通信场景(如聊天、直播弹幕、实时数据监控、在线协作等)。
@@ -562,8 +562,8 @@ Set-Cookie: sessionId=abc123; Path=/
| 服务器主动推送 | 不支持HTTP/2 的 Server Push 仅能推送资源,非实时数据) | 原生支持,可主动向客户端发数据 |
| 协议标识 | http:// / https:// | ws:// / wss://(加密版)|
## 特性
### 握手过程
## 3.2 特性
### 3.2.1 握手过程
1. 客户端发送 HTTP 请求,请求头包含 Upgrade: websocket 和 Connection: Upgrade表示要升级为 WebSocket 协议);
2. 服务器响应 101 Switching Protocols握手成功连接转为 WebSocket 持久连接;
3. 后续通信不再使用 HTTP 格式,而是 WebSocket 帧格式(二进制 / 文本)。
@@ -587,50 +587,357 @@ Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= # 服务器加密后的密钥,客户端验证
```
### 全双工通信
### 3.2.2 全双工通信
  此时 HTTP 连接已升级为 WebSocket 连接双方可以通过这个连接实时、双向地发送数据。数据传输采用帧Frame 格式,支持文本数据和二进制数据(如图片、视频流)。
### 帧格式
### 3.2.3 帧格式
  WebSocket 数据以帧为单位传输,帧头包含操作码(文本帧 0x01、二进制帧 0x02、关闭帧 0x08 等)、掩码(客户端发送数据需掩码,服务器无需);
### 无同源限制
### 3.2.4 无同源限制
  WebSocket 不遵循同源策略(但服务器可通过 Origin 头限制跨域);
### 心跳机制
### 3.2.5 心跳机制
  通过 Ping/Pong 帧维持连接(避免网络设备断开空闲连接,如 LMS 系统需定期发送 Ping 帧,服务器回复 Pong 帧)。
# MQTT
## 定义
# 四、MQTT
## 4.1 定义
  MQTTMessage Queuing Telemetry Transport消息队列遥测传输是一种**轻量级、低带宽、低功耗的发布 / 订阅Publish/Subscribe模式物联网IoT通信协议**,由 IBM 于 1999 年设计,核心目标是解决受限设备(如传感器、嵌入式设备)和低带宽、不稳定网络环境下的高效数据传输问题。
## 架构
## 4.2 架构
- 发布者Publisher发送消息的设备 / 服务;
- 订阅者Subscriber接收消息的设备 / 服务;
- broker代理服务器核心中间件接收发布者的消息根据「主题Topic」转发给订阅者如 EMQ X、Mosquitto、RabbitMQ 支持 MQTT 插件);
- 主题Topic消息的分类标识。
## 特性
### QoS服务质量等级
## 4.3 特性
### 4.3.1 QoS服务质量等级
- QoS 0最多一次消息发送一次不保证送达
- QoS 1至少一次消息至少送达一次可能重复
- QoS 2恰好一次消息仅送达一次最可靠。
### 轻量级
### 4.3.2 轻量级
  头部开销极小(固定头部仅 2 字节),消息体支持二进制 / 文本,适合低带宽场景
### 保留消息Retained Message
### 4.3.3 保留消息Retained Message
  broker 保存某个主题的最后一条消息,新订阅者订阅后立即收到该消息
### 遗嘱消息Will Message
### 4.3.4 遗嘱消息Will Message
  客户端异常断开时broker 自动发送预设消息
### 清洁会话Clean Session
### 4.3.5 清洁会话Clean Session
  客户端断开连接后broker 是否保留订阅信息和未送达消息
## 组成
## 4.4 组成
  MQTT 消息由 固定头部Fixed Header、可变头部Variable Header、负载Payload 三部分组成:
| 部分 | 作用 |
|--------------|----------------------------------------------------------------------|
| 固定头部 | 必选2 字节起包含消息类型如发布、订阅、QoS 等级、是否保留消息等标识。 |
| 可变头部 | 可选,仅部分消息类型(如发布、订阅)需要,包含主题名、消息 ID 等信息。 |
| 负载 | 可选,消息的实际内容(如 JSON 字符串、二进制数据),例如 {"temperature": 25}。 |
| 负载 | 可选,消息的实际内容(如 JSON 字符串、二进制数据),例如 {"temperature": 25}。 |
# 五、.Net Core实现
## 5.1 SuperSocket
  [SuperSocket](https://www.supersocket.net/)是一个轻量级, 跨平台而且可扩展的 .Net/Mono Socket 服务器程序框架。可以轻松构建TCP、UDP、WebSocket服务器。
## 5.2 安装依赖
  NuGut安装SuperSocket、SuperSocket.WebSocket和SuperSocket.WebSocket.Server 2.0及以上版本。
## 5.3 配置文件
  appsettings.json
```json
{
"serverOptions": {
"TcpServer": {
"name": "TcpServer",
"listeners": [
{
"ip": "Any",
"port": 4040
}
]
},
"WebSocketServer": {
"name": "WebSocket",
"listeners": [
{
"ip": "Any",
"port": 5050
}
]
}
},
"Mqtt": {
"Host": "127.0.0.1",
"Port": 1883,
"ClientId": "MqttClient",
"Topics": [
"test/topic1",
"test/topic2"
]
}
}
```
## 5.3 主程序
  program.cs
```cs
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
services.AddSingleton<IPackageHandler<ProtocolFrame>, TcpPackageHandler>();
services.AddSingleton<WebSocketMessageHandler>();
// 1. 注入 MQTT 配置
services.Configure<MqttSettings>(context.Configuration.GetSection("Mqtt"));
// 2. 注册 MQTT 客户端
services.AddSingleton<IMqttClient>(serviceProvider => new MqttClientFactory().CreateMqttClient());
// 3. 注册 MQTT 后台服务
services.AddHostedService<MqttService>();
})
.AsMultipleServerHostBuilder()
.AddServer<TcpService, ProtocolFrame, BinaryPipelineFilter>(builder =>
{
builder.ConfigureServerOptions((ctx, config) => config.GetSection("TcpServer"));
})
.AddWebSocketServer(builder =>
{
builder
.UseWebSocketMessageHandler(async (session, package) =>
{
using var scope = session.Server.ServiceProvider.CreateScope();
var handler = scope.ServiceProvider.GetRequiredService<WebSocketMessageHandler>();
await handler.HandleAsync(session, package);
})
.ConfigureServerOptions((ctx, config) => config.GetSection("WebSocketServer"));
})
.ConfigureLogging(logging => logging.ClearProviders())
.UseNLog()
.Build();
await host.RunAsync();
```
&emsp;&emsp;通过.AsMultipleServerHostBuilder()可以构造多服务器实例。
## 5.4 TCP服务器
### 5.4.1 协议数据
```cs
/// <summary>
/// 协议数据结构 示例65 6D 00 05 00 01 68 65 6C 6C 6F
/// </summary>
/// <param name="Magic">帧头标识</param>
/// <param name="Length">数据长度</param>
/// <param name="Type">数据类型</param>
/// <param name="Payload">数据内容</param>
///
record ProtocolFrame(
ushort Magic,
ushort Length,
ushort Type,
byte[] Payload
);
```
&emsp;&emsp;可以根据实际情况自定义消息格式。
### 5.4.2 协议解析
```cs
class BinaryPipelineFilter : FixedHeaderPipelineFilter<ProtocolFrame>
{
/// <summary>
/// 固定头长度
/// </summary>
public BinaryPipelineFilter() : base(6)
{
}
/// <summary>
/// 从包头中解析出 Body 长度
/// </summary>
/// <param name="buffer">字节流</param>
/// <returns>Body 长度</returns>
protected override int GetBodyLengthFromHeader(ref ReadOnlySequence<byte> buffer)
{
var reader = new SequenceReader<byte>(buffer);
// 读取前2字节 → magic
reader.TryReadBigEndian(out ushort magic);
// 再读2字节 → Length
reader.TryReadBigEndian(out ushort length);
// 再读2字节 → Type
reader.TryReadBigEndian(out ushort type);
// 校验帧头
if (magic != 0x656D)
{
throw new Exception("非法帧头");
}
// 限制长度
if (length == 0 || length > 8192)
{
throw new Exception("非法长度");
}
// 返回 Payload 长度
return length;
}
/// <summary>
/// 把完整字节包 → 转换成 ProtocolFrame
/// </summary>
/// <param name="buffer">字节流</param>
/// <returns>ProtocolFrame</returns>
protected override ProtocolFrame DecodePackage(ref ReadOnlySequence<byte> buffer)
{
var reader = new SequenceReader<byte>(buffer);
// 读取前2字节 → magic
reader.TryReadBigEndian(out ushort magic);
// 再读2字节 → Length
reader.TryReadBigEndian(out ushort length);
// 再读2字节 → Type
reader.TryReadBigEndian(out ushort type);
var payload = buffer.Slice(6, length).ToArray();
// 构造ProtocolFrame
return new ProtocolFrame(magic, length, type, payload);
}
}
```
&emsp;&emsp;根据协议数据ProtocolFrame来解包。
### 5.4.3 消息处理
```cs
class TcpPackageHandler(ILogger<TcpPackageHandler> logger) : IPackageHandler<ProtocolFrame>
{
private readonly ILogger<TcpPackageHandler> _logger = logger;
public async ValueTask Handle(IAppSession session, ProtocolFrame package, CancellationToken cancellationToken)
{
_logger.LogInformation($"Magic={package.Magic:X4}, Type={package.Type}, Len={package.Length}");
await session.SendAsync(package.Payload, cancellationToken);
}
}
```
### 5.4.4 TCPService
```cs
class TcpService(IServiceProvider serviceProvider, IOptions<ServerOptions> serverOptions) : SuperSocketService<ProtocolFrame>(serviceProvider, serverOptions)
{
}
```
## 5.5 Websocket服务器
### 5.5.1 消息处理
```cs
class WebSocketMessageHandler(ILogger<WebSocketMessageHandler> logger)
{
private readonly ILogger<WebSocketMessageHandler> _logger = logger;
public async ValueTask HandleAsync(WebSocketSession session, WebSocketPackage package)
{
_logger.LogInformation($"[WebSocket] {package.Message}");
await session.SendAsync("ok");
}
}
```
## 5.6 MQTT服务器
### 5.6 MqttService
```cs
class MqttService(IMqttClient mqttClient, IOptions<MqttSettings> settings, ILogger<TcpPackageHandler> logger) : BackgroundService
{
private readonly IMqttClient _mqttClient = mqttClient;
private readonly MqttSettings _settings = settings.Value;
private readonly ILogger<TcpPackageHandler> _logger = logger;
private MqttClientOptions? _mqttOptions;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// 构建连接配置
_mqttOptions = new MqttClientOptionsBuilder()
.WithClientId($"{_settings.ClientId}_{Guid.NewGuid():N}")
.WithTcpServer(_settings.Host, _settings.Port)
.WithCleanSession()
.Build();
_mqttClient.ConnectedAsync += OnConnectedAsync;
_mqttClient.DisconnectedAsync += OnDisconnectedAsync;
_mqttClient.ApplicationMessageReceivedAsync += HandleMessage;
_logger.LogInformation("[MQTT] 正在连接到服务器 {Host}:{Port}...", _settings.Host, _settings.Port);
await _mqttClient.ConnectAsync(_mqttOptions, stoppingToken);
// 等待程序停止
await Task.Delay(Timeout.Infinite, stoppingToken);
}
public async Task OnConnectedAsync(MqttClientConnectedEventArgs arg)
{
_logger.LogInformation("[MQTT] 已成功连接到服务器");
// 连接成功后订阅主题
foreach (var topic in _settings.Topics)
{
await _mqttClient.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
_logger.LogInformation($"[MQTT] 已订阅主题:{topic}");
}
await Task.CompletedTask;
}
public async Task OnDisconnectedAsync(MqttClientDisconnectedEventArgs arg)
{
_logger.LogError("[MQTT] 连接失败,原因:{Reason}", arg.Reason);
await Task.CompletedTask;
}
public async Task HandleMessage(MqttApplicationMessageReceivedEventArgs arg)
{
var topic = arg.ApplicationMessage.Topic;
var payload = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);
_logger.LogInformation($"\n[MQTT] 收到消息\n主题{topic}\n内容{payload}\n");
await Task.CompletedTask;
}
public async Task<bool> PublishAsync(string topic, string payload, CancellationToken cancellationToken = default)
{
try
{
var message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
await _mqttClient.PublishAsync(message, cancellationToken);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "[MQTT] 发布消息到主题 {Topic} 失败", topic);
return false;
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
await _mqttClient.DisconnectAsync();
await base.StopAsync(stoppingToken);
}
}
```
&emsp;&emsp;需要实现后台服务接口一直运行。