C# Tcplistener,Tcp服务端简易封装

2023-12-20 06:33:04

前言

我最近有个需求要写Tcp服务端,我发现Tcp服务端的回调函数比较麻烦,简化Tcp的服务,我打算自己封装一个简单的Tcp服务端。

相关文章

C# TCP应用编程三 异步TCP应用编程

C# Tcpclient Tcplistener 服务器接收多个客户端消息通讯

关于C#Socket断开重连问题

前言

我最近有个Tcp服务端的项目,发现TcpListener 服务端官方写起来很麻烦。而且没有回调函数。现在做个简单的服务端封装

设计

TcpServerService
ShowMsg:打印消息
AddClient_CallBack:新增Tcp客户端回调函数
SendMsg/ReceiveMsg:Tcp客户端发送接受回调
Clients:Tcp客户端集合,连接增加,断开去除
其它函数

代码

 public class TcpServeService
{
    public string Ip { get; set; }

    public int Port { get; set; }

    public TcpListener Server { get; set; }

    public List<TcpClient> Clients { get; set; }

    /// <summary>
    /// 客户端添加回调函数,如果要重写通讯逻辑需要覆盖
    /// </summary>
    public Action<TcpClient> AddClient_CallBack { get; set; }


    public Action<string> ShowMsg { get; set; }

    /// <summary>
    /// 默认自动回复Tcp服务端
    /// </summary>
    /// <param name="ip"></param>
    /// <param name="port"></param>
    public TcpServeService(string ip, int port)
    {
        Clients = new List<TcpClient>();
        ShowMsg = (msg) => Console.WriteLine(msg);
        AddClient_CallBack = (client) => AutoSendBack(client);
        this.Ip = ip;
        this.Port = port;
        Server = new TcpListener(IPAddress.Parse(ip), port);
    }


    /// <summary>
    /// Tcp添加Client回调
    /// </summary>
    /// <param name="ar"></param>
    private void DoAcceptTcpclient(IAsyncResult ar)
    {
        // Get the listener that handles the client request.
        TcpListener listener = (TcpListener)ar.AsyncState;

        // End the operation and display the received data on 
        // the console.
        TcpClient client = listener.EndAcceptTcpClient(ar);

        Clients.Add(client);

        // Process the connection here. (Add the client to a
        // server table, read data, etc.)
        ShowMsg($"Tcp客户端连接成功!,当前连接数{Clients.Count},Id[{client.Client.RemoteEndPoint.ToString()}]");
        AddClient_CallBack(client);
        //开启线程用来不断接收来自客户端的数据
        Server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpclient), Server);
    }

    /// <summary>
    /// 移除Tcp客户端
    /// </summary>
    /// <param name="client"></param>
    public void RemoveClient(TcpClient client)
    {
        NetworkStream stream = client.GetStream();
        ShowMsg($"Tcp客户端连接断开!,当前连接数{Clients.Count},Id[{client.Client.RemoteEndPoint.ToString()}]");
        stream.Close();
        client.Close();
        Clients.Remove(client);
    }

    /// <summary>
    /// 启动Tcp服务
    /// </summary>
    public void Start()
    {
        Server.Start();
        Server.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpclient), Server);
        ShowMsg($"Tcp服务端启动成功!IP[{Ip}],Port[{Port}]");
    }

    /// <summary>
    /// 返回数据
    /// </summary>
    /// <param name="Str"></param>
    /// <param name="Bytes"></param>
    public record TcpData(string Str, byte[] Bytes);

    /// <summary>
    /// 同步阻塞读取数据
    /// </summary>
    /// <param name="client"></param>
    /// <returns></returns>
    public static TcpData ReadMsg(TcpClient client)
    {
        NetworkStream networkStream = client.GetStream();
        var resBytes = new byte[client.ReceiveBufferSize];
        var num = networkStream.Read(resBytes, 0, resBytes.Length);
        resBytes = resBytes.Take(num).ToArray();
        var resStr = UnicodeEncoding.ASCII.GetString(resBytes);
        if (!IsConnect(client))
        {
            throw new Exception($"{client.Client.RemoteEndPoint?.ToString()}Tcp连接已断开");
        }
        
        return new TcpData(resStr,resBytes);
    }

    /// <summary>
    /// 发送Ascll数据
    /// </summary>
    /// <param name="tcpClient"></param>
    /// <param name="msg"></param>
    public static void SendMsg(TcpClient tcpClient, string msg)
    {
        byte[] arrSendMsg = Encoding.UTF8.GetBytes(msg);
        SendMsg(tcpClient, arrSendMsg);
    }

    /// <summary>
    /// Tcp客户端连接是否断开
    /// </summary>
    /// <param name="tcpClient"></param>
    /// <returns></returns>
    public static bool IsConnect(TcpClient tcpClient)
    {
        if (tcpClient.Client.Poll(1, SelectMode.SelectRead) && tcpClient.Available == 0)
        {
            return false;
        }
        else { return true; }
    }

    /// <summary>
    /// 发送Bytes[]数据
    /// </summary>
    /// <param name="tcpClient"></param>
    /// <param name="msg"></param>
    public static void SendMsg(TcpClient tcpClient, byte[] msg)
    {
        NetworkStream networkStream = tcpClient.GetStream();
        networkStream.Write(msg, 0, msg.Length);
    }

    /// <summary>
    /// 发送并返回数据
    /// </summary>
    /// <param name="tcpClient"></param>
    /// <param name="msg"></param>
    /// <returns></returns>
    public static TcpData SendAndReceive(TcpClient tcpClient,string msg)
    {
        SendMsg(tcpClient,msg);
        return ReadMsg(tcpClient);
    }

    public static TcpData SendAndReceive(TcpClient tcpClient, byte[] msg)
    {
        SendMsg(tcpClient, msg);
        return ReadMsg(tcpClient);
    }
    


    /// <summary>
    /// 默认自动回复,异常捕捉
    /// </summary>
    /// <param name="tcpClient"></param>
    /// <param name="timeOut">超时时间</param>
    /// <returns></returns>
    public async Task AutoSendBack(TcpClient tcpClient, int timeOut = 10 * 1000)
    {
        //超时时间
        tcpClient.ReceiveTimeout = timeOut;
        tcpClient.SendTimeout = timeOut;
        while (true)
        {
            try
            {
                if (!Clients.Contains(tcpClient))
                {
                    throw new Exception("Tcp客户端已被移除!");
                }
                var receive = ReadMsg(tcpClient);
                ShowMsg($"TcpClient[{tcpClient.Client.RemoteEndPoint?.ToString()}]:收到数据{receive.Str}");
                SendMsg(tcpClient, receive.Str);
            }
            catch (Exception ex)
            {
                RemoveClient(tcpClient);
                ShowMsg("发送失败");
                ShowMsg(ex.Message);
            }
        }
    }

    
}

简单使用

//对tcpServeService进行了默认配置,默认自动回复,自动维护Client集合
TcpServeService tcpServeService = new TcpServeService("192.168.100.21", 10003);

//如果想要自定义回复,需要覆盖AddClient_CallBack函数,使用异步任务处理连接
//tcpServeService.AddClient_CallBack = ((client) => {
//    Task.Run(() =>
//    {
//        //你的客户端连接异步任务
//    });
//});

//如果想要打印在Winfrom/WPF的界面,覆盖此回调
//tcpServeService.ShowMsg = (msg) =>
//{
//    //你的消息打印函数
//};
//tcpServeService.Start();
tcpServeService.Start();

运行结果

在这里插入图片描述

文章来源:https://blog.csdn.net/qq_44695769/article/details/135067893
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。