一对多 TCP 服务器与客户端流量控制系统开发文档

发布时间:2026/7/30 22:36:01
一对多 TCP 服务器与客户端流量控制系统开发文档 一对多 TCP 服务器与客户端流量控制系统开发文档1. 文档目的本文档为作者自己的练习项目本人是一名 C# 和 WPF 初学者本文从创建解决方案开始逐步完成一个仅在本机运行的“一台服务器、多台客户端”TCP 通信项目。完成后你可以先启动服务器再多次启动客户端程序每个客户端都能向服务器发送文本服务器只把文本回显给原客户端。服务器与客户端均展示实时流量数据且每个客户端的上传、下载流量均独立限速。本文是第一版的完整范围。请严格按“实施顺序”逐步完成并验证不要一开始同时编写所有文件。ps如有需要可向作者私聊无偿索要源码2. 已确认的需求项目项最终约定开发语言与框架C#、.NET 8、WPF运行方式仅本机模拟不部署到局域网或互联网通信协议TCP服务端形式独立 WPF 程序客户端形式独立 WPF 程序可多次打开通信模式一个服务器对应多个客户端消息类型UTF-8 文本服务器处理方式只回显给发送消息的客户端流量控制每个客户端独立限制上传与下载速率修改限速不提供运行时界面修改修改默认值后重启程序数据展示数值、进度条、表格、日志折线图不需要自动发送需要用于制造流量并验证限速不做的功能文件传输、群发、登录、数据库、远程部署3. 基本概念本项目中“上传”和“下载”的方向固定如下客户端 - 服务器上传流量 服务器 - 原客户端下载流量服务器回显服务器不是聊天服务器。客户端 A 发送的内容只会返回给 A不会发送给 B、C 等其他客户端。上传文本仅回显客户端 AWPF服务器WPF客户端 BWPF客户端 CWPF4. 开发前准备4.1 操作系统和软件WPF 只能在 Windows 上开发和运行。请准备Windows 10 或 Windows 11。Visual Studio 2022 Community 或更高版本。安装 Visual Studio 时勾选“.NET 桌面开发”工作负载。已安装 .NET 8 SDK。4.2 本项目不需要安装的内容第一版不需要 NuGet 包不需要数据库不需要 IIS也不需要 Docker。只使用 .NET 自带的System.Net.Sockets、WPF 和基础集合类。★ 关键三个项目的目标框架必须兼容。共享类库使用net8.0两个 WPF 项目使用net8.0-windows。5. 从零创建解决方案5.1 创建空白解决方案打开 Visual Studio。点击“创建新项目”。搜索并选择“空白解决方案”。名称输入OneToManyTcpDemo。选择一个自己容易找到的目录。点击“创建”。此时解决方案中还没有任何项目。5.2 添加共享类库项目在“解决方案资源管理器”中右键解决方案OneToManyTcpDemo。选择“添加” - “新建项目”。搜索“类库”。选择“类库.NET”。项目名称填写TcpDemo.Shared。框架选择.NET 8.0。点击“创建”。创建后删除 Visual Studio 自动生成的Class1.cs后续使用清晰的文件名代替它。5.3 添加服务器 WPF 项目右键解决方案 - “添加” - “新建项目”。搜索“WPF 应用”。选择“WPF 应用”。项目名称填写TcpDemo.Server。框架选择.NET 8.0。点击“创建”。5.4 添加客户端 WPF 项目重复服务器项目的创建过程项目名称填写TcpDemo.Client完成后解决方案应有三个项目OneToManyTcpDemo ├── TcpDemo.Shared ├── TcpDemo.Server └── TcpDemo.Client5.5 添加项目引用服务器和客户端需要使用共享项目中的报文、限速和统计代码。右键TcpDemo.Server- “添加” - “项目引用”。勾选TcpDemo.Shared。点击“确定”。对TcpDemo.Client重复以上操作。★ 关键不要把FrameCodec、RateLimiter等代码分别复制到服务器和客户端。两端必须引用同一个共享项目才能保证协议一致。5.6 首次生成点击生成 - 生成解决方案在“错误列表”中确认错误0如果此时有错误先解决错误不要继续下一阶段。6. 总体项目结构完成后建议采用以下文件结构。文件夹可在项目上右键“添加” - “新建文件夹”创建类文件可右键相应文件夹“添加” - “类”创建。OneToManyTcpDemo │ ├── TcpDemo.Shared │ ├── Configuration │ │ └── TrafficSettings.cs │ ├── Protocol │ │ ├── MessageType.cs │ │ ├── TcpMessage.cs │ │ └── FrameCodec.cs │ └── Traffic │ ├── TrafficSnapshot.cs │ ├── TrafficCounter.cs │ └── RateLimiter.cs │ ├── TcpDemo.Server │ ├── Models │ │ └── ClientSessionInfo.cs │ ├── Services │ │ ├── TcpServerService.cs │ │ └── ClientSession.cs │ ├── ViewModels │ │ ├── ViewModelBase.cs │ │ ├── RelayCommand.cs │ │ └── ServerViewModel.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── App.xaml │ └── App.xaml.cs │ └── TcpDemo.Client ├── Services │ └── TcpClientService.cs ├── ViewModels │ ├── ViewModelBase.cs │ ├── RelayCommand.cs │ └── ClientViewModel.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── App.xaml └── App.xaml.cs7. 共享项目每个文件要实现什么TcpDemo.Shared不包含任何 WPF 界面也不直接连接服务器。它只负责两端共用的规则和工具。7.1Configuration/TrafficSettings.cs职责集中保存项目固定配置。应定义的内容配置项建议初始值作用DefaultHost127.0.0.1客户端默认连接地址DefaultPort5000服务器默认监听端口UploadBytesPerSecond10 * 1024单个客户端上传限速DownloadBytesPerSecond10 * 1024单个客户端下载限速MaxMessageLength64 * 1024一条文本消息允许的最大字节数RateRefreshIntervalMs500或1000界面刷新速率的时间间隔此文件不应该包含 TCP 收发逻辑也不应该包含 WPF 控件代码。★ 关键限速单位统一为“字节/秒”。10 KB/s的代码值应为10 * 1024而不是10。7.2Protocol/MessageType.cs职责用枚举标识每条 TCP 消息的用途。至少定义枚举名称建议数值发送方向作用TextRequest1客户端 - 服务器普通文本请求EchoResponse2服务器 - 客户端对请求文本的回显SystemMessage3服务器 - 客户端系统提示或错误说明此文件只定义类型不执行网络操作。7.3Protocol/TcpMessage.cs职责表示一条已经被完整解析的应用层消息。应包含TypeMessageType Contentstring示例Type TextRequest Content 你好服务器此文件只负责存数据不在这里进行编码、发送、日志或限速。7.4Protocol/FrameCodec.cs职责解决 TCP 的“半包”和“粘包”问题统一服务器与客户端之间的字节格式。TCP 只保证字节顺序不保证一次读取就是一条消息。因此本项目规定报文格式[4 字节正文长度][1 字节消息类型][UTF-8 文本内容]其中“正文长度”至少应覆盖“消息类型 文本内容”的字节数。建议实现的方法方法要做什么WriteMessageAsync把TcpMessage编码成字节并完整写入NetworkStreamReadMessageAsync从NetworkStream读取并解析一条完整报文返回TcpMessageReadExactlyAsync循环读取直到缓冲区指定长度全部填满ReadMessageAsync的执行步骤先读取 4 字节长度。将长度转换成整数。检查长度是否大于 0 且不超过MaxMessageLength。按该长度读取剩余正文。读取第 1 个字节作为消息类型。剩余字节按 UTF-8 解码为文本。返回TcpMessage。★ 关键ReadAsync一次可能只得到部分字节所以ReadExactlyAsync必须循环读取。没有这一步自动发送时很容易解析错误。7.5Traffic/TrafficSnapshot.cs职责表示某一时刻用于界面显示的流量快照。建议属性TotalBytes BytesPerSecond可增加格式化后的属性或在 ViewModel 内格式化TotalText例如“12.50 KB” SpeedText例如“9.82 KB/s”这个文件的作用是把“统计结果”与“统计过程”分开便于界面读取。7.6Traffic/TrafficCounter.cs职责累加流量并计算实时速率。建议维护的数据累计总字节数 当前统计周期新增字节数 上次刷新时间 当前每秒速率建议公开的方法方法功能AddBytes(int count)每次成功发送或接收后累加字节数GetSnapshot()读取当前总量和当前速率RefreshSpeed()根据上一个周期内新增字节数计算速率计数规则发送成功后增加发送方向字节数。接收成功后增加接收方向字节数。建议统计实际经过NetworkStream的全部字节包括 4 字节长度头保证统计与限速规则一致。7.7Traffic/RateLimiter.cs职责在不阻塞 UI 的情况下限制传输速度。每一个客户端会使用独立的限速器上传和下载各一个。建议字段RateBytesPerSecond 下一次允许发送/处理的时间 用于保护时间计算的锁建议公开方法WaitAsync(int byteCount, CancellationToken token)该方法的含义是当前这批byteCount数据在指定速率下需要等待多长时间等待结束后调用方才可真正读写这批数据。例如限速10 KB/s 本次1 KB 理论最少时间约 100 ms★ 关键必须使用await Task.Delay不能使用Thread.Sleep。Thread.Sleep会造成窗口失去响应。★ 关键不要让所有客户端共用一个RateLimiter。每个ClientSession都要创建自己的上传、下载限速器否则会变成“服务器总流量限速”。8. 服务器项目每个文件要实现什么8.1Models/ClientSessionInfo.cs职责表示服务器窗口中客户端表格的一行数据。建议属性属性作用ClientId服务端递增生成的客户端编号例如 1、2、3RemoteEndPoint客户端地址与端口例如127.0.0.1:53214ConnectedTime建立连接的时间Status已连接、已断开、异常断开UploadTotalText此客户端上传总量UploadSpeedText此客户端当前上传速度DownloadTotalText此客户端下载总量DownloadSpeedText此客户端当前下载速度UploadProgress上传进度条百分比范围 0 到 100DownloadProgress下载进度条百分比范围 0 到 100这个类应该实现INotifyPropertyChanged以便数值变化后 DataGrid 自动刷新。8.2Services/ClientSession.cs职责管理“服务器与某一个客户端”的完整生命周期。每连接一个客户端就创建一个ClientSession。它不能被其他客户端共用。建议持有TcpClient NetworkStream ClientSessionInfo 上传限速器 下载限速器 上传统计器 下载统计器 CancellationTokenSource建议方法方法功能RunAsync循环接收该客户端的消息HandleMessageAsync按消息类型处理一条消息SendAsync向当前客户端发送一条完整消息Close或CloseAsync关闭该连接、释放资源RunAsync中应执行调用FrameCodec.ReadMessageAsync读取一条完整消息。读取到的字节按该客户端上传限速处理。更新该客户端上传统计。如果消息类型是TextRequest创建同内容的EchoResponse。调用SendAsync回显给当前客户端。如果连接关闭、取消或发生异常通知TcpServerService移除当前会话。SendAsync中应执行使用下载限速器等待。调用FrameCodec.WriteMessageAsync。更新下载统计。★ 关键这个类中执行“回显”时目标只能是自己的NetworkStream不能访问其他客户端的网络流。8.3Services/TcpServerService.cs职责管理服务器监听器和全部客户端会话。建议字段TcpListener ConcurrentDictionaryint, ClientSession CancellationTokenSource 递增客户端编号建议方法方法功能StartAsync(string host, int port)创建监听器并开始接收连接AcceptClientsAsync持续调用AcceptTcpClientAsyncRemoveClient删除断开的客户端会话StopAsync停止监听、关闭所有会话启动流程创建 TcpListener - Start - 启动 AcceptClientsAsync 循环 - 接受一个 TcpClient - 分配 ClientId - 创建 ClientSession - 加入字典 - 通知界面添加一行客户端数据 - 启动该 ClientSession.RunAsync停止流程停止接受新客户端 - 取消后台任务 - 逐个关闭 ClientSession - 清空客户端字典 - 通知界面更新客户端数量为 0建议使用事件向 ViewModel 通知客户端已连接 客户端已断开 收到日志 客户端数据已更新这样网络服务不需要直接依赖 WPF 控件。8.4ViewModels/ViewModelBase.cs职责为服务器和客户端的 ViewModel 提供属性变化通知能力。至少实现INotifyPropertyChanged OnPropertyChanged(...)本文件不放 TCP 代码、不放按钮业务代码。8.5ViewModels/RelayCommand.cs职责将 WPF 按钮与 ViewModel 方法绑定。应实现ICommand支持执行方法 是否可执行判断可选服务器至少需要两个命令StartServerCommand StopServerCommand8.6ViewModels/ServerViewModel.cs职责连接服务器服务与服务器界面是服务器窗口的核心。建议属性属性作用ServerIp默认127.0.0.1ServerPort默认5000ServerStatus未启动、运行中、已停止ClientCount当前连接总数ClientsObservableCollectionClientSessionInfoLogsObservableCollectionstringStartServerCommand启动按钮命令StopServerCommand停止按钮命令应实现的业务调用TcpServerService.StartAsync。接到客户端连接事件后添加到Clients。接到断开事件后删除或标记客户端。每次变更后更新ClientCount。接到日志事件后追加到Logs。使用DispatcherTimer每 500 或 1000 毫秒刷新客户端流量显示。★ 关键网络线程不能直接操作ObservableCollection。需要通过 WPF 的Dispatcher切回 UI 线程后再添加日志、添加客户端或更新列表。8.7MainWindow.xaml服务器职责定义服务器窗口的视觉布局和数据绑定。推荐布局第一行监听地址、端口、启动按钮、停止按钮 第二行服务器状态、当前连接数量 中间客户端 DataGrid 底部日志 ListBox客户端DataGrid列应包括编号 远程地址 连接状态 连接时间 上传总量 上传速率 下载总量 下载速率可在上传、下载速率列旁显示进度条进度条最大值为100。绑定要求地址输入框 - ServerIp 端口输入框 - ServerPort 启动按钮 - StartServerCommand 停止按钮 - StopServerCommand 连接数量文本 - ClientCount 客户端表格 - Clients 日志列表 - Logs8.8MainWindow.xaml.cs服务器职责窗口初始化与关闭时资源释放。应做的事情创建ServerViewModel。设置DataContext。在窗口关闭事件中调用停止服务器逻辑避免端口未释放。不要把 TCP 接收循环、限速算法、复杂业务代码直接写到这里。8.9App.xaml与App.xaml.cs服务器第一版通常无需修改。它们负责 WPF 程序启动。若要添加全局异常记录可后续在App.xaml.cs中处理但不属于第一阶段必须内容。9. 客户端项目每个文件要实现什么9.1Services/TcpClientService.cs职责管理一个客户端窗口与服务器之间的 TCP 连接。建议字段TcpClient NetworkStream CancellationTokenSource 上传限速器 上传统计器 下载统计器 SemaphoreSlim 发送锁建议方法方法功能ConnectAsync(host, port)连接服务器并启动接收循环SendTextAsync(text)发送一条文本请求ReceiveLoopAsync持续接收服务器回显DisconnectAsync主动断开并释放资源ConnectAsync的执行流程创建 TcpClient - ConnectAsync - 获取 NetworkStream - 创建取消令牌 - 启动 ReceiveLoopAsync - 通知界面状态变为“已连接”SendTextAsync的执行流程检查是否已连接 - 等待发送锁 - 创建 TextRequest - 根据本条消息字节数执行上传限速 - FrameCodec.WriteMessageAsync - 更新上传统计 - 释放发送锁ReceiveLoopAsync的执行流程循环调用 FrameCodec.ReadMessageAsync - 收到 EchoResponse - 更新下载统计 - 触发“收到回显”事件 - ViewModel 将文本追加到界面日志★ 关键客户端的接收循环必须独立运行。不能在每次发送后才临时读取一次否则自动发送和断开处理容易卡住。★ 关键手动发送和自动发送会同时调用发送功能必须用SemaphoreSlim保证一条完整报文写入完成后下一条才可写入。9.2ViewModels/ViewModelBase.cs与服务器项目作用相同实现INotifyPropertyChanged和OnPropertyChanged。为了初学阶段清晰可以在客户端项目复制同样实现后期可再把它移到共享项目。9.3ViewModels/RelayCommand.cs与服务器项目作用相同实现ICommand将按钮绑定到 ViewModel 方法。客户端需要的命令ConnectCommand DisconnectCommand SendCommand StartAutoSendCommand StopAutoSendCommand9.4ViewModels/ClientViewModel.cs职责保存客户端窗口显示的数据并调用TcpClientService。建议属性属性作用Host默认127.0.0.1Port默认5000ConnectionStatus未连接、连接中、已连接、已断开InputText用户输入待发送的文本Messages发送、回显、系统提示日志UploadTotalText客户端上传总量UploadSpeedText当前上传速度DownloadTotalText客户端下载总量DownloadSpeedText当前下载速度UploadProgress上传速度占限速比例DownloadProgress下载速度占限速比例IsAutoSending是否正在自动发送应实现的业务点击连接时调用ConnectAsync。点击发送时调用SendTextAsync(InputText)。收到服务器回显时追加[回显]日志。使用DispatcherTimer定期读取统计快照并更新流量属性。开始自动发送时创建专用CancellationTokenSource。停止自动发送或断开连接时取消该令牌。自动发送逻辑建议如下只要未取消且连接正常 生成“自动消息序号 时间 填充字符”的文本 调用已有的 SendTextAsync 等待一个较短时间填充字符用于提高单条消息大小例如每条约 1 KB。发送频率应高于限速允许的频率这样才能看出限速器的效果。★ 关键自动发送不能重新编写一套 TCP 写入代码必须复用SendTextAsync确保手动发送与自动发送使用同一个协议、锁、限速和统计逻辑。9.5MainWindow.xaml客户端职责定义客户端窗口界面。推荐分区连接区 流量统计区 文本发送区 自动发送控制区 消息日志区连接区服务器地址输入框 端口输入框 连接按钮 断开按钮 连接状态文本流量统计区上传总量、上传速率、上传限速、上传进度条 下载总量、下载速率、下载限速、下载进度条文本发送区多行或单行 TextBox 发送按钮自动发送控制区开始自动发送按钮 停止自动发送按钮 当前自动发送状态消息日志区使用ListBox或只读多行TextBox显示[系统] 已连接到 127.0.0.1:5000 [发送] 你好 [回显] 你好 [系统] 已断开连接推荐绑定地址输入框 - Host 端口输入框 - Port 发送文本框 - InputText 连接按钮 - ConnectCommand 断开按钮 - DisconnectCommand 发送按钮 - SendCommand 自动发送按钮 - StartAutoSendCommand / StopAutoSendCommand 日志列表 - Messages9.6MainWindow.xaml.cs客户端职责设置DataContext并在窗口关闭时主动断开。应做创建ClientViewModel。将其赋值给窗口DataContext。在窗口关闭事件中调用断开逻辑。不要把自动发送循环、TCP 收发、统计计算直接放在此文件中。9.7App.xaml与App.xaml.cs客户端第一版通常无需额外逻辑保持 Visual Studio 自动生成内容即可。10. 服务器和客户端的数据流一次手动发送的完整过程如下客户端输入文本 - ClientViewModel 调用 TcpClientService.SendTextAsync - 客户端上传限速 - FrameCodec 组装 TCP 报文并写出 - 客户端增加上传统计 - 服务器 ClientSession 读取完整报文 - 服务器对该客户端执行上传限速并增加上传统计 - 服务器创建 EchoResponse - 服务器对该客户端执行下载限速 - 服务器写回原客户端 - 服务器增加下载统计 - 客户端接收循环读取 EchoResponse - 客户端增加下载统计 - ClientViewModel 在日志中显示回显文本11. 界面更新规则11.1 为什么不能每收到一个字节就刷新界面频繁刷新 WPF 控件会造成界面卡顿。因此网络线程只负责累加统计界面通过定时器每 500 或 1000 毫秒读取一次统计快照。11.2 推荐刷新方式服务器和客户端 ViewModel 各自使用一个DispatcherTimer每 500/1000 ms 获取各方向 TrafficCounter 快照 格式化字节数与速率 更新绑定属性 计算进度条百分比格式化参考原始字节数显示文本800800 B20482.00 KB10485761.00 MB12. 必须处理的异常情况情况期望行为端口已被占用服务器显示启动失败日志不崩溃客户端连接不到服务器客户端显示连接失败日志不崩溃客户端主动关闭服务器移除该会话并更新数量服务器主动停止所有客户端变为已断开状态收到非法长度服务端记录异常并断开该客户端消息超过 64 KB不处理该消息记录异常并断开或提示用户点击停止自动发送自动循环尽快停止窗口关闭取消任务并关闭 TCP 资源★ 关键网络异常属于正常情况的一部分。所有await网络操作都应有try/catch/finally避免任意一个客户端断开导致服务器程序整体退出。13. 实施顺序与阶段验收阶段 1项目骨架完成内容创建三个项目、添加引用、创建文件夹和空类。验收整个解决方案可以生成错误数为 0。阶段 2报文协议完成内容MessageType、TcpMessage、FrameCodec。验收可正确编码、解码中文、英文和较长文本。阶段 3单客户端通信完成内容服务器监听、客户端连接、文本回显。验收启动一个客户端发送“你好”能收到同样内容的回显。★ 关键阶段 3 成功前不要加入限速和自动发送。阶段 4多客户端通信完成内容服务器接受多个连接每个连接独立ClientSession。验收至少启动 3 个客户端A 发送消息时只有 A 收到回显。阶段 5服务器统计界面完成内容连接数量、客户端表格、日志。验收客户端连接和断开时数量和列表正确更新。阶段 6客户端统计界面完成内容客户端上传下载总量、实时速率、进度条、消息日志。验收发送文本后客户端和服务器对应数据均变化。阶段 7独立限速完成内容每个ClientSession独立上传下载限速客户端发送端也执行上传限速。验收自动发送 10 秒后单客户端速率稳定在约 10 KB/s多个客户端互不抢占限额。阶段 8自动发送与异常收尾完成内容开始/停止自动发送、服务器停止、客户端断开、异常日志。验收所有“必须处理的异常情况”均不会造成程序崩溃。14. 最终验收用例用例 1启动服务器打开服务器程序。地址为127.0.0.1端口为5000。点击“启动服务器”。预期显示“运行中”连接数量为 0日志显示启动成功。用例 2多客户端连接多次启动客户端程序。每个客户端点击“连接”。预期服务器列表显示每个客户端连接数量与客户端窗口数一致。用例 3只回显给原客户端客户端 A 发送“测试消息”。查看 A、B、C 的日志。预期仅 A 显示[回显] 测试消息。用例 4验证限速在客户端 A 点击“开始自动发送”。保持至少 10 秒。观察客户端 A 和服务器客户端 A 行。预期上传、下载总量不断增加实时速率接近配置限速长期不会明显超过限速。用例 5验证独立性客户端 A、B 同时自动发送。查看服务器表格。预期A、B 的上传下载速率分别显示A 的流量不会占用 B 的限额。用例 6断开关闭客户端 A或点击其“断开”按钮。预期服务器连接数量减少 1日志记录断开服务器继续服务其他客户端。15. 如何启动多个客户端进行演示开发时可先在 Visual Studio 中调试服务器项目。生成成功后客户端程序通常位于TcpDemo.Client\bin\Debug\net8.0-windows\TcpDemo.Client.exe双击该文件可打开一个客户端窗口。重复双击即可打开多个客户端实例。建议演示顺序启动服务器。打开 3 个客户端并连接。展示服务器连接数量为 3。在 A 手动发送文本证明只回显给 A。在 A、B 开启自动发送展示两者独立限速。关闭一个客户端展示服务器连接数量变为 2。16. 初学者最容易出现的问题问题原因处理方式中文乱码两端编码不一致两端统一使用 UTF-8接收文本不完整把一次ReadAsync当作完整消息必须使用ReadExactlyAsync服务器卡住在 UI 线程执行网络循环或Thread.Sleep使用async/await与Task.DelayWPF 跨线程异常后台线程直接更新控件或集合使用Dispatcher或DispatcherTimer自动发送后无法停止没有取消令牌使用CancellationTokenSource两客户端消息混在一起写入 TCP 流时无发送锁客户端发送使用SemaphoreSlim多客户端共用限速限速器定义在服务器全局每个会话各自创建上传、下载限速器端口无法再次启动上次监听器未关闭在停止和窗口关闭时释放TcpListener17. 第一版完成标准当下列项目全部满足时第一版即完成服务器能启动和停止。多个客户端能同时连接。服务器显示正确连接数量。客户端可发送 UTF-8 文本。服务器只回显给发送者。服务器显示每个客户端的上传、下载总量与速率。客户端显示自己的上传、下载总量与速率。每个客户端上传和下载均独立限速。客户端可自动发送文本。关闭客户端或服务器不会导致另一端崩溃。不包含折线图、文件传输、广播和远程部署功能。