美文网首页
C#TCP通讯

C#TCP通讯

作者: 小羊爱学习 | 来源:发表于2024-07-25 15:33 被阅读0次

简介

  • TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在C#中,TCP程序设计是指利用Socket类、TcpClient类和 TcpListener类编写的网络通信程序,这3个类都位于System.Net.Sockets命名空间中。利用TCP协议进行通信的两个应用程序是有主次之分的,一个称为服务器端程序,另一个称为客户端程序。本文主要讲解TcpClient和TcpListener。
  • 代码示例要求:局域网内一个服务端对多个客户端的tcp通讯,服务端监听展示目前上线的设备,同时需要时向某个设备发送一些指令,设备上线后每隔5秒进行一次心跳通讯,心跳数据为基本字符串信息。

TcpClient 类

  • TcpClient 类提供了一个更高级别的抽象,它封装了底层的 Socket 功能,使得TCP网络通信的编写更加简单和直观。TcpClient 主要用于客户端/服务器模式中的客户端部分。
属性及方法 说明
Available属性 获取已经从网络接收且可供读取的数据量
Client属性 获取或设置基础Socket
Connected属性 获取一个值,该值指示TepClient的基础Socket是否已连接到远程主机
RecieveBufferSize属性 获取或设置接收缓冲区的大小
RecieveTimeout属性 获取或设置在初始化一个读取操作后TcpClient等待接收数据的时间量
SendBufferSize属性 获取或设置发送缓冲区的大小
SendTimeout属性 获取或设置TcpClient等待发送操作成功完成的时间量
BeginConnect方法 开始一个对远程主机连接的异步请求
Close方法 释放此TcpClient实例,而不关闭基础连接
Connec方法 使用指定的主机名和端口号将客户端连接到TCP主机
EndConnect方法 异步接受传入的连接尝试
GetStream方法 返回用于发送和接收数据的NetworkStream

示例代码

public class YZTCPClient
{
    // 单例模式
    private static readonly YZTCPClient instance = new YZTCPClient();
    // TcpClient对象
    private TcpClient tcpClient;
    private string ipAddress;
    private int port;
    // Task取消令牌
    private CancellationTokenSource cts;
    // 读取长度
    private readonly int readLength = 1024;
    // 客户端用户名
    private readonly string userName = ConfigurationManager.AppSettings["UserName"];
    // 事件
    public event Action<string> EventReceiveMessage;// 接收到服务端消息
    public event Action<bool> EventConnectState;// 连接状态
    private YZTCPClient()
    {
        tcpClient = new TcpClient();
    }

    public static YZTCPClient GetInstance()
    {
        return instance;
    }
    public void Connect(string ipAddress, int port)
    {
        this.ipAddress = ipAddress;
        this.port = port;
        if (tcpClient.Connected)
        {
            Debug.WriteLine($"Already connected to server:{tcpClient.Connected}");
            EventConnectState?.Invoke(tcpClient.Connected);
            return;
        }
        Debug.WriteLine("Begin connect to server");
        Task.Factory.StartNew(() =>
        {
            while (!tcpClient.Connected)
            {
                try
                {
                    // 客户端连接
                    tcpClient.Connect(ipAddress, port);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Failed to connect to server:{0}", ex.Message);
                    EventConnectState?.Invoke(tcpClient.Connected);
                }
                finally
                {
                    Thread.Sleep(1000);// 连接失败后停止1秒
                }
            }
            // todo:三次握手处理
            EventConnectState?.Invoke(tcpClient.Connected);
            Debug.WriteLine("Successed to connect to server:{0}", tcpClient.Connected);
            try
            {
                cts = new CancellationTokenSource();
                Task.Factory.StartNew(() =>
                {
                    // 开始心跳
                    SendHeartBeat();
                }, cts.Token);

                Task.Factory.StartNew(() =>
                {
                    // 处理收到的服务端消息
                    ListenForMessage();
                }, cts.Token);
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Task.Factory.StartNew Fail:{0}", ex.Message);
                throw ex;
            }
        });
    }

    private void ListenForMessage()
    {
        try
        {
            NetworkStream stream = tcpClient.GetStream();
            while (!cts.Token.IsCancellationRequested)
            {
                byte[] bytes = new byte[4096];
                try
                {
                    int bytesRead = stream.Read(bytes, 0, readLength);
                    if (bytesRead == 0)
                    {
                        break;
                    }
                    string message = "";
                    try
                    {
                        message = Encoding.UTF8.GetString(bytes, 0, bytesRead);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"GetString Fail:{ex.Message}");
                    }
                    Console.WriteLine($"Read Message:{message}");
                    EventReceiveMessage?.Invoke(message);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"NetworkStream Read Fail:{ex.Message}");
                    if (!tcpClient.Connected)
                    {
                        Debug.WriteLine("Server disconnect");
                        EventConnectState?.Invoke(tcpClient.Connected);
                        // 释放资源
                        cts?.Cancel();
                        tcpClient?.Close();
                        stream.Close();
                        tcpClient = null;
                        stream = null;
                        tcpClient = new TcpClient();
                        // 重新开启连接
                        Connect(this.ipAddress,this.port);
                    }

                }
            }
            //stream.Close();
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"GetStream Fail:{ex.Message}");
        }

    }

    public void WriteMessage(byte[] data)
    {
        // todo: 发送数据
    }
    // 发送心跳
    private void SendHeartBeat()
    {
        // 组装心跳信息
        string message = AssembleHeartBeat();

        // 获取一个网络流对象
        try
        {
            NetworkStream stream = tcpClient.GetStream();
            while (!cts.Token.IsCancellationRequested)
            {
                try
                {
                    byte[] data = Encoding.UTF8.GetBytes(message);
                    //byte[] data = Encoding.ASCII.GetBytes(message);
                    try
                    {
                        Debug.WriteLine("Send HeartBeat");
                        stream.Write(data, 0, data.Length);
                        Thread.Sleep(5000);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"NetworkStream Write Fail: {ex.Message}");
                        if (!tcpClient.Connected)
                        {
                            Debug.WriteLine("Server disconnect");
                            //EventConnectState?.Invoke(tcpClient.Connected);
                            //cts?.Cancel();
                        }
                        break;
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"GetBytes Fail:{ex.Message}");
                    break;// 如果发现GetBytes失败,还是建议退出循环
                }

            }
            //stream.Flush();
            //stream.Close();
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"GetStream Fail:{ex.Message}");
        }
    }

    // 组装心跳数据
    private string AssembleHeartBeat()
    {
        // todo:定义心跳数据,后期有时间可以使用二进制定协议
        string message = ":";
        try
        {
            var hostName = Dns.GetHostName();
            // 设备名(主机名)
            //message = hostName;
            message = userName;
            var ipAddresses = Dns.GetHostAddresses(hostName);
            foreach (var ipAddress in ipAddresses)
            {
                if (ipAddress.AddressFamily == AddressFamily.InterNetwork)
                {
                    // ipv4协议地址
                    var localIp = ipAddress.ToString();
                    message += ":" + localIp;
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"AssembleHeartBeat Fail:{ex.Message}");
        }
        return message;
    }

    // 停止连接
    public void Disconnect()
    {
        try
        {
            cts?.Cancel();
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"Disconnect Fail:{ex.Message}");
            throw ex;
        }
        //tcpClient?.Close();
    }
}

TcpListener类

  • 与TcpClient相似,TcpListener 主要用于客户端/服务器模式中的服务端部分。
属性及方法 说明
LocalEndpoint属性 获取当前TcpListener的基础EndPoint
Server属性 获取基础网络Socket
AcceptSocket/AcceptTcpClient方法 接受挂起的连接请求
BeginAcceptSocket/BeginAcceptTcpClient方法 开始一个异步操作来接受一个传入的连接尝试
EndAcceptSocket方法 异步接受传入的连接尝试,并创建新的Socket来处理远程主机通信
EndAcceptTcpClient方法 异步接受传入的连接尝试,并创建新的TcpClient来处理远程主机通信
Start方法 开始侦听传入的连接请求
Stop方法 关闭侦听器

示例代码

 public class YZTCPServer
 {
     // 单例模式
     private static readonly YZTCPServer instance = new YZTCPServer();
     private readonly string ipAddress = ConfigurationManager.AppSettings["IPAddress"];
     private readonly int port = int.Parse(ConfigurationManager.AppSettings["Port"]);
     private YZTCPServer()
     {
         try
         {
             tcpListener = new TcpListener(IPAddress.Parse(ipAddress), port);
         }
         catch (Exception ex)
         {
             Debug.WriteLine($"YZTCPServer Init Fail:{ex.Message}");
             throw ex;
         }

         // 监听客户端列表
         tcpClients.CollectionChanged += (sender, e) =>
         {
             Debug.WriteLine($"tcpclients change:{tcpClients.Count()}");

             List<ClientInfo> list = tcpClients.Cast<ClientInfo>().ToList();
             EventClients?.Invoke(list);
         };
     }
     public static YZTCPServer GetInstance()
     {
         return instance;
     }

     private readonly TcpListener tcpListener;
     // Task取消令牌
     private CancellationTokenSource cts;
     // 读取长度
     private readonly int readLength = 1024;
     // 客户端列表
     //private readonly ObservableCollection<TcpClient> tcpClients = new ObservableCollection<TcpClient>();
     private readonly ObservableCollection<ClientInfo> tcpClients = new ObservableCollection<ClientInfo>();
     // 线程锁
     private static readonly object lockObj = new object();
     // 事件
     public event Action<List<ClientInfo>> EventClients;
     // 构造函数
     //public YZTCPServer(string ipAddress, int port)
     //{
     //    try
     //    {
     //        tcpListener = new TcpListener(IPAddress.Parse(ipAddress), port);
     //    }
     //    catch (Exception ex)
     //    {
     //        Debug.WriteLine($"YZTCPServer Init Fail:{ex.Message}");
     //        throw ex;
     //    }

     //    // 监听客户端列表
     //    tcpClients.CollectionChanged += (sender, e) =>
     //    {
     //        Debug.WriteLine($"tcpclients change:{tcpClients.Count()}");

     //        List<ClientInfo> list = tcpClients.Cast<ClientInfo>().ToList();
     //        EventClients?.Invoke(list);
     //    };
     //}

     // 开启服务
     public void StartServer()
     {
         cts = new CancellationTokenSource();
         try
         {
             Task.Factory.StartNew(ListenForClients, cts.Token);
         }
         catch (Exception ex)
         {
             Debug.WriteLine($"StartServer Fail:{ex.Message}");
             throw ex;
         }
     }
     // 监听客户端
     private void ListenForClients()
     {
         try
         {
             tcpListener.Start();
             while (!cts.Token.IsCancellationRequested)
             {
                 Debug.WriteLine("Waiting for a new client connection");
                 try
                 {
                     TcpClient client = tcpListener.AcceptTcpClient();//AcceptTcpClient 是一个阻塞调用,它会等待直到有新的客户端连接
                     Debug.WriteLine("There is a client connection");
                     if (client != null)
                     {
                         ClientInfo info = new ClientInfo()
                         {
                             Client = client,
                         };
                         tcpClients.Add(info);
                         //tcpClients.Add(client);
                         try
                         {
                             Task.Factory.StartNew(() => HandleClientComm(client), cts.Token);
                         }
                         catch (Exception ex)
                         {
                             CloseClient(client);
                             Debug.WriteLine($"Task.Factory.StartNew HandleClientComm Fail:{ex.Message}");
                         }
                     }
                 }
                 catch (Exception ex)
                 {
                     Debug.WriteLine($"AcceptTcpClient Fail:{ex.Message}");
                 }
             }
             //tcpListener.Stop();这里永远不会执行,因为AcceptTcpClient阻塞了while循环
         }
         catch (Exception ex)
         {
             Debug.WriteLine($"TcpListener Start Fail:{ex.Message}");
         }
     }

     // 处理客户端消息
     private void HandleClientComm(TcpClient client)
     {
         try
         {
             // 获取网络流对象
             NetworkStream stream = client.GetStream();
             while (!cts.Token.IsCancellationRequested)
             {
                 // 读取数据
                 byte[] bytes = new byte[readLength];
                 //byte[] bytes = new byte[client.ReceiveBufferSize];
                 try
                 {
                     int bytesRead = stream.Read(bytes, 0, bytes.Length);
                     if (bytesRead == 0)
                     {
                         break;
                     }
                     // todo:解包问题
                     // todo: 判断数据类型(心跳或别的消息)
                     string message = "";
                     try
                     {
                         message = Encoding.UTF8.GetString(bytes, 0, bytesRead);
                         //string message = Encoding.ASCII.GetString(bytes, 0, bytesRead);
                     }
                     catch (Exception ex)
                     {
                         Debug.WriteLine($"GetString Fail:{ex.Message}");
                     }
                     Console.WriteLine($"Read Message:{message}");
                     try
                     {
                         lock (lockObj)
                         {
                             ClientInfo item = tcpClients.FirstOrDefault(p => p.Client == client);
                             if (item != null)
                             {
                                 int itemIndex = tcpClients.IndexOf(item);
                                 string[] fruits = message.Split(':');
                                 if (fruits.Length > 1)
                                 {
                                     item.Name = fruits[0];
                                     item.IPAddress = fruits[1];
                                 }
                                 tcpClients[itemIndex] = item;
                             }
                         }
                     }
                     catch (Exception ex)
                     {
                         Debug.WriteLine($"FirstOrDefault Fail:{ex.Message}");
                     }

                 }
                 catch (Exception ex)
                 {
                     //在TCP/IP协议中,并没有直接的方法来检查连接的状态(如“已连接”、“已关闭”等)。
                     //连接状态是通过TCP协议栈的状态机来管理的,并且对于应用程序来说,通常只能通过尝试读写操作或接收异常来间接地了解连接的状态。
                     Debug.WriteLine($"NetworkStream Read Fail:{ex.Message}");
                     break;
                 }

             }
             stream.Close();
             CloseClient(client);
             Debug.WriteLine("Client Close");
         }
         catch (Exception ex)
         {
             CloseClient(client);
             Debug.WriteLine($"GetStream Fail:{ex.Message}");
         }
     }
     // 关闭客户端连接,从客户端列表删除
     private void CloseClient(TcpClient client)
     {
         client.Close();
         //tcpClients.Remove(client);
         try
         {
             ClientInfo item = tcpClients.FirstOrDefault(p => p.Client == client);
             lock (lockObj)
             {
                 tcpClients.Remove(item);
             }
         }
         catch (Exception ex)
         {
             Debug.WriteLine($"FirstOrDefault Fail:{ex.Message}");
         }

     }
     // 结束服务
     public void StopServer()
     {
         try
         {
             cts?.Cancel();
         }
         catch (Exception ex)
         {
             Debug.WriteLine($"Disconnect Fail:{ex.Message}");
             throw ex;
         }
         //tcpListener.Stop();
         //tcpClients.Clear();
     }
 }

 public class ClientInfo
 {
     public TcpClient Client { get; set; }
     // 客户端名字
     public string Name { get; set; }
     // 客户端ip地址
     public string IPAddress { get; set; }
 }

客户端使用示例:

        private readonly string ipAddress = ConfigurationManager.AppSettings["IPAddress"];
        private readonly int port = int.Parse(ConfigurationManager.AppSettings["Port"]);
        public HomeWin()
        {
            InitializeComponent();
            // 开启连接服务端
            YZTCPClient.GetInstance().Connect(ipAddress, port);
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            YZTCPClient.GetInstance().EventReceiveMessage += Client_EventReceiveMessage;
        }

        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            YZTCPClient.GetInstance().EventReceiveMessage -= Client_EventReceiveMessage;
        }
        private void Client_EventReceiveMessage(string obj)
         {
             Debug.WriteLine("处理服务端的消息:{0}", obj);
            // 回到主线程
            Application.Current.Dispatcher.Invoke(new Action(() =>
                 {
                     // 解析数据
                 }));
        }

服务器端使用示例:

        private List<ClientInfo> clientInfos = new List<ClientInfo>();
        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("UserControl_Unloaded");
            YZTCPServer.GetInstance().EventClients -= Server_EventClients;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("UserControl_Loaded");
            // 开启tcp监听
            YZTCPServer.GetInstance().StartServer();
            YZTCPServer.GetInstance().EventClients += Server_EventClients;
        }
        private void Server_EventClients(List<ClientInfo> obj)
        {
            //Dispatcher.Invoke(new Action(() =>
            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                clientInfos = obj;
                clientDG.ItemsSource = obj;
            }));
        }
//Common.SysParas.client:需要发送消息到客户端时使用,此处是全局变量保存了某个客户端实例
        private void bthPlayClient_Click(object sender, RoutedEventArgs e)
        {
            // 传递参数 让客户端执行播放
            string tcpParam = "执行命令";
            try
            {
                NetworkStream stream = Common.SysParas.client.GetStream();
                byte[] data = Encoding.UTF8.GetBytes(tcpParam);
                stream.Write(data, 0, data.Length);
                //消息已发送到客户端
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Send TCP Message Fail:{ex.Message}");
            }

相关文章

  • 通讯稿速成记

    通讯稿按内容分为:人物通讯、事件通讯、概貌通讯、工作通讯。 按形式分为:记事通讯、访问(专访)、小故事、集纳、巡礼...

  • 通讯

    通讯

  • 通讯。

    “鬼来电好恐怖怎样都挂不断……”“哈哈哈大概是鬼有保持通讯的能力吧。”“哈哈……——小心!”地震突如其来,刚刚欢笑...

  • 通讯。

    “鬼来电好恐怖怎样都挂不断……”“哈哈哈大概是鬼有保持通讯的能力吧。”“哈哈……——小心!”地震突如其来,刚刚欢笑...

  • 通讯

    Activity 相关的服务由 ActivityManagerService 这个系统服务提供。 每个应用内 Ac...

  • 通讯

    刚出生那几年,我还是见过传呼机的,不过那时候每家每户已经有了固定电话。 不过不到几年,传呼机就一下都不见了,拿在人...

  • 通讯

    虽然微信语音、视频通话很受人欢迎,但是传统的打电话还是占有非常重要的地位和较大的比例。 老人们闲时会学着发点语音给...

  • 通讯

    开源篇-WebSocket搭建UE5通信桥梁[https://zhuanlan.zhihu.com/p/54621...

  • 命题_第十四章_选择题

    计算机常用的通讯方式有() A.同步通讯B.异步通讯C.串行通讯D.并行通讯 解析:C;D

  • 【王兴国营养特训班】第5期第11课7组虞文施

    细胞通讯和分化、干细胞、细胞衰老和死亡、癌细胞 细胞通讯 细胞通讯的方式:直接通讯:间隙连接、膜表面分子接触通讯、...

网友评论

      本文标题:C#TCP通讯

      本文链接:https://www.haomeiwen.com/subject/jhcmhjtx.html