From 306f8df886df56859fe7cc0243df4d76786d4ed1 Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Thu, 23 Apr 2026 19:42:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=9B=B4=E6=96=B0=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E7=BC=96=E7=A8=8B=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Web-Backend/Others/Netword-Program.md | 371 +++++++++++++++++++-- 1 file changed, 339 insertions(+), 32 deletions(-) diff --git a/docs/Web-Backend/Others/Netword-Program.md b/docs/Web-Backend/Others/Netword-Program.md index f606323..271c883 100644 --- a/docs/Web-Backend/Others/Netword-Program.md +++ b/docs/Web-Backend/Others/Netword-Program.md @@ -433,45 +433,45 @@ class TcpMessageProcessor: ::: -# HTTP -## 定义 +# 二、HTTP +## 2.1 定义   HTTP(HyperText 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.0(1996 年) +## 2.3 版本 +### 2.3.1 HTTP/1.0(1996 年) - 基础版本,支持 GET、POST、HEAD 三种请求方法。 - 每次请求都需要建立新的 TCP 连接(短连接),连接建立和关闭的开销大,性能较低。 - 不支持长连接、管线化请求,仅支持简单的文本传输。 -### HTTP/1.1(1999 年) +### 2.3.2 HTTP/1.1(1999 年) - 默认开启 长连接(Keep-Alive):同一 TCP 连接可处理多个请求,减少连接开销。 - 支持 管线化请求:客户端可连续发送多个请求,无需等待前一个响应返回(部分浏览器未完全支持)。 - 新增请求方法:PUT、DELETE、OPTIONS、TRACE、CONNECT。 - 支持 chunked 编码(分块传输)、缓存控制(Cache-Control)、内容协商等核心功能。 -### HTTP/2(2015 年) +### 2.3.3 HTTP/2(2015 年)   基于 SPDY 协议优化,核心目标是提升性能: - 二进制帧传输:将请求 / 响应数据拆分为二进制帧,而非 HTTP/1.x 的文本格式,解析效率更高。 - 多路复用:同一 TCP 连接中可并发处理多个请求(通过帧的 Stream ID 区分),解决 HTTP/1.1 的 “队头阻塞” 问题。 - 服务器推送(Server Push):服务器可主动向客户端推送关联资源(如 HTML 引用的 CSS/JS),减少客户端请求次数。 - 头部压缩(HPACK):对请求头和响应头进行压缩,减少传输体积(HTTP/1.x 头部重复传输开销大)。 -### HTTP/3(2022 年) +### 2.3.4 HTTP/3(2022 年)   基于 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 定义   MQTT(Message 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}。 | \ No newline at end of file +| 负载 | 可选,消息的实际内容(如 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, TcpPackageHandler>(); + services.AddSingleton(); + + // 1. 注入 MQTT 配置 + services.Configure(context.Configuration.GetSection("Mqtt")); + // 2. 注册 MQTT 客户端 + services.AddSingleton(serviceProvider => new MqttClientFactory().CreateMqttClient()); + // 3. 注册 MQTT 后台服务 + services.AddHostedService(); + }) + .AsMultipleServerHostBuilder() + .AddServer(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(); + + await handler.HandleAsync(session, package); + }) + .ConfigureServerOptions((ctx, config) => config.GetSection("WebSocketServer")); + }) + .ConfigureLogging(logging => logging.ClearProviders()) + .UseNLog() + .Build(); + +await host.RunAsync(); +``` + +  通过.AsMultipleServerHostBuilder()可以构造多服务器实例。 + +## 5.4 TCP服务器 +### 5.4.1 协议数据 +```cs +/// +/// 协议数据结构 示例:65 6D 00 05 00 01 68 65 6C 6C 6F +/// +/// 帧头标识 +/// 数据长度 +/// 数据类型 +/// 数据内容 +/// +record ProtocolFrame( + ushort Magic, + ushort Length, + ushort Type, + byte[] Payload +); +``` + +  可以根据实际情况自定义消息格式。 + +### 5.4.2 协议解析 +```cs +class BinaryPipelineFilter : FixedHeaderPipelineFilter +{ + /// + /// 固定头长度 + /// + public BinaryPipelineFilter() : base(6) + { + } + + /// + /// 从包头中解析出 Body 长度 + /// + /// 字节流 + /// Body 长度 + protected override int GetBodyLengthFromHeader(ref ReadOnlySequence buffer) + { + var reader = new SequenceReader(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; + } + + /// + /// 把完整字节包 → 转换成 ProtocolFrame + /// + /// 字节流 + /// ProtocolFrame + protected override ProtocolFrame DecodePackage(ref ReadOnlySequence buffer) + { + var reader = new SequenceReader(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); + } +} +``` + +  根据协议数据ProtocolFrame来解包。 + +### 5.4.3 消息处理 +```cs +class TcpPackageHandler(ILogger logger) : IPackageHandler +{ + private readonly ILogger _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) : SuperSocketService(serviceProvider, serverOptions) +{ +} +``` + +## 5.5 Websocket服务器 +### 5.5.1 消息处理 +```cs +class WebSocketMessageHandler(ILogger logger) +{ + private readonly ILogger _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 settings, ILogger logger) : BackgroundService +{ + private readonly IMqttClient _mqttClient = mqttClient; + private readonly MqttSettings _settings = settings.Value; + private readonly ILogger _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 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); + } +} +``` + +  需要实现后台服务接口一直运行。 \ No newline at end of file