C#實現(xiàn)TCP客戶端和服務(wù)器的基本功能
效果展示
服務(wù)器端實現(xiàn)
首先,我們實現(xiàn)TCP服務(wù)器。以下是服務(wù)器端所需的類和代碼:
using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace TCPIP_Test { public class TcpIp_Server { Socket serverSocket; // 服務(wù)器端Socket,用于服務(wù)器與客戶端的通信 Socket clientSocket; // 客戶端Socket,用于與客戶端建立連接 TcpListener tcpListener; // 負(fù)責(zé)監(jiān)聽客戶端的連接請求 Thread tcpListenerThread; // 監(jiān)聽連接請求的線程 Dictionary<string, Socket> dicClientSockets = new Dictionary<string, Socket>(); // 存儲客戶端Socket的集合,以客戶端的IP/端口作為鍵 Dictionary<string, Thread> dicReceiveMsg = new Dictionary<string, Thread>(); // 存儲每個客戶端接收消息的線程集合 string _IP; // 服務(wù)器的IP地址 int _Port; // 服務(wù)器的端口 IPAddress _ipAddress; // 服務(wù)器的IPAddress對象 EndPoint _endPoint; // 服務(wù)器的端點,包括IP地址和端口 bool linked = false; // 標(biāo)識服務(wù)器是否已開始監(jiān)聽客戶端連接 bool unlinked = false; // 標(biāo)識服務(wù)器是否已關(guān)閉連接 // 構(gòu)造函數(shù),初始化服務(wù)器的IP和端口 public TcpIp_Server(string ip, int port) { serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 初始化Socket對象,使用TCP協(xié)議 _IP = ip; _Port = port; _ipAddress = IPAddress.Parse(ip); // 將IP地址字符串轉(zhuǎn)換為IPAddress對象 _endPoint = new IPEndPoint(_ipAddress, port); // 構(gòu)造服務(wù)器端點 } // 判斷服務(wù)器是否已綁定到指定的端口 private bool _IsBound; public bool IsBound { get { return _IsBound = serverSocket.IsBound; } } // 判斷服務(wù)器Socket是否已連接 private bool _Connected; public bool Connected { get { return _Connected = serverSocket.Connected; } } // 綁定服務(wù)器并開始監(jiān)聽客戶端連接 public bool Bind(ref string msg) { try { if (!linked) { tcpListener = new TcpListener(_ipAddress, _Port); // 創(chuàng)建TcpListener對象,綁定IP和端口 tcpListener.Start(); // 啟動監(jiān)聽 linked = true; // 標(biāo)記已開始監(jiān)聽 ThreadStart threadStart = new ThreadStart(ListenConnectRequest); // 創(chuàng)建一個委托,執(zhí)行監(jiān)聽連接請求的方法 tcpListenerThread = new Thread(threadStart); // 創(chuàng)建一個新線程來監(jiān)聽客戶端請求 tcpListenerThread.IsBackground = true; // 設(shè)置為后臺線程 tcpListenerThread.Start(); // 啟動監(jiān)聽線程 msg = $"[Info] 服務(wù)器{tcpListener.LocalEndpoint.ToString()}監(jiān)聽成功!"; // 返回監(jiān)聽成功的消息 return true; } else { msg = $"[Info] 已監(jiān)聽"; // 如果已經(jīng)開始監(jiān)聽,則返回已監(jiān)聽消息 return true; } } catch (Exception ex) { msg = $"[Err] 連接失?。⌒畔?{ex}"; // 如果發(fā)生異常,返回錯誤信息 return false; } } // 斷開與指定客戶端的連接 public bool DisConnectClient(string client, ref string msg) { try { if (dicClientSockets.ContainsKey(client)) { dicClientSockets[client].Close(); // 關(guān)閉與客戶端的連接 msg = $"[Info] 斷開客戶端{client}連接"; // 返回斷開成功的消息 return true; } else { msg = $"[Info] 客戶端{client}已斷開"; // 如果客戶端已斷開,返回已斷開消息 return true; } } catch (Exception ex) { msg = $"[Err] 斷開失?。⌒畔?{ex}"; // 如果發(fā)生異常,返回錯誤信息 return false; } } // 關(guān)閉服務(wù)器監(jiān)聽并斷開所有客戶端連接 public bool ShutDown(string[] clientList, ref string msg) { try { if (linked) { linked = false; // 標(biāo)記服務(wù)器停止監(jiān)聽 tcpListener.Stop(); // 停止TcpListener監(jiān)聽 for (int i = 0; i < clientList.Length; i++) { dicClientSockets[clientList[i]].Close(); // 關(guān)閉每個客戶端的連接 } msg = $"[Info] 服務(wù)器監(jiān)聽斷開"; // 返回關(guān)閉監(jiān)聽的消息 return true; } else { unlinked = true; // 標(biāo)記已斷開 msg = $"[Info] 已斷開"; // 返回已斷開消息 return true; } } catch (Exception ex) { tcpListener.Stop(); // 停止TcpListener unlinked = true; // 標(biāo)記已斷開 msg = $"[Info] 已斷開!!!"; // 返回關(guān)閉連接的消息 return false; } } // 向指定客戶端發(fā)送數(shù)據(jù) public bool SendToClient(string client, string cmd, ref string msg) { try { if (!string.IsNullOrEmpty(cmd)) { dicClientSockets[client].Send(Encoding.UTF8.GetBytes(cmd)); // 將命令數(shù)據(jù)發(fā)送給客戶端 msg = $"[Info] 發(fā)送{client}信息={cmd}"; // 返回發(fā)送成功的消息 return true; } else return false; // 如果命令為空,返回發(fā)送失敗 } catch (Exception ex) { msg = $"[Err] 發(fā)送信息失??!信息={ex}"; // 如果發(fā)送失敗,返回錯誤信息 return false; } } // 監(jiān)聽客戶端的連接請求 private void ListenConnectRequest() { while (linked) { try { Socket clientSocket = tcpListener.AcceptSocket(); // 等待并接受客戶端連接 ParameterizedThreadStart parameterizedThreadStart = new ParameterizedThreadStart(ReceiveData); // 創(chuàng)建接收數(shù)據(jù)的線程委托 Thread receiveMsgThread = new Thread(parameterizedThreadStart); // 創(chuàng)建一個線程來接收客戶端數(shù)據(jù) receiveMsgThread.IsBackground = true; // 設(shè)置為后臺線程 receiveMsgThread.Start(clientSocket); // 啟動線程并傳入客戶端Socket dicClientSockets.Add(clientSocket.RemoteEndPoint.ToString(), clientSocket); // 將客戶端Socket加入字典 dicReceiveMsg.Add(clientSocket.RemoteEndPoint.ToString(), receiveMsgThread); // 將接收消息線程加入字典 Form_Server._AddListEvent(clientSocket.RemoteEndPoint.ToString()); // 更新UI列表,顯示已連接的客戶端 } catch (Exception ex) { Form_Server._ShowServerLogEvent($"[Err] 監(jiān)聽失??!信息={ex}"); // 監(jiān)聽失敗時顯示錯誤日志 } } } // 接收客戶端發(fā)送的數(shù)據(jù) public void ReceiveData(Object obj) { Socket clientSocket = (Socket)obj; // 將傳入的參數(shù)轉(zhuǎn)換為Socket string str = clientSocket.RemoteEndPoint.ToString(); // 獲取客戶端的遠程端點(IP+端口) while (true) { try { byte[] buffer = new byte[1024 * 1024]; // 創(chuàng)建緩存區(qū),大小為1MB int length = clientSocket.Receive(buffer); // 接收客戶端數(shù)據(jù) string message = Encoding.UTF8.GetString(buffer, 0, length); // 將字節(jié)數(shù)組轉(zhuǎn)換為字符串 if (length != 0) { Form_Server._ShowServerLogEvent($"[Info] 接收客戶端{clientSocket.RemoteEndPoint}信息={message}"); // 顯示接收到的消息 } } catch (Exception) { Form_Server._RemoveListEvent(str); // 從UI列表中移除已斷開的客戶端 dicClientSockets.Remove(str); // 從客戶端字典中移除該客戶端的Socket dicReceiveMsg.Remove(str); // 從接收消息線程字典中移除該客戶端的線程 break; // 退出接收數(shù)據(jù)的循環(huán) } } } } }
代碼解析:
服務(wù)器初始化: 在構(gòu)造函數(shù)中,初始化了服務(wù)器的Socket、監(jiān)聽端口和IP地址。
綁定與監(jiān)聽: Bind() 方法會啟動一個監(jiān)聽線程,等待客戶端連接請求。
客戶端連接管理: 客戶端連接通過 ListenConnectRequest() 方法處理。每當(dāng)有客戶端連接時,創(chuàng)建一個新的線程來接收客戶端數(shù)據(jù)。
發(fā)送與接收數(shù)據(jù): SendToClient() 方法用于發(fā)送數(shù)據(jù)到指定客戶端,而 ReceiveData() 方法則接收客戶端發(fā)送的數(shù)據(jù)。
斷開與關(guān)閉: DisConnectClient() 和 ShutDown() 方法分別用于斷開客戶端連接和關(guān)閉服務(wù)器監(jiān)聽。
接下來是服務(wù)器界面的代碼:
using System; using System.Drawing; using System.Net; using System.Net.Sockets; using System.Windows.Forms; namespace TCPIP_Test { // 委托用于更新服務(wù)器日志 public delegate void ShowServerLogDelegate(string message); // 委托用于向客戶端列表中添加客戶端 public delegate void AddListDelegate(string value); // 委托用于從客戶端列表中移除客戶端 public delegate void RemoveListDelegate(string value); public partial class Form_Server : Form { // 靜態(tài)委托實例,用于更新日志、添加或移除客戶端 public static ShowServerLogDelegate _ShowServerLogEvent = null; public static AddListDelegate _AddListEvent = null; public static RemoveListDelegate _RemoveListEvent = null; // TCP服務(wù)器實例 TcpIp_Server tcpIp_Server; string ip; // 服務(wù)器IP地址 int port; // 服務(wù)器端口 string msg; // 用于存儲消息 Form_Client form_Client; // 客戶端窗體實例 String[] clientList; // 當(dāng)前連接的客戶端列表 // 構(gòu)造函數(shù),初始化窗體 public Form_Server() { InitializeComponent(); } // 窗體加載時執(zhí)行的操作 private void Form_Server_Load(object sender, EventArgs e) { // 訂閱日志、添加客戶端和移除客戶端事件 _ShowServerLogEvent += ShowServerLog; _AddListEvent += AddList; _RemoveListEvent += RemoveList; // 獲取本機的IP地址并添加到ComboBox控件 GetIP(); // 設(shè)置默認(rèn)端口為5000 port = int.Parse(textBox_Port.Text = "5000"); } // 獲取本機所有的IP地址,并將IPv4地址添加到IP地址選擇框 private void GetIP() { string name = Dns.GetHostName(); // 獲取主機名 IPAddress[] iPAddress = Dns.GetHostAddresses(name); // 獲取所有IP地址 foreach (IPAddress item in iPAddress) { // 只添加IPv4地址到ComboBox中 if (item.AddressFamily == AddressFamily.InterNetwork) comboBox_IP.Items.Add(item.ToString()); } // 設(shè)置默認(rèn)選擇為第一個IP地址 comboBox_IP.SelectedIndex = 0; } // 開始監(jiān)聽按鈕點擊事件 private void button_Listen_Click(object sender, EventArgs e) { // 檢查是否已輸入IP和端口 if (comboBox_IP.SelectedItem == null || textBox_Port.Text == "") { MessageBox.Show("請輸入IP和Port!"); return; } ip = comboBox_IP.SelectedItem.ToString(); // 獲取選擇的IP地址 port = int.Parse(textBox_Port.Text); // 獲取端口號 // 創(chuàng)建TCP服務(wù)器實例并初始化 tcpIp_Server = new TcpIp_Server(ip, port); _ShowServerLogEvent($"[Info] 服務(wù)器初始化完成"); // 啟動服務(wù)器監(jiān)聽 _ShowServerLogEvent($"[Info] 服務(wù)器開始監(jiān)聽..."); if(tcpIp_Server.Bind(ref msg)) button_Listen.BackColor = Color.YellowGreen; // 設(shè)置監(jiān)聽按鈕顏色為綠色 _ShowServerLogEvent($"{msg}"); // 顯示監(jiān)聽狀態(tài)消息 } // 停止監(jiān)聽按鈕點擊事件 private void Button_ShutDown_Click(object sender, EventArgs e) { // 停止服務(wù)器監(jiān)聽 _ShowServerLogEvent($"[Info] 服務(wù)器停止監(jiān)聽..."); tcpIp_Server.ShutDown(clientList, ref msg); listBox_Client.Items.Clear(); // 清空客戶端列表 label_ClientCount.Text = "0"; // 更新客戶端數(shù)量顯示 button_Listen.BackColor = Color.LightGray; // 恢復(fù)監(jiān)聽按鈕顏色 _ShowServerLogEvent($"{msg}"); // 顯示停止監(jiān)聽的消息 } // 單個客戶端發(fā)送消息按鈕點擊事件 private void Button_SendOnce_Click(object sender, EventArgs e) { // 檢查是否選擇了客戶端 if (listBox_Client.SelectedIndex == -1) { MessageBox.Show(new Form { TopMost = true }, $"請選擇客戶端!", "Error"); return; } // 獲取選擇的客戶端和消息內(nèi)容 _ShowServerLogEvent($"[Info] 服務(wù)器單發(fā)送信息..."); string client = listBox_Client.SelectedItem.ToString(); string cmd = textBox_Message.Text; // 向客戶端發(fā)送消息 tcpIp_Server.SendToClient(client, cmd, ref msg); _ShowServerLogEvent($"{msg}"); // 顯示發(fā)送狀態(tài)消息 } // 向所有客戶端發(fā)送消息按鈕點擊事件 private void button_SendAll_Click(object sender, EventArgs e) { _ShowServerLogEvent($"[Info] 服務(wù)器多發(fā)送信息..."); string cmd = textBox_Message.Text; // 獲取消息內(nèi)容 for (int i = 0; i < listBox_Client.Items.Count; i++) { // 向所有客戶端發(fā)送消息 tcpIp_Server.SendToClient(listBox_Client.Items[i].ToString(), cmd, ref msg); _ShowServerLogEvent($"{msg}"); // 顯示每次發(fā)送的消息 } } // 清空日志按鈕點擊事件 private void Button_Clear_Click(object sender, EventArgs e) { richTextBox_Log.Text = ""; // 清空日志框 } // 打開客戶端按鈕點擊事件(目前被注釋掉) private void button_OpenClient_Click(object sender, EventArgs e) { // 這里的代碼被注釋掉,表示當(dāng)前功能未啟用 //if (form_Client == null || form_Client.IsDisposed) { // form_Client = new Form_Client(); // form_Client.Show(); //} //else { // form_Client.TopMost = true; //} } // 服務(wù)器窗體關(guān)閉時的處理 private void Form_Server_FormClosing(object sender, FormClosingEventArgs e) { // 確保在窗體關(guān)閉時關(guān)閉服務(wù)器 if (tcpIp_Server != null) tcpIp_Server.ShutDown(clientList, ref msg); _ShowServerLogEvent($"{msg}"); // 顯示服務(wù)器關(guān)閉消息 _ShowServerLogEvent -= ShowServerLog; // 取消訂閱日志事件 } // 顯示服務(wù)器日志的方法(線程安全更新UI) private void ShowServerLog(string message) { if (InvokeRequired) { this.BeginInvoke(new Action(() => ShowServerLog(message))); // 在UI線程更新日志 } else { int maxLine = 100; // 設(shè)置日志最大行數(shù) if (this.richTextBox_Log.Lines.Length > maxLine) { // 如果日志行數(shù)超過最大行數(shù),刪除最舊的日志 this.richTextBox_Log.Text = richTextBox_Log.Text.Substring(richTextBox_Log.Lines[0].Length + 1); } // 在日志框中追加新的日志 richTextBox_Log.AppendText($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss(fff)")} {message}\r\n"); richTextBox_Log.SelectionStart = richTextBox_Log.Text.Length; richTextBox_Log.ScrollToCaret(); // 滾動到日志的最新位置 } } // 向客戶端列表中添加客戶端(線程安全操作) private void AddList(string value) { if (InvokeRequired) { this.BeginInvoke(new Action(() => AddList(value))); // 在UI線程更新客戶端列表 } else { listBox_Client.Items.Add(value); // 添加客戶端到列表 // 更新客戶端列表數(shù)組 clientList = new String[listBox_Client.Items.Count]; int i = 0; foreach (var item in listBox_Client.Items) { clientList[i] = item.ToString(); } label_ClientCount.Text = clientList.Length.ToString(); // 更新客戶端數(shù)量顯示 } } // 從客戶端列表中移除客戶端(線程安全操作) private void RemoveList(string value) { if (InvokeRequired) { this.BeginInvoke(new Action(() => RemoveList(value))); // 在UI線程更新客戶端列表 } else { listBox_Client.Items.Remove(value); // 從列表中移除客戶端 // 更新客戶端列表數(shù)組 int i = 0; foreach (var item in listBox_Client.Items) { clientList[i] = item.ToString(); } label_ClientCount.Text = clientList.Length.ToString(); // 更新客戶端數(shù)量顯示 } } } }
功能解析:
服務(wù)器配置與啟動:選擇IP地址和端口,啟動服務(wù)器監(jiān)聽客戶端連接。
客戶端管理:顯示連接的客戶端,支持單個或所有客戶端發(fā)送消息。
日志記錄:實時顯示服務(wù)器日志,記錄連接、消息發(fā)送等操作。
停止服務(wù)器:停止監(jiān)聽,清空客戶端列表,斷開連接。
客戶端實現(xiàn)
服務(wù)器端實現(xiàn)完成后,接下來我們來實現(xiàn)TCP客戶端。客戶端界面代碼與服務(wù)器端類似,主要使用以下類:
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace TCPIP_Test { // 定義TCP客戶端類 public class TcpIp_Client { // 客戶端Socket對象,用于與服務(wù)器建立TCP連接 Socket clientSocket; // 服務(wù)器的IP地址 string _IP; // 服務(wù)器的端口 int _Port; // 服務(wù)器的IPAddress對象 IPAddress _ipAddress; // 服務(wù)器的IPEndPoint對象,包含IP地址和端口 IPEndPoint _iPEndPoint; // 標(biāo)識連接狀態(tài) bool linked = false; // 標(biāo)識是否曾經(jīng)斷開連接 bool unlinked = false; // 構(gòu)造函數(shù),初始化IP地址和端口 public TcpIp_Client(string ip, int port) { clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 創(chuàng)建TCP連接的Socket對象 _IP = ip; // 設(shè)置IP地址 _Port = port; // 設(shè)置端口號 _ipAddress = IPAddress.Parse(ip); // 將字符串IP地址解析為IPAddress對象 _iPEndPoint = new IPEndPoint(_ipAddress, port); // 創(chuàng)建端點對象,包含IP地址和端口 } // 當(dāng)前連接狀態(tài) private bool _Connected; public bool Connected { get { return _Connected = clientSocket.Connected; } // 返回Socket的連接狀態(tài) } // 連接到服務(wù)器 public bool ConnectServer(ref string msg) { try { if (!linked) { // 如果沒有連接 if (unlinked) { // 如果曾經(jīng)斷開連接,重新創(chuàng)建Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } // 連接到服務(wù)器 clientSocket.Connect(_iPEndPoint); if (clientSocket.Connected) { // 如果連接成功 linked = true; // 設(shè)置連接狀態(tài)為已連接 msg = $"[Info] 服務(wù)器{clientSocket.LocalEndPoint.ToString()}連接成功!"; // 設(shè)置成功消息 ReceiveData(); // 調(diào)用方法開始接收數(shù)據(jù) return true; } else { // 如果連接失敗 msg = $"[Info] 客戶端連接失?。?; return false; } } else { // 如果已經(jīng)連接 msg = $"[Info] 已連接"; return true; } } catch (Exception ex) { msg = $"[Err] 連接失??!信息={ex}"; // 連接失敗時設(shè)置錯誤消息 return false; } } // 斷開與服務(wù)器的連接 public bool DisConnectServer(ref string msg) { try { if (linked) { // 如果已連接 linked = false; // 設(shè)置連接狀態(tài)為未連接 clientSocket.Disconnect(false); // 斷開連接并釋放資源 unlinked = true; // 標(biāo)記為已斷開 msg = $"[Info] 客戶端連接斷開"; return true; } else { // 如果已經(jīng)是斷開狀態(tài) msg = $"[Info] 已斷開"; return true; } } catch (Exception ex) { msg = $"[Err] 斷開失?。⌒畔?{ex}"; // 斷開連接失敗時設(shè)置錯誤消息 return false; } } // 向服務(wù)器發(fā)送消息 public bool SendToServer(string cmd, ref string msg) { try { if (clientSocket.Connected) { // 如果已連接 clientSocket.Send(Encoding.UTF8.GetBytes(cmd)); // 將命令轉(zhuǎn)換為字節(jié)并發(fā)送 msg = $"[Info] 發(fā)送{clientSocket.LocalEndPoint.ToString()}信息={cmd}"; // 設(shè)置發(fā)送成功的消息 return true; } else { // 如果未連接 MessageBox.Show(new Form { TopMost = true }, "未連接!", "Info", MessageBoxButtons.OK); // 彈出提示框 msg = $"未連接!"; return false; } } catch (Exception ex) { msg = $"[Err] 發(fā)送信息失?。⌒畔?{ex}"; // 發(fā)送失敗時設(shè)置錯誤消息 return false; } } // 接收來自服務(wù)器的數(shù)據(jù) private void ReceiveData() { string str = ""; // 存儲接收到的消息 Task.Run(() => { while (linked) { // 只要連接保持有效,就持續(xù)接收數(shù)據(jù) try { if (clientSocket.Connected) { Thread.Sleep(10); // 為了避免占用過多的CPU時間,稍微暫停一下 } else { continue; // 如果連接斷開,繼續(xù)檢查連接狀態(tài) } byte[] data = new byte[1024]; // 緩沖區(qū)用于接收數(shù)據(jù) int length = clientSocket.Receive(data); // 接收服務(wù)器發(fā)送的數(shù)據(jù) string message = Encoding.UTF8.GetString(data, 0, length); // 將接收到的字節(jié)數(shù)據(jù)轉(zhuǎn)換為字符串 if (message != "") { str = message; // 存儲接收到的消息 Form_Client._ShowClientLog($"[Info] 接收服務(wù)器{clientSocket.LocalEndPoint.ToString()}信息={message}"); // 顯示接收到的消息 } } catch (Exception ex) { Form_Client._ShowClientLog($"[Err] 接收服務(wù)器信息失?。⌒畔?{ex}"); // 如果接收失敗,顯示錯誤信息 } } }); } } }
代碼解析:
TcpIp_Client類:該類用于實現(xiàn)一個TCP客戶端,提供連接服務(wù)器、斷開連接、發(fā)送消息、接收消息等功能。
構(gòu)造函數(shù):接受服務(wù)器的IP地址和端口號,初始化Socket并創(chuàng)建連接端點(IPEndPoint)。
ConnectServer方法:建立與服務(wù)器的TCP連接。連接成功后,啟動接收數(shù)據(jù)的線程。
DisConnectServer方法:斷開與服務(wù)器的連接并釋放相關(guān)資源。
SendToServer方法:將給定的命令發(fā)送到服務(wù)器。
ReceiveData方法:接收來自服務(wù)器的消息,并在接收到數(shù)據(jù)時將其打印到客戶端界面或日志中。
關(guān)鍵技術(shù):
Socket編程:使用Socket類進行TCP連接的建立、數(shù)據(jù)的發(fā)送和接收。
多線程:ReceiveData方法通過Task.Run()啟動一個新的線程來異步接收數(shù)據(jù)。
消息編碼:使用UTF8對消息進行編碼和解碼。
客戶端界面的實現(xiàn)代碼如下:
using System; using System.Drawing; using System.Net; using System.Net.Sockets; using System.Windows.Forms; namespace TCPIP_Test { // 定義一個委托,用于顯示客戶端日志信息 public delegate void EventShowClientLog(string message); // 客戶端窗口類 public partial class Form_Client : Form { // 聲明委托實例,用于日志顯示 public static EventShowClientLog _ShowClientLog = null; // TCP客戶端實例 TcpIp_Client tcpIp_Client; // 存儲IP地址和端口 string ip; int port; string msg; // 服務(wù)器窗口實例 Form_Server form_Server; // 構(gòu)造函數(shù),初始化組件 public Form_Client() { InitializeComponent(); } // 窗體加載時的事件處理 private void Form1_Load(object sender, EventArgs e) { // 綁定委托到日志顯示方法 _ShowClientLog += ShowClientLog; // 獲取本機IP地址并填充到下拉框中 GetIP(); // 默認(rèn)端口5000 port = int.Parse(textBox_Port.Text = "5000"); } // 獲取本機IP地址的方法 private void GetIP() { // 獲取本機的計算機名 string name = Dns.GetHostName(); // 獲取所有IP地址 IPAddress[] iPAddress = Dns.GetHostAddresses(name); // 遍歷所有IP地址,只添加IPv4地址到下拉框中 foreach (IPAddress item in iPAddress) { if (item.AddressFamily == AddressFamily.InterNetwork) comboBox_IP.Items.Add(item.ToString()); } // 默認(rèn)選擇第一個IP地址 comboBox_IP.SelectedIndex = 0; } // 連接按鈕點擊事件處理 private void Button_Connect_Click(object sender, EventArgs e) { // 判斷是否選擇了IP地址和端口 if (comboBox_IP.SelectedItem == null || textBox_Port.Text == "") { MessageBox.Show("請輸入IP和Port!"); return; } // 獲取選擇的IP和端口 ip = comboBox_IP.SelectedItem.ToString(); port = int.Parse(textBox_Port.Text); // 創(chuàng)建TCP客戶端實例并嘗試連接到服務(wù)器 tcpIp_Client = new TcpIp_Client(ip, port); _ShowClientLog($"[Info] 客戶端初始化完成"); _ShowClientLog($"[Info] 客戶端開始連接..."); // 嘗試連接服務(wù)器 if (tcpIp_Client.ConnectServer(ref msg)) button_Connect.BackColor = Color.YellowGreen; // 連接成功后,改變按鈕顏色 _ShowClientLog($"{msg}"); } // 斷開連接按鈕點擊事件處理 private void Button_DisConnect_Click(object sender, EventArgs e) { _ShowClientLog($"[Info] 客戶端斷開連接..."); // 調(diào)用斷開連接方法 tcpIp_Client.DisConnectServer(ref msg); button_Connect.BackColor = Color.LightGray; // 斷開連接后,恢復(fù)按鈕顏色 _ShowClientLog($"{msg}"); } // 發(fā)送一次數(shù)據(jù)按鈕點擊事件處理 private void Button_SendOnce_Click(object sender, EventArgs e) { _ShowClientLog($"[Info] 客戶端發(fā)送信息..."); // 獲取輸入框中的信息 string cmd = textBox_Message.Text; // 發(fā)送數(shù)據(jù)到服務(wù)器 tcpIp_Client.SendToServer(cmd, ref msg); _ShowClientLog($"{msg}"); } // 顯示日志信息的方法 private void ShowClientLog(string message) { // 判斷是否需要在UI線程中更新 if (richTextBox_Log.InvokeRequired) { EventShowClientLog d = new EventShowClientLog(ShowClientLog); richTextBox_Log.Invoke(d, new object[] { message }); } else { // 最大顯示行數(shù)限制 int maxLine = 100; // 如果超過最大行數(shù),刪除最舊的日志行 if (this.richTextBox_Log.Lines.Length > maxLine) { this.richTextBox_Log.Text = richTextBox_Log.Text.Substring(richTextBox_Log.Lines[0].Length + 1); } // 添加日志到文本框,并顯示當(dāng)前時間戳 richTextBox_Log.AppendText($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss(fff)")} {message}\r\n"); // 滾動到文本框底部 richTextBox_Log.SelectionStart = richTextBox_Log.Text.Length; richTextBox_Log.ScrollToCaret(); } } // 清除日志按鈕點擊事件處理 private void Button_Clear_Click(object sender, EventArgs e) { // 清空日志顯示框 richTextBox_Log.Text = ""; } // 打開服務(wù)器窗口按鈕點擊事件處理 private void button_OpenServer_Click(object sender, EventArgs e) { // 如果服務(wù)器窗口不存在或已關(guān)閉,則新建一個服務(wù)器窗口 if (form_Server == null || form_Server.IsDisposed) { form_Server = new Form_Server(); form_Server.Show(); } else { // 如果服務(wù)器窗口已經(jīng)打開,確保它位于最前面 form_Server.TopMost = true; } } } }
代碼解析:
- 1.EventShowClientLog 委托 用于線程安全地更新客戶端日志。
- 2.Form1_Load() 加載時獲取本機IP地址并填充IP選擇框,設(shè)置默認(rèn)端口。
- 3.GetIP() 獲取并顯示本機所有IPv4地址。
- 4.Button_Connect_Click()連接服務(wù)器,成功后更新UI顯示連接信息。
- 5.Button_DisConnect_Click()斷開與服務(wù)器的連接,并顯示斷開信息。
- 6.Button_SendOnce_Click() 發(fā)送文本框內(nèi)容到服務(wù)器,并顯示發(fā)送結(jié)果。
- 7.ShowClientLog() 顯示日志信息,超出限制時自動刪除最舊日志。
- 8.Button_Clear_Click() 清空日志框內(nèi)容。
- 9.button_OpenServer_Click() 打開或顯示服務(wù)器窗口。
總結(jié)
本文介紹了如何使用C#實現(xiàn)TCP客戶端和服務(wù)器,并通過簡單的代碼示例展示了如何設(shè)置客戶端與服務(wù)器的通信。通過設(shè)置監(jiān)聽端口和獲取客戶端IP地址,您可以在客戶端和服務(wù)器之間建立連接并互相發(fā)送消息。通過進一步的優(yōu)化和擴展,您可以實現(xiàn)更為復(fù)雜的網(wǎng)絡(luò)通信功能。
以上就是C#實現(xiàn)TCP客戶端和服務(wù)器的基本功能的詳細內(nèi)容,更多關(guān)于C# TCP客戶端和服務(wù)器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中LinkedList<T>的存儲結(jié)構(gòu)詳解
這篇文章主要介紹了深度解析C#中LinkedList<T>的存儲結(jié)構(gòu),本文將從鏈表的基礎(chǔ)特性、C#中LinkedList的底層實現(xiàn)邏輯,.NET的不同版本對于Queue的不同實現(xiàn)方式的原因分析等幾個視角進行簡單的解讀,需要的朋友可以參考下2023-12-12