C#實(shí)現(xiàn)TCP客戶端和服務(wù)器的基本功能
效果展示

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

