美文网首页
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}");
                }
    

    相关文章

      网友评论

          本文标题:C#TCP通讯

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