编辑
2025-09-27
C#
00

目录

什么是 TcpListener?
TcpListener 常用方法
方法
界面
代码示例
总结

在这篇文章中,我们将探讨如何使用 TcpListener 在 C# 中实现一个简单的多用户服务器。通过一个实际例子,我们会了解 TcpListener 的常用属性和方法,并且我们将创建一个基本的服务器应用,可以监听客户端连接和处理客户端消息。

什么是 TcpListener?

TcpListener 是 .NET 提供的一个用于监听 TCP 网络连接的类。它能侦听传入的客户端连接请求,并与这些客户端通信。

TcpListener 常用方法

方法

  • Start(): 启动 TcpListener 以准备侦听传入的连接请求。
  • Stop(): 关闭 TcpListener
  • AcceptTcpClient(): 阻塞并等待传入连接尝试。
  • AcceptTcpClientAsync(): 异步等待并接受一个传入的连接尝试。
  • BeginAcceptTcpClient(): 异步地开始传入连接尝试的操作,并选择回调方法来处理它。
  • EndAcceptTcpClient(): 结束异步客户端连接尝试。

界面

image.png

代码示例

以下代码展示了如何使用 TcpListener 创建并运行一个简单的服务器应用。这个服务器能够接受多个客户端的连接,并与这些客户端进行消息交换。

C#
using System.Collections.Concurrent; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.Json; using System.Windows.Forms; namespace TcpServer { public partial class FrmMain : Form { private TcpListener tcpListener; private bool isServerRunning = false; private ConcurrentDictionary<string, TcpClient> clients = new ConcurrentDictionary<string, TcpClient>(); public FrmMain() { InitializeComponent(); } private async void btnStart_Click(object sender, EventArgs e) { // 检查服务器是否已经在运行 if (!isServerRunning) { try { // 定义要监听的端口号,确保与客户端尝试连接的端口匹配 int port = 3001; // 使用IPAddress.Any,服务器将监听所有网络接口上的客户端活动 IPAddress localAddr = IPAddress.Any; // 初始化TcpListener来监听上述IP地址和端口 tcpListener = new TcpListener(localAddr, port); // 启动TcpListener开始监听 tcpListener.Start(); // 标记服务器状态为运行中 isServerRunning = true; // 日志输出,提示服务器已启动 AppendTextToLog("Server started..."); // 服务器启动后禁用启动按钮,防止重复启动 btnStart.Enabled = false; // 异步接受客户端连接,此方法将持续运行,直到服务器停止 await AcceptClientsAsync(tcpListener); } catch (Exception ex) { // 如果在启动服务器过程中发生异常,显示错误消息 MessageBox.Show($"Exception occurred: {ex.Message}"); } } } private async Task AcceptClientsAsync(TcpListener listener) { // 持续监听并接受客户端,直到服务器停止运行 while (isServerRunning) { try { // 异步等待并接受一个连接尝试 TcpClient client = await listener.AcceptTcpClientAsync(); // 使用客户端的远程终结点作为唯一标识符 string clientKey = $"{client.Client.RemoteEndPoint}"; // 将新连接的客户端添加到客户端列表中 clients.TryAdd(clientKey, client); // 更新客户端列表显示(假设这是一个UI操作) UpdateClientList(); // 记录新客户端的连接 AppendTextToLog("Client connected: " + clientKey); // 异步处理该客户端的数据交换 HandleClientAsync(client, clientKey); } catch (OperationCanceledException) { // 操作被取消,可能是因为服务器正在关闭 AppendTextToLog("Server stopping: Accept operation canceled."); break; // 跳出循环 } catch (ObjectDisposedException) { // TcpListener已经被关闭 AppendTextToLog("Server stopping: Listener has been disposed."); break; // 跳出循环 } catch (Exception ex) { // 检查异常信息以确定是否是因为操作被取消 if (ex.Message.Contains("The I/O operation has been aborted because of either a thread exit or an application request")) { AppendTextToLog("Server stopping: Operation aborted."); break; // 由于服务器关闭导致的异常,跳出循环 } else { // 如果是其他类型的异常,记录异常信息 AppendTextToLog($"Exception occurred: {ex.Message}"); // 发生异常,将服务器运行状态标记为停止 isServerRunning = false; } } } } private async void HandleClientAsync(TcpClient client, string clientKey) { try { // 获取与客户端关联的网络流 using (NetworkStream stream = client.GetStream()) { // 分配一个缓冲区用于接收数据 byte[] buffer = new byte[1024]; int bytesRead; StringBuilder sb = new StringBuilder(); // 持续读取网络流中的数据,直到没有更多数据(即客户端关闭连接) while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0) { // 将接收到的字节数据转换为字符串 string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead); // 记录接收到的消息 AppendTextToLog($"Received: {receivedMessage}"); sb.Append(receivedMessage); if (receivedMessage.EndsWith("$$$")) { string json = sb.ToString().Substring(0, sb.ToString().Length - 3); sb.Append(json); MyTcpLib.Message message = Newtonsoft.Json.JsonConvert.DeserializeObject <MyTcpLib.Message>(json); if (message != null) { if (message.Type == "FILE") { // 将Base64字符串转换为字节数组 byte[] fileBytes = Convert.FromBase64String(message.Context); File.WriteAllBytes("d:\\" + message.FileName, fileBytes); } } sb=new StringBuilder(); } } } } catch (Exception ex) { // 处理过程中发生异常,记录异常信息 AppendTextToLog($"Exception occurred: {ex.Message}"); } finally { // 无论是否成功处理客户端,最终都尝试从客户端列表中移除该客户端 if (clients.TryRemove(clientKey, out _)) { // 更新客户端列表的显示 UpdateClientList(); } // 关闭客户端连接 client.Close(); } } private void AppendTextToLog(string message) { if (InvokeRequired) { Invoke(new Action(() => AppendTextToLog(message))); return; } txtLog.AppendText(message + Environment.NewLine); } private void UpdateClientList() { if (InvokeRequired) { Invoke(new Action(UpdateClientList)); return; } lstClient.Items.Clear(); foreach (var clientKey in clients.Keys) { lstClient.Items.Add(clientKey); } } private void btnStop_Click(object sender, EventArgs e) { if (isServerRunning && tcpListener != null) { try { // 停止TcpListener接受新的连接请求 tcpListener.Stop(); // 标记服务器状态为停止 isServerRunning = false; // 日志输出,提示服务器已停止 AppendTextToLog("Server stopped..."); // 重新启用启动按钮,允许用户重新启动服务器 btnStart.Enabled = true; } catch (Exception ex) { } } } private void cmnuMain_SendMsg_Click(object sender, EventArgs e) { try { var selectClient = clients[lstClient.SelectedItem.ToString()]; frmSendMsg frmSendMsg = new frmSendMsg(); frmSendMsg.SendMessage = (x) => { byte[] data = Encoding.UTF8.GetBytes(x); var stream = selectClient.GetStream(); stream.WriteAsync(data); }; frmSendMsg.ShowDialog(); } catch { } } private void lstClient_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { cmnuMain.Show(lstClient, new Point(e.X, e.Y)); } } } }

frmSendMsg

C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace TcpServer { public partial class frmSendMsg : Form { public Action<string> SendMessage; public frmSendMsg() { InitializeComponent(); } private void btnSendMsg_Click(object sender, EventArgs e) { if(SendMessage != null) { SendMessage(txtMsg.Text); } this.Close(); } } }

image.png

总结

使用 TcpListener 创建一个简单的多连接服务器是学习网络编程的良好起点。本篇文章中的示例展示了如何使用它的基本属性和方法,以及如何实现基本的客户端连接管理和消息处理。通过 TcpListener 和异步编程,我们可以构建高效且可扩展的网络应用。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!