| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- /*
- 伪代码计划(详细步骤):
- 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
- {
- /// <summary>
- /// 基于 TouchSocket 的 TCP 通信协议实现。
- /// 提供同步/异步的发送/接收方法,并在连接状态、发送/接收数据时触发事件。
- /// 注意:此类不保证线程安全,调用方应在多线程场景做并发控制。
- /// </summary>
- public class TcpProtocol : ICommunicationProtocol
- {
- /// <summary>
- /// 目标主机地址(仅构造时设置)。
- /// </summary>
- private readonly string _host;
- /// <summary>
- /// 目标端口(仅构造时设置)。
- /// </summary>
- private readonly int _port;
- /// <summary>
- /// TouchSocket 的 TCP 客户端实例。
- /// </summary>
- private TcpClient _client;
- /// <summary>
- /// 获取当前连接状态。若未初始化客户端或客户端不在线,则返回 false。
- /// </summary>
- public bool IsConnected => _client?.Online == true;
- /// <summary>
- /// 等待客户端,用于发送后等待返回(SendThenReturn)的操作。
- /// 注意:该字段在连接成功后由 ConnectAsync 初始化。
- /// </summary>
- public IWaitingClient<ITcpClient, IReceiverResult> _waitClient { get; private set; }
- /// <summary>
- /// 数据包结束符配置,决定接收时的数据分包行为(None/CR/LF/CRLF)。
- /// </summary>
- public Terminator Terminator { get; set; } = Terminator.None;
- /// <summary>
- /// 数据编码,默认使用 ASCII。
- /// 在发送/接收时用于将字符串与字节数组互相转换。
- /// </summary>
- public Encoding Encoding { get; set; } = Encoding.ASCII;
- /// <summary>
- /// 当连接状态发生变化时触发。参数:sender、是否已连接(true=已连接)。
- /// </summary>
- public event Action<object, bool> ConnectionChanged;
- /// <summary>
- /// 当接收到数据时触发。参数:sender、接收到的数据(已使用 <see cref="Encoding"/> 转为字符串)。
- /// </summary>
- public event Action<object, string> DataReceived;
- /// <summary>
- /// 当发送数据时触发。参数:sender、发送的数据(字符串形式,使用 <see cref="Encoding"/> 转换)。
- /// </summary>
- public event Action<object, string> DataSent;
- /// <summary>
- /// 创建一个新的 <see cref="TcpProtocol"/> 实例。
- /// </summary>
- /// <param name="host">目标主机 IP 地址字符串。</param>
- /// <param name="port">目标端口号。</param>
- /// <param name="terminator">可选的数据结束符配置,默认为 <see cref="Terminator.None"/>。</param>
- public TcpProtocol(string host, int port, Terminator terminator = Terminator.None)
- {
- _host = host;
- _port = port;
- Terminator = terminator;
- }
- /// <summary>
- /// 异步连接到远端主机并根据 <see cref="Terminator"/> 配置数据分包适配器。
- /// 成功连接后会触发 <see cref="ConnectionChanged"/> 事件。
- /// </summary>
- /// <returns>如果连接成功返回 true,否则返回 false。</returns>
- public async Task<bool> 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;
- }
- }
- /// <summary>
- /// 异步断开当前连接并触发 <see cref="ConnectionChanged"/> 事件(false)。
- /// </summary>
- /// <returns>已完成的任务。</returns>
- public Task DisconnectAsync()
- {
- ConnectionChanged?.Invoke(this, false);
- _client?.Close();
- return Task.CompletedTask;
- }
- /// <summary>
- /// 异步发送字节数据并等待响应。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <param name="timeout">等待响应的超时时间(毫秒),默认 5000 毫秒。</param>
- /// <returns>返回接收到的字节数组。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端未初始化时抛出。</exception>
- public async Task<byte[]> 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;
- }
- /// <summary>
- /// 异步发送字节数据(不等待响应)。
- /// 注意:内部使用同步发送接口,立即返回 Task.CompletedTask。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <returns>已完成的任务。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接时抛出。</exception>
- 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;
- }
- /// <summary>
- /// 异步发送字符串并等待响应(使用当前 <see cref="Encoding"/> 编码)。
- /// </summary>
- /// <param name="data">要发送的字符串。</param>
- /// <param name="timeout">等待响应的超时时间(毫秒),默认 5000 毫秒。</param>
- /// <returns>接收到的字符串响应(使用当前编码解码)。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端未初始化时抛出。</exception>
- public async Task<string> 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);
- }
- /// <summary>
- /// 同步发送字符串并等待响应(阻塞调用线程)。
- /// </summary>
- /// <param name="data">要发送的字符串。</param>
- /// <param name="timeout">等待响应的超时时间(毫秒),默认 5000 毫秒。</param>
- /// <returns>接收到的字符串响应(使用当前编码解码)。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端未初始化时抛出。</exception>
- 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);
- }
- /// <summary>
- /// 同步发送字节数组并等待响应(阻塞调用线程)。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <param name="timeout">等待响应的超时时间(毫秒),默认 5000 毫秒。</param>
- /// <returns>接收到的字节数组。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接或等待客户端未初始化时抛出。</exception>
- 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;
- }
- /// <summary>
- /// 同步发送字符串(不等待响应)。
- /// </summary>
- /// <param name="data">要发送的字符串。</param>
- /// <exception cref="InvalidOperationException">当尚未连接时抛出。</exception>
- public void Send(string data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, data);
- _client.Send(Encoding.GetBytes(data));
- }
- /// <summary>
- /// 异步发送字符串(不等待响应),使用客户端的异步发送接口。
- /// </summary>
- /// <param name="data">要发送的字符串。</param>
- /// <returns>发送完成的任务。</returns>
- /// <exception cref="InvalidOperationException">当尚未连接时抛出。</exception>
- public async Task SendAsync(string data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, data);
- await _client.SendAsync(Encoding.GetBytes(data));
- }
- /// <summary>
- /// 同步发送字节数组(不等待响应)。
- /// </summary>
- /// <param name="data">要发送的字节数组。</param>
- /// <exception cref="InvalidOperationException">当尚未连接时抛出。</exception>
- public void Send(byte[] data)
- {
- if (_client == null)
- throw new InvalidOperationException("Not connected");
- DataSent?.Invoke(this, Encoding.GetString(data));
- _client.Send(data);
- }
- /// <summary>
- /// 释放底层客户端资源。调用后实例不应再使用。
- /// </summary>
- public void Dispose()
- {
- _client?.Dispose();
- }
- }
- }
|