2025-12-20 13:23:44 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Net;
|
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Infrastructure
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Socket 服务端工具类(你的“接受端”)
|
|
|
|
|
|
/// 功能:监听端口,与网关(客户端)建立长连接,支持收发字节数据(byte[])
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class SocketGatewayServer : IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
private TcpListener _listener;
|
|
|
|
|
|
private bool _isRunning;
|
|
|
|
|
|
private Thread _acceptThread;
|
|
|
|
|
|
|
|
|
|
|
|
private TcpClient _connectedClient;
|
|
|
|
|
|
private NetworkStream _clientStream;
|
|
|
|
|
|
private bool _clientConnected;
|
|
|
|
|
|
|
|
|
|
|
|
private readonly string _listenIp;
|
|
|
|
|
|
private readonly int _listenPort;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构造函数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="listenIp">监听的IP,如 "127.0.0.1" 或 "0.0.0.0"</param>
|
|
|
|
|
|
/// <param name="listenPort">监听的端口,如 8888</param>
|
|
|
|
|
|
public SocketGatewayServer(string listenIp = "127.0.0.1", int listenPort = 8888)
|
|
|
|
|
|
{
|
|
|
|
|
|
_listenIp = listenIp;
|
|
|
|
|
|
_listenPort = listenPort;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 启动服务端,开始监听网关(客户端)连接
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Start()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isRunning) return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
IPAddress ipAddress = string.IsNullOrEmpty(_listenIp) || _listenIp == "0.0.0.0"
|
|
|
|
|
|
? IPAddress.Any
|
|
|
|
|
|
: IPAddress.Parse(_listenIp);
|
|
|
|
|
|
|
|
|
|
|
|
_listener = new TcpListener(ipAddress, _listenPort);
|
|
|
|
|
|
_listener.Start();
|
|
|
|
|
|
_isRunning = true;
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"Socket 服务端已启动,正在监听 [{_listenIp}]:{_listenPort}");
|
|
|
|
|
|
|
|
|
|
|
|
_acceptThread = new Thread(AcceptClient);
|
|
|
|
|
|
_acceptThread.IsBackground = true;
|
|
|
|
|
|
_acceptThread.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"❌ 启动服务端失败: {ex.Message}");
|
|
|
|
|
|
Stop();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 等待并接受一个客户端(网关)连接
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void AcceptClient()
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
while (_isRunning)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine(" 等待网关(客户端)连接...");
|
|
|
|
|
|
|
|
|
|
|
|
TcpClient client = _listener.AcceptTcpClient(); // 阻塞直到客户端连接
|
|
|
|
|
|
Console.WriteLine($"网关(客户端)已连接: {client.Client.RemoteEndPoint}");
|
|
|
|
|
|
StartReceiving();
|
|
|
|
|
|
|
|
|
|
|
|
lock (this)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 断开之前的连接(如果存在)
|
|
|
|
|
|
_connectedClient?.Close();
|
|
|
|
|
|
_connectedClient = client;
|
|
|
|
|
|
_clientStream = client.GetStream();
|
|
|
|
|
|
_clientConnected = true;
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("已建立长连接,可以开始收发字节数据");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isRunning)
|
|
|
|
|
|
Console.WriteLine($"接受客户端时出错: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 向网关(客户端)发送字节数据(byte[])
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="data">要发送的字节数据</param>
|
|
|
|
|
|
/// <returns>是否发送成功</returns>
|
|
|
|
|
|
public bool SendToGateway(byte[] data)
|
|
|
|
|
|
{
|
2025-12-23 08:59:30 +08:00
|
|
|
|
if (!_clientConnected || _clientStream == null || _connectedClient == null)
|
2025-12-20 13:23:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine(" 未连接到网关,无法发送数据");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_clientStream.Write(data, 0, data.Length);
|
|
|
|
|
|
Console.WriteLine($" 已发送 {data.Length} 字节数据给网关");
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"发送数据失败: {ex.Message}");
|
|
|
|
|
|
DisconnectClient();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// (可选)接收来自网关的字节数据
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void StartReceiving()
|
|
|
|
|
|
{
|
|
|
|
|
|
Thread receiveThread = new Thread(ReceiveLoop);
|
|
|
|
|
|
receiveThread.IsBackground = true;
|
|
|
|
|
|
receiveThread.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void ReceiveLoop()
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] buffer = new byte[4096]; // 可根据需求调整缓冲区大小
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
while (_clientConnected && _connectedClient != null && _clientStream != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_clientStream.DataAvailable)
|
|
|
|
|
|
{
|
|
|
|
|
|
int bytesRead = _clientStream.Read(buffer, 0, buffer.Length);
|
|
|
|
|
|
if (bytesRead > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] receivedData = new byte[bytesRead];
|
|
|
|
|
|
Buffer.BlockCopy(buffer, 0, receivedData, 0, bytesRead);
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"收到来自网关的 {bytesRead} 字节数据");
|
|
|
|
|
|
|
|
|
|
|
|
// 🔧 新增:将收到的 byte[] 使用 GBK 编码转换为字符串
|
|
|
|
|
|
// string receivedText = Encoding.GetEncoding("GBK").GetString(receivedData);
|
|
|
|
|
|
|
|
|
|
|
|
// Console.WriteLine($"GBK 解码后的报文内容: {receivedText}");
|
|
|
|
|
|
PrintBytesAsHex(receivedData);
|
|
|
|
|
|
// TODO: 你可以在这里进一步处理 receivedText 或 receivedData
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Thread.Sleep(100); // 避免空转消耗 CPU
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine($"接收数据时出错: {ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
finally
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("接收线程已退出");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将字节数组转换为十六进制字符串并打印
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="bytes">要转换的字节数组</param>
|
|
|
|
|
|
static void PrintBytesAsHex(byte[] bytes)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (bytes == null || bytes.Length == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("字节数组为空或长度为0。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 StringBuilder 或直接拼接,这里使用 String.Join 和格式化
|
|
|
|
|
|
string hexString = BitConverter.ToString(bytes).Replace("-", " "); // 可选:用空格分隔每个字节
|
|
|
|
|
|
// 或者不替换,就是用 "-" 分隔:string hexString = BitConverter.ToString(bytes);
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("字节数据的十六进制表示为:"+ hexString);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DisconnectClient()
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (this)
|
|
|
|
|
|
{
|
|
|
|
|
|
_clientConnected = false;
|
|
|
|
|
|
_clientStream?.Close();
|
|
|
|
|
|
_connectedClient?.Close();
|
|
|
|
|
|
_clientStream = null;
|
|
|
|
|
|
_connectedClient = null;
|
|
|
|
|
|
Console.WriteLine("🔌 已断开与网关的连接");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
|
{
|
|
|
|
|
|
_isRunning = false;
|
|
|
|
|
|
DisconnectClient();
|
|
|
|
|
|
_listener?.Stop();
|
|
|
|
|
|
Console.WriteLine("🛑 Socket 服务端已停止");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
Stop();
|
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|