2025-11-13 15:31:08 +08:00

212 lines
8.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
/// <summary>
/// 将字符串消息打包为带长度头的字节数组(推荐用于文本协议)
/// </summary>
/// <param name="message">要发送的字符串消息</param>
/// <returns>完整的报文字节数组(含长度头)</returns>
public static byte[] PackStringMessage(string message)
{
byte[] body = Encoding.UTF8.GetBytes(message);
return PackBytesMessage(body);
}
/// <summary>
/// 将任意字节数组打包为带长度头的完整报文(通用)
/// </summary>
/// <param name="body">消息内容的字节数组</param>
/// <returns>完整报文字节数组(含长度头)</returns>
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;
}
/// <summary>
/// 通过 Socket 发送一整个报文(自动打包为带长度头的格式)
/// </summary>
/// <param name="socket">已连接的 Socket</param>
/// <param name="message">要发送的字符串消息</param>
public static void SendStringMessage(Socket socket, string message)
{
byte[] packet = PackStringMessage(message);
SendRawPacket(socket, packet);
}
/// <summary>
/// 通过 Socket 发送一整个二进制报文(自动打包为带长度头的格式)
/// </summary>
/// <param name="socket">已连接的 Socket</param>
/// <param name="body">要发送的字节数组</param>
public static void SendBytesMessage(Socket socket, byte[] body)
{
byte[] packet = PackBytesMessage(body);
SendRawPacket(socket, packet);
}
/// <summary>
/// 直接通过 Socket 发送原始报文数据(不打包,适用于已处理好的报文)
/// </summary>
/// <param name="socket">已连接的 Socket</param>
/// <param name="packet">完整的报文数据(含长度头,或者自定义格式)</param>
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);
}
}
/// <summary>
/// 通过 Socket 发送一个 16 进制格式的原始报文自动将16进制字符串转为字节数组后发送不打包长度头
/// </summary>
/// <param name="socket">已连接的 Socket</param>
/// <param name="hexString">16进制格式的字符串例如 "010203" 或 "01 02 03" 或 "01-02-03"</param>
/// <exception cref="ArgumentException">当 hexString 格式不正确时抛出</exception>
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);
}
/// <summary>
/// 通过 NetworkStream 发送一整个报文(字符串)
/// </summary>
/// <param name="stream">网络流</param>
/// <param name="message">字符串消息</param>
public static void SendStringMessage(NetworkStream stream, string message)
{
byte[] packet = PackStringMessage(message);
SendRawPacket(stream, packet);
}
/// <summary>
/// 通过 NetworkStream 发送一整个报文(字节数组)
/// </summary>
/// <param name="stream">网络流</param>
/// <param name="body">字节数组消息</param>
public static void SendBytesMessage(NetworkStream stream, byte[] body)
{
byte[] packet = PackBytesMessage(body);
SendRawPacket(stream, packet);
}
/// <summary>
/// 通过 NetworkStream 发送原始报文数据
/// </summary>
/// <param name="stream">网络流</param>
/// <param name="packet">完整报文</param>
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);
}
}
/// <summary>
/// 计算多个 byte[] 数组中所有字节之和,然后对 256 取余返回校验字节0~255
/// </summary>
/// <param name="byteArrays">多个 byte[] 数组,可以是任意数量</param>
/// <returns>校验和一个字节0~255所有字节之和 % 256</returns>
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);
}
}
}