using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace DOAN.Common.SocketHelper { public static class SocketSenderHelper { // 消息头长度:4字节,用于存储消息体的长度(int) private const int HeaderSize = 4; /// /// 将字符串消息打包为带长度头的字节数组(推荐用于文本协议) /// /// 要发送的字符串消息 /// 完整的报文字节数组(含长度头) public static byte[] PackStringMessage(string message) { byte[] body = Encoding.UTF8.GetBytes(message); return PackBytesMessage(body); } /// /// 将任意字节数组打包为带长度头的完整报文(通用) /// /// 消息内容的字节数组 /// 完整报文字节数组(含长度头) public static byte[] PackBytesMessage(byte[] body) { if (body == null) throw new ArgumentNullException(nameof(body)); int bodyLength = body.Length; // 总长度 = 4字节头部 + body长度 byte[] packet = new byte[HeaderSize + bodyLength]; // 1. 先写入消息长度(4字节,int) byte[] lengthBytes = BitConverter.GetBytes(bodyLength); Buffer.BlockCopy(lengthBytes, 0, packet, 0, HeaderSize); // 2. 再写入消息内容 Buffer.BlockCopy(body, 0, packet, HeaderSize, bodyLength); return packet; } /// /// 通过 Socket 发送一整个报文(自动打包为带长度头的格式) /// /// 已连接的 Socket /// 要发送的字符串消息 public static void SendStringMessage(Socket socket, string message) { byte[] packet = PackStringMessage(message); SendRawPacket(socket, packet); } /// /// 通过 Socket 发送一整个二进制报文(自动打包为带长度头的格式) /// /// 已连接的 Socket /// 要发送的字节数组 public static void SendBytesMessage(Socket socket, byte[] body) { byte[] packet = PackBytesMessage(body); SendRawPacket(socket, packet); } /// /// 直接通过 Socket 发送原始报文数据(不打包,适用于已处理好的报文) /// /// 已连接的 Socket /// 完整的报文数据(含长度头,或者自定义格式) public static void SendRawPacket(Socket socket, byte[] packet) { if (socket == null) throw new ArgumentNullException(nameof(socket)); if (packet == null) throw new ArgumentNullException(nameof(packet)); try { // 发送全部字节 int totalSent = 0; int len = packet.Length; while (totalSent < len) { int sent = socket.Send(packet, totalSent, len - totalSent, SocketFlags.None); if (sent == 0) throw new SocketException((int)SocketError.ConnectionReset); totalSent += sent; } } catch (Exception ex) { throw new Exception("发送报文失败: " + ex.Message, ex); } } /// /// 通过 Socket 发送一个 16 进制格式的原始报文(自动将16进制字符串转为字节数组后发送,不打包长度头) /// /// 已连接的 Socket /// 16进制格式的字符串,例如 "010203" 或 "01 02 03" 或 "01-02-03" /// 当 hexString 格式不正确时抛出 public static void SendHexStringMessage(Socket socket, string hexString) { if (socket == null) throw new ArgumentNullException(nameof(socket)); if (string.IsNullOrWhiteSpace(hexString)) throw new ArgumentException("Hex 字符串不能为空或空白。", nameof(hexString)); // 移除所有非十六进制字符(只保留 0-9, A-F, a-f) string cleanHex = new string(hexString .Where(c => "0123456789ABCDEFabcdef".Contains(c)) .ToArray()); if (cleanHex.Length == 0) throw new ArgumentException("Hex 字符串中不包含有效的十六进制字符。"); if (cleanHex.Length % 2 != 0) throw new ArgumentException("无效的 16 进制字符串:字符个数必须为偶数(每两个字符表示一个字节)。"); byte[] bytes = new byte[cleanHex.Length / 2]; for (int i = 0; i < bytes.Length; i++) { string byteStr = cleanHex.Substring(i * 2, 2); bytes[i] = Convert.ToByte(byteStr, 16); } // 调用现有的发送原始报文方法 SendRawPacket(socket, bytes); } /// /// 通过 NetworkStream 发送一整个报文(字符串) /// /// 网络流 /// 字符串消息 public static void SendStringMessage(NetworkStream stream, string message) { byte[] packet = PackStringMessage(message); SendRawPacket(stream, packet); } /// /// 通过 NetworkStream 发送一整个报文(字节数组) /// /// 网络流 /// 字节数组消息 public static void SendBytesMessage(NetworkStream stream, byte[] body) { byte[] packet = PackBytesMessage(body); SendRawPacket(stream, packet); } /// /// 通过 NetworkStream 发送原始报文数据 /// /// 网络流 /// 完整报文 public static void SendRawPacket(NetworkStream stream, byte[] packet) { if (stream == null) throw new ArgumentNullException(nameof(stream)); if (packet == null) throw new ArgumentNullException(nameof(packet)); try { stream.Write(packet, 0, packet.Length); stream.Flush(); // 确保数据发送出去 } catch (Exception ex) { throw new Exception("通过 NetworkStream 发送报文失败: " + ex.Message, ex); } } /// /// 计算多个 byte[] 数组中所有字节之和,然后对 256 取余,返回校验字节(0~255) /// /// 多个 byte[] 数组,可以是任意数量 /// 校验和,一个字节(0~255),即:所有字节之和 % 256 public static byte CalculateChecksum(params byte[][] byteArrays) { int sum = 0; // 遍历每一个 byte[] 数组 foreach (byte[] bytes in byteArrays) { if (bytes == null) continue; // 如果某个数组为空,跳过(你也可以选择抛异常) // 累加该数组中的每一个字节 foreach (byte b in bytes) { sum += b; // byte 自动提升为 int,安全累加 } } // 计算校验和:总和 % 256,然后转为 byte(范围保证是 0~255) return (byte)(sum % 256); } } }