/* 伪代码计划(详细步骤): 1. 在文件顶部添加多行注释,说明将要执行的注释生成工作(用于审阅)。 2. 为公共类 `TcpProtocol` 添加 XML 文档注释,描述其用途与行为。 3. 为构造函数添加注释,说明参数含义及默认值。 4. 为每个公共属性(如 `IsConnected`,`_waitClient`,`Terminator`,`Encoding`)添加 XML 注释,说明返回值或作用,指明线程/连接相关注意事项(只读/可写)。 5. 为事件(`ConnectionChanged`、`DataReceived`、`DataSent`)添加注释,说明何时触发、参数含义。 6. 为每个公共方法添加 XML 注释: - `ConnectAsync`: 说明尝试连接的行为、返回值及异常处理,标注使用的配置项(结束符处理)。 - `DisconnectAsync`: 说明断开连接的行为及事件触发。 - `SendAndReceiveAsync(byte[], int)`: 说明发送/接收的流程、超时含义及可能抛出的异常。 - `SendAsync(byte[])`, `Send(byte[])`, `Send(string)`, `SendAsync(string)`: 说明发送方法区别与同步/异步注意事项。 - 同步版本的 `SendAndReceive` 方法说明阻塞行为与异常。 - `Dispose`: 说明释放资源。 7. 保持现有实现不变,仅插入文档注释与必要的内部注释,以便于 IntelliSense 与维护。 8. 确保注释为中文,简洁明了,符合 .NET XML 注释惯例,并且不修改代码逻辑或签名。 注:所有注释均以 XML 文档注释形式写入,便于 Visual Studio 的 IntelliSense 展示。 */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web.UI.WebControls.WebParts; using TeamAAS_VP.Enums; using TeamAAS_VP.Models.Robot; using TouchSocket.Core; using TouchSocket.Sockets; namespace TeamAAS_VP.Core.Lights { /// /// 基于 TouchSocket 的 TCP 通信协议实现。 /// 提供同步/异步的发送/接收方法,并在连接状态、发送/接收数据时触发事件。 /// 注意:此类不保证线程安全,调用方应在多线程场景做并发控制。 /// public class TcpProtocol : ICommunicationProtocol { /// /// 目标主机地址(仅构造时设置)。 /// private readonly string _host; /// /// 目标端口(仅构造时设置)。 /// private readonly int _port; /// /// TouchSocket 的 TCP 客户端实例。 /// private TcpClient _client; /// /// 获取当前连接状态。若未初始化客户端或客户端不在线,则返回 false。 /// public bool IsConnected => _client?.Online == true; /// /// 等待客户端,用于发送后等待返回(SendThenReturn)的操作。 /// 注意:该字段在连接成功后由 ConnectAsync 初始化。 /// public IWaitingClient _waitClient { get; private set; } /// /// 数据包结束符配置,决定接收时的数据分包行为(None/CR/LF/CRLF)。 /// public Terminator Terminator { get; set; } = Terminator.None; /// /// 数据编码,默认使用 ASCII。 /// 在发送/接收时用于将字符串与字节数组互相转换。 /// public Encoding Encoding { get; set; } = Encoding.ASCII; /// /// 当连接状态发生变化时触发。参数:sender、是否已连接(true=已连接)。 /// public event Action ConnectionChanged; /// /// 当接收到数据时触发。参数:sender、接收到的数据(已使用 转为字符串)。 /// public event Action DataReceived; /// /// 当发送数据时触发。参数:sender、发送的数据(字符串形式,使用 转换)。 /// public event Action DataSent; /// /// 创建一个新的 实例。 /// /// 目标主机 IP 地址字符串。 /// 目标端口号。 /// 可选的数据结束符配置,默认为 。 public TcpProtocol(string host, int port, Terminator terminator = Terminator.None) { _host = host; _port = port; Terminator = terminator; } /// /// 异步连接到远端主机并根据 配置数据分包适配器。 /// 成功连接后会触发 事件。 /// /// 如果连接成功返回 true,否则返回 false。 public async Task ConnectAsync() { try { _client = new TouchSocket.Sockets.TcpClient(); var config = new TouchSocketConfig(); config.SetRemoteIPHost(new IPHost(IPAddress.Parse(_host), _port)); config.ConfigurePlugins(a => { a.UseTcpReconnection(); }); ////如需永远尝试连接,tryCount设置为-1即可。 ////设置结束符 if (Terminator == Terminator.None) { config.SetTcpDataHandlingAdapter(() => { return new NormalDataHandlingAdapter(); }); ////亦或者省略\r\n,但此时调用方不能高速调用,会粘包 } else if (Terminator == Terminator.CR) { config.SetTcpDataHandlingAdapter(() => { return new TerminatorPackageAdapter("\r"); }); //命令行中使用\r结尾 } else if (Terminator == Terminator.LF) { config.SetTcpDataHandlingAdapter(() => { return new TerminatorPackageAdapter("\n"); }); //命令行中使用\n结尾 } else if (Terminator == Terminator.CRLF) { config.SetTcpDataHandlingAdapter(() => { return new TerminatorPackageAdapter("\r\n"); }); //命令行中使用\r\n结尾 } //载入配置 _client.Setup(config); ////调用CreateWaitingClient获取到IWaitingClient的对象。 _waitClient = _client.CreateWaitingClient(new WaitingOptions() { FilterFunc = response => //设置用于筛选的fun委托,当返回为true时,才会响应返回 { return true; //if (response.Data.Length == 1) //{ // return true; //} //return false; } }); var result = await _client.TryConnectAsync(); if (result.IsSuccess) { ConnectionChanged?.Invoke(this, true); return true; } ConnectionChanged?.Invoke(this, false); return false; } catch { return false; } } /// /// 异步断开当前连接并触发 事件(false)。 /// /// 已完成的任务。 public Task DisconnectAsync() { ConnectionChanged?.Invoke(this, false); _client?.Close(); return Task.CompletedTask; } /// /// 异步发送字节数据并等待响应。 /// /// 要发送的字节数组。 /// 等待响应的超时时间(毫秒),默认 5000 毫秒。 /// 返回接收到的字节数组。 /// 当尚未连接或等待客户端未初始化时抛出。 public async Task SendAndReceiveAsync(byte[] data, int timeout = 5000) { if (_waitClient == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, Encoding.GetString(data)); var response = await _waitClient.SendThenReturnAsync(data, timeout); DataReceived?.Invoke(this, Encoding.GetString(response)); return response; } /// /// 异步发送字节数据(不等待响应)。 /// 注意:内部使用同步发送接口,立即返回 Task.CompletedTask。 /// /// 要发送的字节数组。 /// 已完成的任务。 /// 当尚未连接时抛出。 public Task SendAsync(byte[] data) { if (_client == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, Encoding.GetString(data)); _client.Send(data); return Task.CompletedTask; } /// /// 异步发送字符串并等待响应(使用当前 编码)。 /// /// 要发送的字符串。 /// 等待响应的超时时间(毫秒),默认 5000 毫秒。 /// 接收到的字符串响应(使用当前编码解码)。 /// 当尚未连接或等待客户端未初始化时抛出。 public async Task SendAndReceiveAsync(string data, int timeout = 5000) { if (_waitClient == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, data); var response = await SendAndReceiveAsync(Encoding.GetBytes(data), timeout); DataReceived?.Invoke(this, Encoding.GetString(response)); return Encoding.GetString(response); } /// /// 同步发送字符串并等待响应(阻塞调用线程)。 /// /// 要发送的字符串。 /// 等待响应的超时时间(毫秒),默认 5000 毫秒。 /// 接收到的字符串响应(使用当前编码解码)。 /// 当尚未连接或等待客户端未初始化时抛出。 public string SendAndReceive(string data, int timeout = 5000) { if (_waitClient == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, data); var response = _waitClient.SendThenReturn(Encoding.GetBytes(data), timeout); DataReceived?.Invoke(this, Encoding.GetString(response)); return Encoding.GetString(response); } /// /// 同步发送字节数组并等待响应(阻塞调用线程)。 /// /// 要发送的字节数组。 /// 等待响应的超时时间(毫秒),默认 5000 毫秒。 /// 接收到的字节数组。 /// 当尚未连接或等待客户端未初始化时抛出。 public byte[] SendAndReceive(byte[] data, int timeout = 5000) { if (_waitClient == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, Encoding.GetString(data)); var response = _waitClient.SendThenReturn(data, timeout); DataReceived?.Invoke(this, Encoding.GetString(response)); return response; } /// /// 同步发送字符串(不等待响应)。 /// /// 要发送的字符串。 /// 当尚未连接时抛出。 public void Send(string data) { if (_client == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, data); _client.Send(Encoding.GetBytes(data)); } /// /// 异步发送字符串(不等待响应),使用客户端的异步发送接口。 /// /// 要发送的字符串。 /// 发送完成的任务。 /// 当尚未连接时抛出。 public async Task SendAsync(string data) { if (_client == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, data); await _client.SendAsync(Encoding.GetBytes(data)); } /// /// 同步发送字节数组(不等待响应)。 /// /// 要发送的字节数组。 /// 当尚未连接时抛出。 public void Send(byte[] data) { if (_client == null) throw new InvalidOperationException("Not connected"); DataSent?.Invoke(this, Encoding.GetString(data)); _client.Send(data); } /// /// 释放底层客户端资源。调用后实例不应再使用。 /// public void Dispose() { _client?.Dispose(); } } }