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

这篇具有很好参考价值的文章主要介绍了C# Tcplistener,Tcp服务端简易封装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

相关文章

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

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

关于C#Socket断开重连问题

前言

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

设计

代码

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; }

    /// <summary>
    /// 客户端自动断开
    /// </summary>
    public Action<TcpClient> RemoveClient_CallBack { get; set; }


    /// <summary>
    /// 检测是否断开Tcp服务
    /// </summary>
    public int CheckConnectTime { get; set; } = 1 * 1000;

    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);

        CheckConnectLoop();
    }


    /// <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>
    /// 监测Tcp 客户端服务是否断开
    /// </summary>
    /// <returns></returns>
    private async Task CheckConnectLoop()
    {
        while (true)
        {
            //Console.WriteLine("检测设备连接状况");
            try
            {
                var removeList = new List<TcpClient>();
                foreach (var item in Clients)
                {
                    var isConnect = IsConnect(item);
                    if (!isConnect)
                    {
                        //Console.WriteLine($"设备已断开");
                        if (RemoveClient_CallBack != null)
                        {
                            RemoveClient_CallBack(item);
                        }
                        removeList.Add(item);
                    }

                }
                foreach (var item in removeList)
                {
                    Clients.Remove(item);
                }
                await Task.Delay(CheckConnectTime);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());   
            }
           
        }
    }

    /// <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();

运行结果

C# Tcplistener,Tcp服务端简易封装,C#,c#,tcp/ip,开发语言

心跳包Tcp服务器

在现实使用中,串口设备的有效连接距离一般不超过5m,太长了就考虑转网络了。串口转网络也是十分成熟的技术。为了区别每个串口设备的,需要一个类似于身份证编码的东西。一般不用IP地址,因为Ip地址可能会出现冲突,一般使用的是一个8位的心跳包作为区别
C# Tcplistener,Tcp服务端简易封装,C#,c#,tcp/ip,开发语言
当每次连上设备的时候,都会自动发送一个心跳包。
C# Tcplistener,Tcp服务端简易封装,C#,c#,tcp/ip,开发语言

心跳包客户端实体类

   public class TcpHeartCilent
   {

       /// <summary>
       /// 心跳包
       /// </summary>
       public byte[] HeartNum { get; set; }

       public string HeartStr
       {
           get => BitConverter.ToString(HeartNum).Replace("-", " ");
       }

       public string ClientIp { get => TcpClient.Client.RemoteEndPoint.ToString(); }

       /// <summary>
       /// Client对象
       /// </summary>
       public TcpClient TcpClient { get; set; }
   }

心跳包服务端

public class TcpHeartService
{
    /// <summary>
    /// 服务端
    /// </summary>
    public TcpServeService TcpServeService { get; set; }

    /// <summary>
    /// 设备添加回调
    /// </summary>
    public Action<TcpHeartCilent> AddTcpHeartClient_CallBack { get; set; }

    /// <summary>
    /// 设备断开回调
    /// </summary>
    public Action<TcpHeartCilent> RemoveTcpHeartClient_CallBack { get; set; }

    public List<TcpHeartCilent> HeartCilents { get; set; } = new List<TcpHeartCilent>();

    public TcpHeartService(string ip, int port)
    {
        TcpServeService = new TcpServeService(ip, port);
        TcpServeService.AddClient_CallBack = (client) =>
        {
            try
            {
                var res = TcpServeService.ReadMsg(client);
                var list = from item in HeartCilents
                           where item.TcpClient.Equals(client)
                           select item;
                if (list.Count() == 0)
                {
                    Console.WriteLine($"心跳包[{BitConverter.ToString(res.Bytes).Replace("-", " ")}].新设备,按照心跳包添加设备信息");
                    var newItem = new TcpHeartCilent()
                    {
                        TcpClient = client,
                        HeartNum = res.Bytes
                    };
                    HeartCilents.Add(newItem);
                    if (AddTcpHeartClient_CallBack != null)
                    {
                        AddTcpHeartClient_CallBack(newItem);
                    }
                    Console.WriteLine("当前设备列表");
                    for (var i = 0; i < HeartCilents.Count; i++)
                    {
                        var item = HeartCilents[i];
                        Console.WriteLine($"设备心跳包[{item.HeartStr}],设备Ip地址[{item.TcpClient.Client.RemoteEndPoint.ToString()}]");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }


        };
        TcpServeService.RemoveClient_CallBack = (client) =>
        {
            var item = HeartCilents.FirstOrDefault(t => t.TcpClient.Equals(client));
            if (item != null)
            {

                Console.WriteLine($"设备[{item.ClientIp}] 已断开!");
                HeartCilents.Remove(item);
                if (RemoveTcpHeartClient_CallBack != null)
                {
                    RemoveTcpHeartClient_CallBack(item);

                }
            }
        };
    }

    public void Start()
    {
        TcpServeService.Start();
    }

}

测试结果

        static void Main(string[] args)
        {

            TcpHeartService tcpHeartService = new TcpHeartService("192.168.100.21", 41966);
            tcpHeartService.Start();

            Console.WriteLine("Hello, World!");

            Console.ReadKey();
        }

C# Tcplistener,Tcp服务端简易封装,C#,c#,tcp/ip,开发语言

  • 可以实现心跳包入库,自动断开

C# Tcplistener,Tcp服务端简易封装,C#,c#,tcp/ip,开发语言

必须要确保连接第一个数据包是心跳包,不然会出逻辑问题。文章来源地址https://www.toymoban.com/news/detail-766310.html

到了这里,关于C# Tcplistener,Tcp服务端简易封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C# 简易TCP网口调试助手(一) 客户端Client

      最近的上位机开发工作中开始频繁涉及到网口、串口的通讯,网上找了各种资料和帖子都没怎么找到好用的开源代码或者工具。目前找到几个好一点的方式来实现的网口和串口通讯工具包,先写个好用的TCP的negut包记录下来,将使用的步骤写下来做个记录。   本博客主要用

    2024年04月13日
    浏览(47)
  • TCP/IP为例数据封装与解封装过程

    一TCP/IP数据封装过程 TCP/IP数据封装分为5个步骤,在本文中我将按流程顺序介绍TCP/IP的数据封装过程 应用层:应用层将需要传输的数据(如,文字,视频,图片,音频等)转换成能够被电脑识别的二进制数字也就是将数据翻译成设备能识别的语言 传输层:数据段被分为数据段

    2024年02月07日
    浏览(46)
  • TCP/IP五层模型、封装和分用

    IP地址:表示计算机的位置,分源IP和目标IP ;举个例子:买快递,商家从上海发货,上海就是源IP,收货地址是湖北,湖北就是目标IP。 端口号:表示一个具体的应用程序,分源端口和目标端口 ,如:某个进程或服务的端口号是8080;还是买快递的例子,发货的商家,商家就

    2024年02月09日
    浏览(55)
  • 简易TCP服务器搭建

    目录 一、套接字及其分类 二、相关接口函数 三、TCP服务器搭建流程 1、创建套接字socket() 2、保存服务器信息 3、套接字绑定 4、监听客户端连接请求 5、接收客户端连接请求 6、数据收发 7、关闭套接字 四、运行结果         所谓套接字(Socket),就是对网络中不同主机上的

    2023年04月18日
    浏览(58)
  • 计算机网络-TCP/IP模型及五层参考模型(OSI与TCP/IP相同点 不同点 5层参考模型及数据封装与解封装)

    OSI:先理论,但没有实践 TCP/IP:先实践,再理论 TCP/IP:基于协议栈而分层 网络接口层:数据链路层与物理层 应用层:包含上三层 异构网络互联:实现不同厂家生产的设备进行相互通信 IP协议面向无连接 传输层是端到端,有实现可靠传输的功能,即有面向连接的功能 传输层

    2024年01月23日
    浏览(53)
  • 网络编程1—— IP地址 + 端口号 +TCP/IP协议 + 协议分层的封装与应用

    本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下! 从本篇文章开始就要分享网络编程的内容了,越发的感受到了编程的魅力,鸡汤来喽!! 1️⃣单机阶段:计算机跟计算

    2024年02月12日
    浏览(42)
  • 简易TCP客户端和服务器端通信

    #includeiostream #include winsock2.h   #include ws2tcpip.h   #includestdlib.h using namespace std; #define  BUF_SIZE  1024 int main() {     cout \\\"客户端\\\" endl;     //设置Winsock版本,     WSADATA   wsaData;     if (WSAStartup(MAKEWORD(2, 2), wsaData) != 0)     {         cout \\\"error\\\" endl;         exit(1);     }     //创建通

    2024年04月29日
    浏览(49)
  • 【JavaEE初阶】网络原理|认识协议|协议分层|TCP/IP模型|封装和分用

    目录 一、认识协议 1.概念 2.作用(为什么需要协议?) 二、协议分层 1.为什么需要⽹络协议的分层? 2. 协议分层是什么 3.分层带来的好处 三、TCP/IP五层(或四层)模型 (1)物理层 (2)数据链路层 (3)网络层 (4)传输层 (5)应用层 四、封装和分用(协议的层和层之间

    2024年04月23日
    浏览(42)
  • Qt实现简易的多线程TCP服务器(附源码)

    目录 一.UI界面的设计 二.服务器的启动 三.实现自定义的TcpServer类 1.在widget中声明自定义TcpServer类的成员变量 2.在TcpServer的构造函数中对于我们声明的m_widget进行初始化,m_widget我们用于后续的显示消息等,说白了就是主界面的更新显示等 四.实现自定义的TcpSocket类 1.TcpSocket.

    2024年04月17日
    浏览(79)
  • C#使用TCP/IP长连接获取数据、心跳检测

    逻辑处理 创建一个Socket对象并连接到服务器,可以使用Socket.Connect()方法。 使用Socket.Send()方法发送数据到服务器。 使用Socket.Receive()方法接收服务器返回的数据。 实现心跳检测,可以定时向服务器发送一个心跳包,如果服务器没有响应,则说明连接已断开。 如果连接断开,

    2024年02月13日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包