using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace DeviceCommand.Base { public class Tcp { public string IPAddress { get; set; } = "127.0.0.1"; public int Port { get; set; } = 502; public int SendTimeout { get; set; } = 3000; public int ReceiveTimeout { get; set; } = 3000; public TcpClient TcpClient { get; set; } = new(); public Tcp CreateDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) { IPAddress = ipAddress; Port = port; SendTimeout = sendTimeout; ReceiveTimeout = receiveTimeout; return this; } public static void ChangeDeviceConfig(Tcp tcp, string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) { tcp.IPAddress = ipAddress; tcp.Port = port; if (sendTimeout > 0) tcp.SendTimeout = sendTimeout; if (receiveTimeout > 0) tcp.ReceiveTimeout = receiveTimeout; } public static async Task ConnectAsync(Tcp tcp, CancellationToken ct = default) { if (!tcp.TcpClient.Connected) { tcp.TcpClient.Close(); tcp.TcpClient.Dispose(); tcp.TcpClient = new TcpClient(); await tcp.TcpClient.ConnectAsync(tcp.IPAddress, tcp.Port, ct); } else { var remoteEndPoint = (IPEndPoint)tcp.TcpClient.Client.RemoteEndPoint!; var ip = remoteEndPoint.Address.MapToIPv4().ToString(); bool isSameAddress = ip.Equals(tcp.IPAddress); bool isSamePort = remoteEndPoint.Port == tcp.Port; if (!isSameAddress || !isSamePort) { tcp.TcpClient.Close(); tcp.TcpClient.Dispose(); tcp.TcpClient = new TcpClient(); await tcp.TcpClient.ConnectAsync(tcp.IPAddress, tcp.Port, ct); } } return true; } public static void Close(Tcp tcp) { tcp.TcpClient.Close(); } public static async Task SendAsync(Tcp tcp, byte[] bytes, CancellationToken ct = default) { var timeoutMs = tcp.SendTimeout; if (timeoutMs <= 0) { await tcp.TcpClient.Client.SendAsync(bytes, ct); return; } var sendTask = tcp.TcpClient.Client.SendAsync(bytes, ct).AsTask(); var timeoutTask = Task.Delay(timeoutMs, ct); var completedTask = await Task.WhenAny(sendTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException($"TCP通讯异常:写入操作在 {timeoutMs} ms内未完成"); await sendTask; } public static async Task SendAsync(Tcp tcp, string str, CancellationToken ct = default) { await SendAsync(tcp, Encoding.UTF8.GetBytes(str), ct); } public static async Task ReadAsync(Tcp tcp, byte[] buffer, CancellationToken ct = default) { if (!tcp.TcpClient.Connected) return null; var timeoutMs = tcp.ReceiveTimeout; if (timeoutMs <= 0) return await ReadBytes(tcp, buffer, ct); var readTask = ReadBytes(tcp, buffer, ct); var timeoutTask = Task.Delay(timeoutMs, ct); var completedTask = await Task.WhenAny(readTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException($"TCP通讯异常:读取操作在 {timeoutMs} ms内未完成"); return await readTask; } private static async Task ReadBytes(Tcp tcp, byte[] buffer, CancellationToken ct) { NetworkStream stream = tcp.TcpClient.GetStream(); int bytesRead = 0; while (bytesRead < buffer.Length) { int read = await stream.ReadAsync(buffer, bytesRead, buffer.Length - bytesRead, ct); if (read == 0) return null; bytesRead += read; } return buffer; } public static async Task ReadAsync(Tcp tcp, string delimiter = "\n", CancellationToken ct = default) { delimiter ??= "\n"; var timeoutMs = tcp.ReceiveTimeout; if (timeoutMs <= 0) return await ReadString(tcp, delimiter, ct); var readTask = ReadString(tcp, delimiter, ct); var timeoutTask = Task.Delay(timeoutMs, ct); var completedTask = await Task.WhenAny(readTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException($"TCP通讯异常:读取操作在 {timeoutMs} ms内未完成"); return await readTask; } private static async Task ReadString(Tcp tcp, string delimiter, CancellationToken ct) { NetworkStream stream = tcp.TcpClient.GetStream(); MemoryStream memoryStream = new(); byte[] buffer = new byte[2048]; int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer, ct)) > 0) { memoryStream.Write(buffer, 0, bytesRead); string data = Encoding.UTF8.GetString(memoryStream.ToArray()); int lineEndIndex = data.IndexOf(delimiter); if (lineEndIndex >= 0) return data[..lineEndIndex].Trim(); } return null; } public async Task WriteRead(Tcp tcp, string str, string endstr, CancellationToken ct = default) { await SendAsync(tcp, str, ct); return await ReadAsync(tcp, endstr, ct); } public async Task ConnectAsync(CancellationToken ct = default) { return await ConnectAsync(this, ct); } } }