C# TCP通信客户端库

9

title: C# TCP通信客户端库
url: csharp-hex-tcpclient
date: 2024年06月16日
category: C#
tags:

  • TCP

TcpHexClient是一个基于C#语言的TCP通信库,它作为一个TCP客户端,专门用于与下位机进行交互,收发16进制数据的命令。在该通信模式中,下位机充当TCP服务端,而TcpHexClient则作为客户端向服务端发送命令,并采用一问一答的方式进行通信。需要用户自行解析数据帧。

源码:

TcpHexClient tcp = new("192.168.2.3", 19999);
bool connected = await tcp.ConnectAsync();
Console.WriteLine($"连接:{connected} 属性Connected:{tcp.Connected}");

var dataBytes = new byte[8]; // 要发送的数据
dataBytes.SetValue<int>(0, 1234);
dataBytes.SetValue<float>(4, 123.45f);

int recvLength = 8; //要接收的数据长度
byte[] recv; //接收到回复数据

try
{
    recv = await tcp.SendAsync(dataBytes, recvLength);
    var i = recv.GetValue<int>(0);
    var f = recv.GetValue<float>(4);
    Console.WriteLine($"i = {i}, f = {f}");
    //i = 1234, f = 123.45
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

特点:

  • 自动重连,连接断开后自动重连
  • 一问一答(一发一收)模式,指定要接收的字节长度
  • 线程安全,可以在多线程中进行收发
  • 打印收发数据
  • .Net Standard 2.0开发,可以在.Net Framework 和 .Net core 中使用,没有依赖

属性:

| 属性名称 | 描述 | 默认值 |
| —————– | ———————————————————— | —— |
| Connected | TcpClient连接状态 | false |
| Timeout | 读取数据的超时时间, | 1000ms |
| ReceiveTimeout | 同TcpClient属性ReceiveTimeout | 1000ms |
| SendTimeout | 同TcpClient属性SendTimeout | 1000ms |
| ReceiveBufferSize | 同TcpClient属性ReceiveBufferSize | 8192 |
| SendBufferSize | 同TcpClient属性SendBufferSize | 8192 |
| LogBytesLimit | 默认128,发送/接收的字节数组长度小于128,则将16进制字符串打印到控制台 | 128 |

方法:

ConnectAsync

连接到TCP服务端,返回连接状态。会启动线程监听连接状态,断线后开始重连。

CloseAsync

关闭TCP连接

ReceiveAsync

接收指定长度的字节数据。可能会出现连接断开,超时等异常。

SendAsync

发送数据,并接收指定长度的回复。可能会出现连接断开,超时等异常。

日志

程序中有一个日志接口,默认会打印日志到控制台,你可以实现此借口

public interface ITcpHexLogger
{
    void LogTrace(string message);
    void LogDebug(string message);
    void LogInfo(string message);
    void LogError(string message, Exception ex);
}

扩展方法

程序中还有几个byte数组的扩展方法,用于数值和byte数组的互转,具体示例如下:

static void TestCmdGetSet()
{
    byte[] cmd = new byte[41];

    const byte b = 0xAA;
    const ushort us = 12345;
    const short s = -4321;
    const uint ui = 1987654321;
    const int i = -123456789;
    const ulong ul = 1234567890;
    const long l = -9876543210;
    const float f = 123.456f;
    const double d = 1234567.1234567d;
    const float epsilon = 0.0001f;

    cmd.SetValue<byte>(0, b); //第0个字节开始,写入一个byte
    cmd.SetValue<ushort>(1, us); //第1个字节开始,写入一个ushort
    cmd.SetValue<short>(3, s);
    cmd.SetValue<uint>(5, ui);
    cmd.SetValue<int>(9, i); //第9个字节开始,写入一个int
    cmd.SetValue<ulong>(13, ul); //13 14 15 16 , 17 18 19 20
    cmd.SetValue<long>(21, l); //21 22 23 24 , 25 26 27 28
    cmd.SetValue<float>(29, f); //29 30 31 32
    cmd.SetValue<double>(33, d); //33 34 35 36 , 37 38 39 40

    byte byteValue = cmd.GetValue<byte>(0);
    ushort ushortValue = cmd.GetValue<ushort>(1);
    short shortValue = cmd.GetValue<short>(3); //从第3个字节开始,读取一个short
    uint uintValue = cmd.GetValue<uint>(5);
    int intValue = cmd.GetValue<int>(9);
    ulong ulongValue = cmd.GetValue<ulong>(13);
    long longValue = cmd.GetValue<long>(21);
    float floatValue = cmd.GetValue<float>(29);
    double doubleValue = cmd.GetValue<double>(33);

    Debug.Assert(byteValue == b);
    Debug.Assert(ushortValue == us);
    Debug.Assert(shortValue == s);
    Debug.Assert(uintValue == ui);
    Debug.Assert(intValue == i);
    Debug.Assert(ulongValue == ul);
    Debug.Assert(longValue == l);
    Debug.Assert(Math.Abs(floatValue - f) <= epsilon);
    Debug.Assert(Math.Abs(doubleValue - d) <= epsilon);
}

使用示例:

TcpHexClient tcp = new("192.168.2.3", 19999, new TcpHexLogger());
bool connected = await tcp.ConnectAsync();
tcp.LogBytesLimit = 0;
Console.WriteLine($"连接:{connected} 属性Connected:{tcp.Connected}");
await TestSendRecv(tcp);
Console.ReadLine();

static async Task TestSendRecv(TcpHexClient tcp)
{
    int counter = 0;
    Stopwatch sw = Stopwatch.StartNew();

    while (true)
    {
        var dataBytes = GeneData();

        byte[] recv;

        try
        {
            if (tcp.Connected)
            {
                recv = await tcp.SendAsync(dataBytes, dataBytes.Length);
            }
            else
            {
                Thread.Sleep(1000);
                continue;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            continue;
        }

        if (recv.SequenceEqual(dataBytes))
        {
            counter++;
            if (counter % 10 == 0)
            {
                Console.WriteLine(
                    $"循环:{counter} -- 发送:{dataBytes.Length} --  接收:{recv.Length}"
                );
            }
        }
        else
        {
            Console.WriteLine($"发送失败:发送接收数据不相等");
        }

        if (sw.Elapsed.TotalSeconds > 60)
        {
            Console.WriteLine($"总共收发:{counter}");
            Console.WriteLine("END");
            break;
        }
    }

}

///生成要发送的字节数组
static byte[] GeneData()
{
    var random = new Random();
    int len = random.Next(16, 64);
    var dataBytes = new byte[len];
    random.NextBytes(dataBytes);
    return dataBytes;
}