IT评测·应用市场-qidao123.com

标题: C#实现TCP客户端和服务器 [打印本页]

作者: 涛声依旧在    时间: 2025-1-2 00:39
标题: C#实现TCP客户端和服务器
本文将介绍怎样利用C#实现TCP客户端和服务器的基本功能,客户端与服务器可以相互发送消息。
效果展示


服务器端实现

起首,我们实现TCP服务器。以下是服务器端所需的类和代码:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. using System.Threading;
  7. namespace TCPIP_Test {
  8.     public class TcpIp_Server {
  9.         Socket serverSocket; // 服务器端Socket,用于服务器与客户端的通信
  10.         Socket clientSocket; // 客户端Socket,用于与客户端建立连接
  11.         TcpListener tcpListener; // 负责监听客户端的连接请求
  12.         Thread tcpListenerThread; // 监听连接请求的线程
  13.         Dictionary<string, Socket> dicClientSockets = new Dictionary<string, Socket>(); // 存储客户端Socket的集合,以客户端的IP/端口作为键
  14.         Dictionary<string, Thread> dicReceiveMsg = new Dictionary<string, Thread>(); // 存储每个客户端接收消息的线程集合
  15.         string _IP; // 服务器的IP地址
  16.         int _Port; // 服务器的端口
  17.         IPAddress _ipAddress; // 服务器的IPAddress对象
  18.         EndPoint _endPoint; // 服务器的端点,包括IP地址和端口
  19.         bool linked = false; // 标识服务器是否已开始监听客户端连接
  20.         bool unlinked = false; // 标识服务器是否已关闭连接
  21.         // 构造函数,初始化服务器的IP和端口
  22.         public TcpIp_Server(string ip, int port) {
  23.             serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 初始化Socket对象,使用TCP协议
  24.             _IP = ip;
  25.             _Port = port;
  26.             _ipAddress = IPAddress.Parse(ip); // 将IP地址字符串转换为IPAddress对象
  27.             _endPoint = new IPEndPoint(_ipAddress, port); // 构造服务器端点
  28.         }
  29.         // 判断服务器是否已绑定到指定的端口
  30.         private bool _IsBound;
  31.         public bool IsBound {
  32.             get { return _IsBound = serverSocket.IsBound; }
  33.         }
  34.         // 判断服务器Socket是否已连接
  35.         private bool _Connected;
  36.         public bool Connected {
  37.             get { return _Connected = serverSocket.Connected; }
  38.         }
  39.         // 绑定服务器并开始监听客户端连接
  40.         public bool Bind(ref string msg) {
  41.             try {
  42.                 if (!linked) {
  43.                     tcpListener = new TcpListener(_ipAddress, _Port); // 创建TcpListener对象,绑定IP和端口
  44.                     tcpListener.Start(); // 启动监听
  45.                     linked = true; // 标记已开始监听
  46.                     ThreadStart threadStart = new ThreadStart(ListenConnectRequest); // 创建一个委托,执行监听连接请求的方法
  47.                     tcpListenerThread = new Thread(threadStart); // 创建一个新线程来监听客户端请求
  48.                     tcpListenerThread.IsBackground = true; // 设置为后台线程
  49.                     tcpListenerThread.Start(); // 启动监听线程
  50.                     msg = $"[Info] 服务器{tcpListener.LocalEndpoint.ToString()}监听成功!"; // 返回监听成功的消息
  51.                     return true;
  52.                 }
  53.                 else {
  54.                     msg = $"[Info] 已监听"; // 如果已经开始监听,则返回已监听消息
  55.                     return true;
  56.                 }
  57.             }
  58.             catch (Exception ex) {
  59.                 msg = $"[Err] 连接失败!信息={ex}"; // 如果发生异常,返回错误信息
  60.                 return false;
  61.             }
  62.         }
  63.         // 断开与指定客户端的连接
  64.         public bool DisConnectClient(string client, ref string msg) {
  65.             try {
  66.                 if (dicClientSockets.ContainsKey(client)) {
  67.                     dicClientSockets[client].Close(); // 关闭与客户端的连接
  68.                     msg = $"[Info] 断开客户端{client}连接"; // 返回断开成功的消息
  69.                     return true;
  70.                 }
  71.                 else {
  72.                     msg = $"[Info] 客户端{client}已断开"; // 如果客户端已断开,返回已断开消息
  73.                     return true;
  74.                 }
  75.             }
  76.             catch (Exception ex) {
  77.                 msg = $"[Err] 断开失败!信息={ex}"; // 如果发生异常,返回错误信息
  78.                 return false;
  79.             }
  80.         }
  81.         // 关闭服务器监听并断开所有客户端连接
  82.         public bool ShutDown(string[] clientList, ref string msg) {
  83.             try {
  84.                 if (linked) {
  85.                     linked = false; // 标记服务器停止监听
  86.                     tcpListener.Stop(); // 停止TcpListener监听
  87.                     for (int i = 0; i < clientList.Length; i++) {
  88.                         dicClientSockets[clientList[i]].Close(); // 关闭每个客户端的连接
  89.                     }
  90.                     msg = $"[Info] 服务器监听断开"; // 返回关闭监听的消息
  91.                     return true;
  92.                 }
  93.                 else {
  94.                     unlinked = true; // 标记已断开
  95.                     msg = $"[Info] 已断开"; // 返回已断开消息
  96.                     return true;
  97.                 }
  98.             }
  99.             catch (Exception ex) {
  100.                 tcpListener.Stop(); // 停止TcpListener
  101.                 unlinked = true; // 标记已断开
  102.                 msg = $"[Info] 已断开!!!"; // 返回关闭连接的消息
  103.                 return false;
  104.             }
  105.         }
  106.         // 向指定客户端发送数据
  107.         public bool SendToClient(string client, string cmd, ref string msg) {
  108.             try {
  109.                 if (!string.IsNullOrEmpty(cmd)) {
  110.                     dicClientSockets[client].Send(Encoding.UTF8.GetBytes(cmd)); // 将命令数据发送给客户端
  111.                     msg = $"[Info] 发送{client}信息={cmd}"; // 返回发送成功的消息
  112.                     return true;
  113.                 }
  114.                 else
  115.                     return false; // 如果命令为空,返回发送失败
  116.             }
  117.             catch (Exception ex) {
  118.                 msg = $"[Err] 发送信息失败!信息={ex}"; // 如果发送失败,返回错误信息
  119.                 return false;
  120.             }
  121.         }
  122.         // 监听客户端的连接请求
  123.         private void ListenConnectRequest() {
  124.             while (linked) {
  125.                 try {
  126.                     Socket clientSocket = tcpListener.AcceptSocket(); // 等待并接受客户端连接
  127.                     ParameterizedThreadStart parameterizedThreadStart = new ParameterizedThreadStart(ReceiveData); // 创建接收数据的线程委托
  128.                     Thread receiveMsgThread = new Thread(parameterizedThreadStart); // 创建一个线程来接收客户端数据
  129.                     receiveMsgThread.IsBackground = true; // 设置为后台线程
  130.                     receiveMsgThread.Start(clientSocket); // 启动线程并传入客户端Socket
  131.                     dicClientSockets.Add(clientSocket.RemoteEndPoint.ToString(), clientSocket); // 将客户端Socket加入字典
  132.                     dicReceiveMsg.Add(clientSocket.RemoteEndPoint.ToString(), receiveMsgThread); // 将接收消息线程加入字典
  133.                     Form_Server._AddListEvent(clientSocket.RemoteEndPoint.ToString()); // 更新UI列表,显示已连接的客户端
  134.                 }
  135.                 catch (Exception ex) {
  136.                     Form_Server._ShowServerLogEvent($"[Err] 监听失败!信息={ex}"); // 监听失败时显示错误日志
  137.                 }
  138.             }
  139.         }
  140.         // 接收客户端发送的数据
  141.         public void ReceiveData(Object obj) {
  142.             Socket clientSocket = (Socket)obj; // 将传入的参数转换为Socket
  143.             string str = clientSocket.RemoteEndPoint.ToString(); // 获取客户端的远程端点(IP+端口)
  144.             while (true) {
  145.                 try {
  146.                     byte[] buffer = new byte[1024 * 1024]; // 创建缓存区,大小为1MB
  147.                     int length = clientSocket.Receive(buffer); // 接收客户端数据
  148.                     string message = Encoding.UTF8.GetString(buffer, 0, length); // 将字节数组转换为字符串
  149.                     if (length != 0) {
  150.                         Form_Server._ShowServerLogEvent($"[Info] 接收客户端{clientSocket.RemoteEndPoint}信息={message}"); // 显示接收到的消息
  151.                     }
  152.                 }
  153.                 catch (Exception) {
  154.                     Form_Server._RemoveListEvent(str); // 从UI列表中移除已断开的客户端
  155.                     dicClientSockets.Remove(str); // 从客户端字典中移除该客户端的Socket
  156.                     dicReceiveMsg.Remove(str); // 从接收消息线程字典中移除该客户端的线程
  157.                     break; // 退出接收数据的循环
  158.                 }
  159.             }
  160.         }
  161.     }
  162. }
复制代码
代码剖析:
服务器初始化: 在构造函数中,初始化了服务器的Socket、监听端口和IP地址。
绑定与监听: Bind() 方法会启动一个监听线程,等待客户端连接请求。
客户端连接受理: 客户端连接通过 ListenConnectRequest() 方法处理。每当有客户端连接时,创建一个新的线程来接收客户端数据。
发送与接收数据: SendToClient() 方法用于发送数据到指定客户端,而 ReceiveData() 方法则接收客户端发送的数据。
断开与关闭: DisConnectClient() 和 ShutDown() 方法分别用于断开客户端连接和关闭服务器监听。

接下来是服务器界面的代码:
  1. using System;
  2. using System.Drawing;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Windows.Forms;
  6. namespace TCPIP_Test {
  7.     // 委托用于更新服务器日志
  8.     public delegate void ShowServerLogDelegate(string message);
  9.     // 委托用于向客户端列表中添加客户端
  10.     public delegate void AddListDelegate(string value);
  11.     // 委托用于从客户端列表中移除客户端
  12.     public delegate void RemoveListDelegate(string value);
  13.    
  14.     public partial class Form_Server : Form {
  15.         // 静态委托实例,用于更新日志、添加或移除客户端
  16.         public static ShowServerLogDelegate _ShowServerLogEvent = null;
  17.         public static AddListDelegate _AddListEvent = null;
  18.         public static RemoveListDelegate _RemoveListEvent = null;
  19.         // TCP服务器实例
  20.         TcpIp_Server tcpIp_Server;
  21.         string ip;  // 服务器IP地址
  22.         int port;  // 服务器端口
  23.         string msg;  // 用于存储消息
  24.         Form_Client form_Client;  // 客户端窗体实例
  25.         String[] clientList;  // 当前连接的客户端列表
  26.         // 构造函数,初始化窗体
  27.         public Form_Server() {
  28.             InitializeComponent();
  29.         }
  30.         // 窗体加载时执行的操作
  31.         private void Form_Server_Load(object sender, EventArgs e) {
  32.             // 订阅日志、添加客户端和移除客户端事件
  33.             _ShowServerLogEvent += ShowServerLog;
  34.             _AddListEvent += AddList;
  35.             _RemoveListEvent += RemoveList;
  36.             // 获取本机的IP地址并添加到ComboBox控件
  37.             GetIP();
  38.             
  39.             // 设置默认端口为5000
  40.             port = int.Parse(textBox_Port.Text = "5000");
  41.         }
  42.         // 获取本机所有的IP地址,并将IPv4地址添加到IP地址选择框
  43.         private void GetIP() {
  44.             string name = Dns.GetHostName();  // 获取主机名
  45.             IPAddress[] iPAddress = Dns.GetHostAddresses(name);  // 获取所有IP地址
  46.             foreach (IPAddress item in iPAddress) {
  47.                 // 只添加IPv4地址到ComboBox中
  48.                 if (item.AddressFamily == AddressFamily.InterNetwork)
  49.                     comboBox_IP.Items.Add(item.ToString());
  50.             }
  51.             // 设置默认选择为第一个IP地址
  52.             comboBox_IP.SelectedIndex = 0;
  53.         }
  54.         // 开始监听按钮点击事件
  55.         private void button_Listen_Click(object sender, EventArgs e) {
  56.             // 检查是否已输入IP和端口
  57.             if (comboBox_IP.SelectedItem == null || textBox_Port.Text == "")
  58.             {
  59.                 MessageBox.Show("请输入IP和Port!");
  60.                 return;
  61.             }
  62.             
  63.             ip = comboBox_IP.SelectedItem.ToString();  // 获取选择的IP地址
  64.             port = int.Parse(textBox_Port.Text);  // 获取端口号
  65.             
  66.             // 创建TCP服务器实例并初始化
  67.             tcpIp_Server = new TcpIp_Server(ip, port);
  68.             _ShowServerLogEvent($"[Info] 服务器初始化完成");
  69.             // 启动服务器监听
  70.             _ShowServerLogEvent($"[Info] 服务器开始监听...");
  71.             if(tcpIp_Server.Bind(ref msg))
  72.                 button_Listen.BackColor = Color.YellowGreen;  // 设置监听按钮颜色为绿色
  73.             _ShowServerLogEvent($"{msg}");  // 显示监听状态消息
  74.         }
  75.         // 停止监听按钮点击事件
  76.         private void Button_ShutDown_Click(object sender, EventArgs e) {
  77.             // 停止服务器监听
  78.             _ShowServerLogEvent($"[Info] 服务器停止监听...");
  79.             tcpIp_Server.ShutDown(clientList, ref msg);
  80.             listBox_Client.Items.Clear();  // 清空客户端列表
  81.             label_ClientCount.Text = "0";  // 更新客户端数量显示
  82.             button_Listen.BackColor = Color.LightGray;  // 恢复监听按钮颜色
  83.             _ShowServerLogEvent($"{msg}");  // 显示停止监听的消息
  84.         }
  85.         // 单个客户端发送消息按钮点击事件
  86.         private void Button_SendOnce_Click(object sender, EventArgs e) {
  87.             // 检查是否选择了客户端
  88.             if (listBox_Client.SelectedIndex == -1) {
  89.                 MessageBox.Show(new Form { TopMost = true }, $"请选择客户端!", "Error");
  90.                 return;
  91.             }
  92.             // 获取选择的客户端和消息内容
  93.             _ShowServerLogEvent($"[Info] 服务器单发送信息...");
  94.             string client = listBox_Client.SelectedItem.ToString();
  95.             string cmd = textBox_Message.Text;
  96.             
  97.             // 向客户端发送消息
  98.             tcpIp_Server.SendToClient(client, cmd, ref msg);
  99.             _ShowServerLogEvent($"{msg}");  // 显示发送状态消息
  100.         }
  101.         // 向所有客户端发送消息按钮点击事件
  102.         private void button_SendAll_Click(object sender, EventArgs e) {
  103.             _ShowServerLogEvent($"[Info] 服务器多发送信息...");
  104.             string cmd = textBox_Message.Text;  // 获取消息内容
  105.             for (int i = 0; i < listBox_Client.Items.Count; i++) {
  106.                 // 向所有客户端发送消息
  107.                 tcpIp_Server.SendToClient(listBox_Client.Items[i].ToString(), cmd, ref msg);
  108.                 _ShowServerLogEvent($"{msg}");  // 显示每次发送的消息
  109.             }
  110.         }
  111.         // 清空日志按钮点击事件
  112.         private void Button_Clear_Click(object sender, EventArgs e) {
  113.             richTextBox_Log.Text = "";  // 清空日志框
  114.         }
  115.         // 打开客户端按钮点击事件(目前被注释掉)
  116.         private void button_OpenClient_Click(object sender, EventArgs e) {
  117.             // 这里的代码被注释掉,表示当前功能未启用
  118.             //if (form_Client == null || form_Client.IsDisposed) {
  119.             //    form_Client = new Form_Client();
  120.             //    form_Client.Show();
  121.             //}
  122.             //else {
  123.             //    form_Client.TopMost = true;
  124.             //}
  125.         }
  126.         // 服务器窗体关闭时的处理
  127.         private void Form_Server_FormClosing(object sender, FormClosingEventArgs e) {
  128.             // 确保在窗体关闭时关闭服务器
  129.             if (tcpIp_Server != null)
  130.                 tcpIp_Server.ShutDown(clientList, ref msg);
  131.             _ShowServerLogEvent($"{msg}");  // 显示服务器关闭消息
  132.             _ShowServerLogEvent -= ShowServerLog;  // 取消订阅日志事件
  133.         }
  134.         // 显示服务器日志的方法(线程安全更新UI)
  135.         private void ShowServerLog(string message) {
  136.             if (InvokeRequired) {
  137.                 this.BeginInvoke(new Action(() => ShowServerLog(message)));  // 在UI线程更新日志
  138.             }
  139.             else {
  140.                 int maxLine = 100;  // 设置日志最大行数
  141.                 if (this.richTextBox_Log.Lines.Length > maxLine) {
  142.                     // 如果日志行数超过最大行数,删除最旧的日志
  143.                     this.richTextBox_Log.Text = richTextBox_Log.Text.Substring(richTextBox_Log.Lines[0].Length + 1);
  144.                 }
  145.                 // 在日志框中追加新的日志
  146.                 richTextBox_Log.AppendText($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss(fff)")} {message}\r\n");
  147.                 richTextBox_Log.SelectionStart = richTextBox_Log.Text.Length;
  148.                 richTextBox_Log.ScrollToCaret();  // 滚动到日志的最新位置
  149.             }
  150.         }
  151.         // 向客户端列表中添加客户端(线程安全操作)
  152.         private void AddList(string value) {
  153.             if (InvokeRequired) {
  154.                 this.BeginInvoke(new Action(() => AddList(value)));  // 在UI线程更新客户端列表
  155.             }
  156.             else {
  157.                 listBox_Client.Items.Add(value);  // 添加客户端到列表
  158.                 // 更新客户端列表数组
  159.                 clientList = new String[listBox_Client.Items.Count];
  160.                 int i = 0;
  161.                 foreach (var item in listBox_Client.Items) {
  162.                     clientList[i] = item.ToString();
  163.                 }
  164.                 label_ClientCount.Text = clientList.Length.ToString();  // 更新客户端数量显示
  165.             }
  166.         }
  167.         // 从客户端列表中移除客户端(线程安全操作)
  168.         private void RemoveList(string value) {
  169.             if (InvokeRequired) {
  170.                 this.BeginInvoke(new Action(() => RemoveList(value)));  // 在UI线程更新客户端列表
  171.             }
  172.             else {
  173.                 listBox_Client.Items.Remove(value);  // 从列表中移除客户端
  174.                 // 更新客户端列表数组
  175.                 int i = 0;
  176.                 foreach (var item in listBox_Client.Items) {
  177.                     clientList[i] = item.ToString();
  178.                 }
  179.                 label_ClientCount.Text = clientList.Length.ToString();  // 更新客户端数量显示
  180.             }
  181.         }
  182.     }
  183. }
复制代码
功能剖析:
服务器配置与启动:选择IP地址和端口,启动服务器监听客户端连接。
客户端管理:显示连接的客户端,支持单个或全部客户端发送消息。
日志记载:实时显示服务器日志,记载连接、消息发送等操作。
制止服务器:制止监听,清空客户端列表,断开连接。

客户端实现

服务器端实现完成后,接下来我们来实现TCP客户端。客户端界面代码与服务器端类似,主要利用以下类:
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using System.Windows.Forms;
  8. namespace TCPIP_Test {
  9.     // 定义TCP客户端类
  10.     public class TcpIp_Client {
  11.         // 客户端Socket对象,用于与服务器建立TCP连接
  12.         Socket clientSocket;
  13.         // 服务器的IP地址
  14.         string _IP;
  15.         // 服务器的端口
  16.         int _Port;
  17.         // 服务器的IPAddress对象
  18.         IPAddress _ipAddress;
  19.         // 服务器的IPEndPoint对象,包含IP地址和端口
  20.         IPEndPoint _iPEndPoint;
  21.         // 标识连接状态
  22.         bool linked = false;
  23.         // 标识是否曾经断开连接
  24.         bool unlinked = false;
  25.         // 构造函数,初始化IP地址和端口
  26.         public TcpIp_Client(string ip, int port) {
  27.             clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 创建TCP连接的Socket对象
  28.             _IP = ip; // 设置IP地址
  29.             _Port = port; // 设置端口号
  30.             _ipAddress = IPAddress.Parse(ip); // 将字符串IP地址解析为IPAddress对象
  31.             _iPEndPoint = new IPEndPoint(_ipAddress, port); // 创建端点对象,包含IP地址和端口
  32.         }
  33.         // 当前连接状态
  34.         private bool _Connected;
  35.         public bool Connected {
  36.             get { return _Connected = clientSocket.Connected; } // 返回Socket的连接状态
  37.         }
  38.         // 连接到服务器
  39.         public bool ConnectServer(ref string msg) {
  40.             try {
  41.                 if (!linked) { // 如果没有连接
  42.                     if (unlinked) {
  43.                         // 如果曾经断开连接,重新创建Socket
  44.                         clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  45.                     }
  46.                     // 连接到服务器
  47.                     clientSocket.Connect(_iPEndPoint);
  48.                     if (clientSocket.Connected) { // 如果连接成功
  49.                         linked = true; // 设置连接状态为已连接
  50.                         msg = $"[Info] 服务器{clientSocket.LocalEndPoint.ToString()}连接成功!"; // 设置成功消息
  51.                         ReceiveData(); // 调用方法开始接收数据
  52.                         return true;
  53.                     } else { // 如果连接失败
  54.                         msg = $"[Info] 客户端连接失败!";
  55.                         return false;
  56.                     }
  57.                 } else { // 如果已经连接
  58.                     msg = $"[Info] 已连接";
  59.                     return true;
  60.                 }
  61.             }
  62.             catch (Exception ex) {
  63.                 msg = $"[Err] 连接失败!信息={ex}"; // 连接失败时设置错误消息
  64.                 return false;
  65.             }
  66.         }
  67.         // 断开与服务器的连接
  68.         public bool DisConnectServer(ref string msg) {
  69.             try {
  70.                 if (linked) { // 如果已连接
  71.                     linked = false; // 设置连接状态为未连接
  72.                     clientSocket.Disconnect(false); // 断开连接并释放资源
  73.                     unlinked = true; // 标记为已断开
  74.                     msg = $"[Info] 客户端连接断开";
  75.                     return true;
  76.                 } else { // 如果已经是断开状态
  77.                     msg = $"[Info] 已断开";
  78.                     return true;
  79.                 }
  80.             }
  81.             catch (Exception ex) {
  82.                 msg = $"[Err] 断开失败!信息={ex}"; // 断开连接失败时设置错误消息
  83.                 return false;
  84.             }
  85.         }
  86.         // 向服务器发送消息
  87.         public bool SendToServer(string cmd, ref string msg) {
  88.             try {
  89.                 if (clientSocket.Connected) { // 如果已连接
  90.                     clientSocket.Send(Encoding.UTF8.GetBytes(cmd)); // 将命令转换为字节并发送
  91.                     msg = $"[Info] 发送{clientSocket.LocalEndPoint.ToString()}信息={cmd}"; // 设置发送成功的消息
  92.                     return true;
  93.                 } else { // 如果未连接
  94.                     MessageBox.Show(new Form { TopMost = true }, "未连接!", "Info", MessageBoxButtons.OK); // 弹出提示框
  95.                     msg = $"未连接!";
  96.                     return false;
  97.                 }
  98.             }
  99.             catch (Exception ex) {
  100.                 msg = $"[Err] 发送信息失败!信息={ex}"; // 发送失败时设置错误消息
  101.                 return false;
  102.             }
  103.         }
  104.         // 接收来自服务器的数据
  105.         private void ReceiveData() {
  106.             string str = ""; // 存储接收到的消息
  107.             Task.Run(() => {
  108.                 while (linked) { // 只要连接保持有效,就持续接收数据
  109.                     try {
  110.                         if (clientSocket.Connected) {
  111.                             Thread.Sleep(10); // 为了避免占用过多的CPU时间,稍微暂停一下
  112.                         } else {
  113.                             continue; // 如果连接断开,继续检查连接状态
  114.                         }
  115.                         byte[] data = new byte[1024]; // 缓冲区用于接收数据
  116.                         int length = clientSocket.Receive(data); // 接收服务器发送的数据
  117.                         string message = Encoding.UTF8.GetString(data, 0, length); // 将接收到的字节数据转换为字符串
  118.                         if (message != "") {
  119.                             str = message; // 存储接收到的消息
  120.                             Form_Client._ShowClientLog($"[Info] 接收服务器{clientSocket.LocalEndPoint.ToString()}信息={message}"); // 显示接收到的消息
  121.                         }
  122.                     }
  123.                     catch (Exception ex) {
  124.                         Form_Client._ShowClientLog($"[Err] 接收服务器信息失败!信息={ex}"); // 如果接收失败,显示错误信息
  125.                     }
  126.                 }
  127.             });
  128.         }
  129.     }
  130. }
复制代码
代码剖析:
TcpIp_Client类:该类用于实现一个TCP客户端,提供连接服务器、断开连接、发送消息、接收消息等功能。
构造函数:担当服务器的IP地址和端标语,初始化Socket并创建连接端点(IPEndPoint)。
ConnectServer方法:建立与服务器的TCP连接。连接成功后,启动接收数据的线程。
DisConnectServer方法:断开与服务器的连接并释放相关资源。
SendToServer方法:将给定的下令发送到服务器。
ReceiveData方法:接收来自服务器的消息,并在接收到数据时将其打印到客户端界面或日志中。
关键技术:
Socket编程:利用Socket类进行TCP连接的建立、数据的发送和接收。
多线程:ReceiveData方法通过Task.Run()启动一个新的线程来异步接收数据。
消息编码:利用UTF8对消息进行编码息争码。

客户端界面的实今世码如下:
  1. using System;
  2. using System.Drawing;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Windows.Forms;
  6. namespace TCPIP_Test
  7. {
  8.     // 定义一个委托,用于显示客户端日志信息
  9.     public delegate void EventShowClientLog(string message);
  10.     // 客户端窗口类
  11.     public partial class Form_Client : Form
  12.     {
  13.         // 声明委托实例,用于日志显示
  14.         public static EventShowClientLog _ShowClientLog = null;
  15.         
  16.         // TCP客户端实例
  17.         TcpIp_Client tcpIp_Client;
  18.         
  19.         // 存储IP地址和端口
  20.         string ip;
  21.         int port;
  22.         string msg;
  23.         
  24.         // 服务器窗口实例
  25.         Form_Server form_Server;
  26.         // 构造函数,初始化组件
  27.         public Form_Client()
  28.         {
  29.             InitializeComponent();
  30.         }
  31.         // 窗体加载时的事件处理
  32.         private void Form1_Load(object sender, EventArgs e)
  33.         {
  34.             // 绑定委托到日志显示方法
  35.             _ShowClientLog += ShowClientLog;
  36.             // 获取本机IP地址并填充到下拉框中
  37.             GetIP();
  38.             // 默认端口5000
  39.             port = int.Parse(textBox_Port.Text = "5000");
  40.         }
  41.         // 获取本机IP地址的方法
  42.         private void GetIP()
  43.         {
  44.             // 获取本机的计算机名
  45.             string name = Dns.GetHostName();
  46.             // 获取所有IP地址
  47.             IPAddress[] iPAddress = Dns.GetHostAddresses(name);
  48.             // 遍历所有IP地址,只添加IPv4地址到下拉框中
  49.             foreach (IPAddress item in iPAddress)
  50.             {
  51.                 if (item.AddressFamily == AddressFamily.InterNetwork)
  52.                     comboBox_IP.Items.Add(item.ToString());
  53.             }
  54.             // 默认选择第一个IP地址
  55.             comboBox_IP.SelectedIndex = 0;
  56.         }
  57.         // 连接按钮点击事件处理
  58.         private void Button_Connect_Click(object sender, EventArgs e)
  59.         {
  60.             // 判断是否选择了IP地址和端口
  61.             if (comboBox_IP.SelectedItem == null || textBox_Port.Text == "")
  62.             {
  63.                 MessageBox.Show("请输入IP和Port!");
  64.                 return;
  65.             }
  66.             // 获取选择的IP和端口
  67.             ip = comboBox_IP.SelectedItem.ToString();
  68.             port = int.Parse(textBox_Port.Text);
  69.             // 创建TCP客户端实例并尝试连接到服务器
  70.             tcpIp_Client = new TcpIp_Client(ip, port);
  71.             _ShowClientLog($"[Info] 客户端初始化完成");
  72.             _ShowClientLog($"[Info] 客户端开始连接...");
  73.             // 尝试连接服务器
  74.             if (tcpIp_Client.ConnectServer(ref msg))
  75.                 button_Connect.BackColor = Color.YellowGreen; // 连接成功后,改变按钮颜色
  76.             _ShowClientLog($"{msg}");
  77.         }
  78.         // 断开连接按钮点击事件处理
  79.         private void Button_DisConnect_Click(object sender, EventArgs e)
  80.         {
  81.             _ShowClientLog($"[Info] 客户端断开连接...");
  82.             
  83.             // 调用断开连接方法
  84.             tcpIp_Client.DisConnectServer(ref msg);
  85.             
  86.             button_Connect.BackColor = Color.LightGray; // 断开连接后,恢复按钮颜色
  87.             _ShowClientLog($"{msg}");
  88.         }
  89.         // 发送一次数据按钮点击事件处理
  90.         private void Button_SendOnce_Click(object sender, EventArgs e)
  91.         {
  92.             _ShowClientLog($"[Info] 客户端发送信息...");
  93.             // 获取输入框中的信息
  94.             string cmd = textBox_Message.Text;
  95.             // 发送数据到服务器
  96.             tcpIp_Client.SendToServer(cmd, ref msg);
  97.             _ShowClientLog($"{msg}");
  98.         }
  99.         // 显示日志信息的方法
  100.         private void ShowClientLog(string message)
  101.         {
  102.             // 判断是否需要在UI线程中更新
  103.             if (richTextBox_Log.InvokeRequired)
  104.             {
  105.                 EventShowClientLog d = new EventShowClientLog(ShowClientLog);
  106.                 richTextBox_Log.Invoke(d, new object[] { message });
  107.             }
  108.             else
  109.             {
  110.                 // 最大显示行数限制
  111.                 int maxLine = 100;
  112.                 // 如果超过最大行数,删除最旧的日志行
  113.                 if (this.richTextBox_Log.Lines.Length > maxLine)
  114.                 {
  115.                     this.richTextBox_Log.Text = richTextBox_Log.Text.Substring(richTextBox_Log.Lines[0].Length + 1);
  116.                 }
  117.                 // 添加日志到文本框,并显示当前时间戳
  118.                 richTextBox_Log.AppendText($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss(fff)")} {message}\r\n");
  119.                 // 滚动到文本框底部
  120.                 richTextBox_Log.SelectionStart = richTextBox_Log.Text.Length;
  121.                 richTextBox_Log.ScrollToCaret();
  122.             }
  123.         }
  124.         // 清除日志按钮点击事件处理
  125.         private void Button_Clear_Click(object sender, EventArgs e)
  126.         {
  127.             // 清空日志显示框
  128.             richTextBox_Log.Text = "";
  129.         }
  130.         // 打开服务器窗口按钮点击事件处理
  131.         private void button_OpenServer_Click(object sender, EventArgs e)
  132.         {
  133.             // 如果服务器窗口不存在或已关闭,则新建一个服务器窗口
  134.             if (form_Server == null || form_Server.IsDisposed)
  135.             {
  136.                 form_Server = new Form_Server();
  137.                 form_Server.Show();
  138.             }
  139.             else
  140.             {
  141.                 // 如果服务器窗口已经打开,确保它位于最前面
  142.                 form_Server.TopMost = true;
  143.             }
  144.         }
  145.     }
  146. }
复制代码
代码剖析:
1.EventShowClientLog 委托   用于线程安全地更新客户端日志。
2.Form1_Load()   加载时获取本机IP地址并填充IP选择框,设置默认端口。
3.GetIP()   获取并显示本机全部IPv4地址。
4.Button_Connect_Click()连接服务器,成功后更新UI显示连接信息。
5.Button_DisConnect_Click()断开与服务器的连接,并显示断开信息。
6.Button_SendOnce_Click()   发送文本框内容到服务器,并显示发送结果。
7.ShowClientLog()   显示日志信息,超出限定时自动删除最旧日志。
8.Button_Clear_Click()  清空日志框内容。
9.button_OpenServer_Click()   打开或显示服务器窗口。

总结

本文介绍了怎样利用C#实现TCP客户端和服务器,并通过简朴的代码示例展示了怎样设置客户端与服务器的通信。通过设置监听端口和获取客户端IP地址,您可以在客户端和服务器之间建立连接并相互发送消息。通过进一步的优化和扩展,您可以实现更为复杂的网络通信功能。

源码地址:https://download.csdn.net/download/weixin_44643352/90058182

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4