当前位置: 首页 > news >正文

C# TCP 服务端与客户端代码分析与补充

一、代码结构分析

1. 服务端代码特点

  • 使用 Socket 类实现 TCP 服务器功能

  • 支持多客户端连接(通过嵌套 Task 实现)

  • 自动获取本机 IPv4 地址

  • 采用异步方式接收客户端连接和数据,避免阻塞 UI 线程

  • 基本的异常处理机制

2. 客户端代码特点

  • 使用异步连接方式(ConnectAsync)避免 UI 卡顿

  • 提供连接状态显示和基本输入验证

  • 简单的消息发送功能

二、代码问题与改进点

1. 服务端存在的问题

  • 没有正确释放资源,可能导致内存泄漏

  • 接收缓冲区大小依赖 client.Available,可能导致数据不完整

  • 异常处理不完善,客户端断开连接可能导致服务端崩溃

  • 没有客户端管理机制,无法区分不同客户端

  • 缺少向客户端发送消息的功能

2. 客户端存在的问题

  • 没有接收服务端消息的功能

  • 关闭连接的功能不完善

  • 缺少断线重连机制

  • 没有消息历史记录功能

三、改进后的代码实现

服务端改进代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
​
namespace 服务端
{public partial class ServerFrm : Form{// 服务器Socket实例private Socket server = null;// 用于取消任务的令牌源private CancellationTokenSource ctsAcceptClients = null;// 存储所有连接的客户端private Dictionary<Socket, string> connectedClients = new Dictionary<Socket, string>();// 用于线程安全访问客户端字典private object clientLock = new object();
​public ServerFrm(){InitializeComponent();// 初始化UIthis.FormClosing += ServerFrm_FormClosing;}
​private void ServerFrm_Load(object sender, EventArgs e){try{// 1. 实例化服务器Socketserver = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
​// 2. 绑定IP和端口int port = 12345;string ipStr = GetLocalIPAddress();if (string.IsNullOrEmpty(ipStr)){ShowMessage("无法获取有效的IPv4地址");return;}
​IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ipStr), port);server.Bind(endPoint);
​// 3. 启动服务器监听server.Listen(100);ShowMessage($"服务器已启动,监听地址: {ipStr}:{port}");
​// 4. 开始接受客户端连接StartAcceptingClients();}catch (Exception ex){ShowMessage($"服务器启动失败: {ex.Message}");}}
​// 获取本地IPv4地址private string GetLocalIPAddress(){IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());foreach (IPAddress ip in host.AddressList){if (ip.AddressFamily == AddressFamily.InterNetwork){return ip.ToString();}}return null;}
​// 开始接受客户端连接private void StartAcceptingClients(){ctsAcceptClients = new CancellationTokenSource();Task.Run(() =>{try{while (!ctsAcceptClients.IsCancellationRequested){// 等待客户端连接Socket client = server.Accept();if (client.Connected){// 获取客户端信息string clientInfo = client.RemoteEndPoint.ToString();ShowMessage($"新客户端连接: {clientInfo}");// 保存客户端连接lock (clientLock){connectedClients[client] = clientInfo;UpdateClientList();}// 开始接收该客户端的消息StartReceivingMessages(client);}}}catch (Exception ex){if (!ctsAcceptClients.IsCancellationRequested){ShowMessage($"接受客户端连接出错: {ex.Message}");}}}, ctsAcceptClients.Token);}
​// 接收客户端消息private void StartReceivingMessages(Socket client){CancellationTokenSource cts = new CancellationTokenSource();Task.Run(() =>{byte[] buffer = new byte[1024]; // 使用固定大小缓冲区string clientInfo = connectedClients[client];try{while (!cts.IsCancellationRequested){// 等待接收数据int bytesRead = client.Receive(buffer);if (bytesRead == 0){// 客户端断开连接ShowMessage($"客户端断开连接: {clientInfo}");break;}// 转换为字符串string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);ShowMessage($"收到来自 {clientInfo} 的消息: {message}");}}catch (Exception ex){ShowMessage($"与客户端 {clientInfo} 通信出错: {ex.Message}");}finally{// 清理资源lock (clientLock){connectedClients.Remove(client);UpdateClientList();}try{if (client.Connected)client.Shutdown(SocketShutdown.Both);}catch { }client.Close();ShowMessage($"客户端连接已关闭: {clientInfo}");}}, cts.Token);}
​// 向客户端发送消息private void SendMessageToClient(Socket client, string message){if (client == null || !client.Connected)return;try{byte[] data = Encoding.UTF8.GetBytes(message);client.Send(data);ShowMessage($"已向 {connectedClients[client]} 发送消息: {message}");}catch (Exception ex){ShowMessage($"发送消息失败: {ex.Message}");}}
​// 关闭所有客户端连接private void CloseAllClients(){lock (clientLock){foreach (var client in connectedClients.Keys.ToList()){try{if (client.Connected)client.Shutdown(SocketShutdown.Both);client.Close();}catch { }}connectedClients.Clear();UpdateClientList();}}
​// 更新客户端列表(UI操作)private void UpdateClientList(){Invoke(new Action(() =>{// 假设你有一个ListBox控件名为lbClientslbClients.Items.Clear();foreach (var clientInfo in connectedClients.Values){lbClients.Items.Add(clientInfo);}}));}
​// 在富文本框中显示消息(线程安全)private void ShowMessage(string message){Invoke(new Action(() =>{richTextBox1.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\n");richTextBox1.ScrollToCaret(); // 滚动到最后一行}));}
​// 发送按钮点击事件private void btnSend_Click(object sender, EventArgs e){if (lbClients.SelectedItem == null){MessageBox.Show("请选择一个客户端");return;}string clientInfo = lbClients.SelectedItem.ToString();string message = textBoxSend.Text.Trim();if (string.IsNullOrEmpty(message)){MessageBox.Show("请输入要发送的消息");return;}// 查找对应的客户端Socketvar client = connectedClients.FirstOrDefault(c => c.Value == clientInfo).Key;if (client != null){SendMessageToClient(client, message);textBoxSend.Clear();}}
​// 关闭服务器private void ServerFrm_FormClosing(object sender, FormClosingEventArgs e){// 取消接受客户端的任务if (ctsAcceptClients != null)ctsAcceptClients.Cancel();// 关闭所有客户端连接CloseAllClients();// 关闭服务器if (server != null){server.Close();}}}
}

改进后的TCP服务端代码

客户端改进代码

using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
​
namespace 客户端
{public partial class ClientFrm : Form{// 客户端Socketprivate Socket client = null;// 接收消息的 cancellation tokenprivate CancellationTokenSource receiveCts = null;// 连接状态private bool isConnected = false;
​public ClientFrm(){InitializeComponent();// 初始化UIthis.FormClosing += ClientFrm_FormClosing;btnDisconnect.Enabled = false;}
​// 连接/断开按钮点击事件private async void btnConnect_Click(object sender, EventArgs e){if (!isConnected){await ConnectToServer();}else{DisconnectFromServer();}}
​// 连接到服务器private async Task ConnectToServer(){try{string ip = txtIpAddress.Text.Trim();int port;if (!int.TryParse(txtPort.Text.Trim(), out port) || port < 1 || port > 65535){ShowStatus("请输入有效的端口号(1-65535)");return;}
​// 禁用连接按钮,防止重复点击btnConnect.Enabled = false;ShowStatus("正在连接到服务器...");
​// 1. 实例化客户端Socketclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
​// 2. 连接到服务器IPEndPoint remoteEp = new IPEndPoint(IPAddress.Parse(ip), port);await client.ConnectAsync(remoteEp);
​// 连接成功isConnected = true;ShowStatus($"已连接到服务器: {ip}:{port}");btnConnect.Text = "断开连接";btnDisconnect.Enabled = true;
​// 开始接收服务器消息StartReceivingMessages();}catch (Exception ex){ShowStatus($"连接失败: {ex.Message}");client = null;}finally{btnConnect.Enabled = true;}}
​// 断开与服务器的连接private void DisconnectFromServer(){if (client != null && client.Connected){try{// 取消接收任务if (receiveCts != null)receiveCts.Cancel();// 关闭连接client.Shutdown(SocketShutdown.Both);client.Close();}catch (Exception ex){ShowStatus($"断开连接出错: {ex.Message}");}}
​// 更新状态isConnected = false;client = null;ShowStatus("已断开与服务器的连接");btnConnect.Text = "连接";btnDisconnect.Enabled = false;}
​// 开始接收服务器消息private void StartReceivingMessages(){receiveCts = new CancellationTokenSource();byte[] buffer = new byte[1024];
​Task.Run(() =>{try{while (!receiveCts.IsCancellationRequested){// 等待接收数据int bytesRead = client.Receive(buffer);if (bytesRead == 0){ShowStatus("服务器已关闭连接");break;}// 转换为字符串string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);ShowMessage($"收到服务器消息: {message}");}}catch (Exception ex){if (!receiveCts.IsCancellationRequested){ShowStatus($"接收消息出错: {ex.Message}");}}finally{// 如果不是主动取消,自动重连if (!receiveCts.IsCancellationRequested){Invoke(new Action(() =>{DisconnectFromServer();// 可以在这里添加自动重连逻辑}));}}}, receiveCts.Token);}
​// 发送消息到服务器private void btnSend_Click(object sender, EventArgs e){SendMessageToServer();}
​// 发送消息到服务器private void SendMessageToServer(){if (!isConnected || client == null || !client.Connected){ShowStatus("请先建立连接再发送消息");return;}
​string message = txtMessage.Text.Trim();if (string.IsNullOrEmpty(message)){ShowStatus("请输入要发送的消息");return;}
​try{byte[] data = Encoding.UTF8.GetBytes(message);client.Send(data);ShowMessage($"我: {message}");txtMessage.Clear();}catch (Exception ex){ShowStatus($"发送消息失败: {ex.Message}");DisconnectFromServer();}}
​// 在消息区域显示消息private void ShowMessage(string message){Invoke(new Action(() =>{richTextBox1.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\n");richTextBox1.ScrollToCaret();}));}
​// 显示状态信息private void ShowStatus(string status){Invoke(new Action(() =>{labelStatus.Text = status;// 同时在消息区域也显示状态信息richTextBox1.AppendText($"[{DateTime.Now:HH:mm:ss}] {status}\n");richTextBox1.ScrollToCaret();}));}
​// 窗体关闭时确保资源释放private void ClientFrm_FormClosing(object sender, FormClosingEventArgs e){DisconnectFromServer();}
​// 断开连接按钮点击事件private void btnDisconnect_Click(object sender, EventArgs e){DisconnectFromServer();}
​// 按Enter键发送消息private void txtMessage_KeyPress(object sender, KeyPressEventArgs e){if (e.KeyChar == (char)Keys.Enter){e.Handled = true; // 防止换行SendMessageToServer();}}}
}

四、服务端与客户端功能对比
功能原始代码改进后代码
多客户端支持基础支持,无管理完善支持,可显示 / 选择客户端
资源释放不完善完善的资源释放机制
消息接收依赖 Available 属性,可能不完整使用固定缓冲区,确保完整接收
双向通信服务端无法向客户端发送支持双向通信
异常处理基础处理完善的异常捕获和处理
UI 体验简单更友好,状态显示更清晰
连接管理基础支持手动断开、自动重连
消息记录简单显示带时间戳的完整记录
五、关键技术点总结
  1. Socket 通信基础

    • 使用Socket类创建 TCP 连接

    • 服务端需要BindListenAccept三个步骤

    • 客户端需要Connect步骤建立连接

  2. 异步编程

    • 使用Task.Runasync/await避免 UI 线程阻塞

    • CancellationTokenSource用于安全取消任务

  3. 多客户端管理

    • 使用字典存储客户端连接

    • 采用线程锁确保多线程安全访问

  4. 数据处理

    • 使用固定大小缓冲区接收数据

    • 采用 UTF8 编码进行字符串转换

  5. 异常处理

    • 捕获网络通信中可能出现的各种异常

    • 客户端断开连接时的妥善处理

通过这些改进,TCP 通信程序变得更加健壮、易用,能够更好地满足实际应用场景的需求。

http://www.dtcms.com/a/418910.html

相关文章:

  • 族蚂建站郴州网站建设费用价格
  • 对象分配在哪块内存?
  • AI Agent智能体如何突破“听懂却做不好”困局?多模态技术打通全链路
  • 图卷积网络 (GCN)
  • JMeter中常用的配置优化
  • 网站怎样做优化调整深圳vi设计深圳vi设计公司
  • 做教育培训网站需要资质么网站对联广告图片
  • 《Muduo网络库:实现Channel通道以及Poller抽象基类》
  • 安全系统架构
  • 中国画廊企业网站模板thinkphp做视频网站
  • C++ 位运算 高频面试考点 力扣 268. 丢失的数字 题解 每日一题
  • 【展厅多媒体】解析VR虚拟驾驶实现多场景自由切换
  • 网站建设吉金手指专业11青海省高等级公路建设管局网站
  • 厦门北京网站建设公司怎样给一个公司做网站
  • 58.Nginx的反向代理和负载均衡
  • 阿里云函数计算 AgentRun 全新发布,构筑智能体时代的基础设施
  • 做营销型网站价格wordpress 考试系统
  • 黄金网站app视频播放画质选择人力资源网站建设计划书
  • 我国省级档案网站建设状况wordpress插件events
  • 【CSS】flex布局
  • 【论文阅读】具身人工智能:从大型语言模型到世界模型
  • 【论文阅读】Segment Anything
  • 大连网站制作仟亿科技wordpress免费网站模板下载
  • 商城网站开发的任务书网址大全2345
  • 八、安装 Hadoop
  • 华为电脑 银河麒麟系统 使用CrossOver安装微软Office2016
  • 设计模式(C++)详解——迭代器模式(3)
  • 做58网站怎么赚钱吗公司起名字大全免费四个字
  • 基于Python的CrewAI多智能体协同写作助手
  • 按月付费网站建设网站建设学那些课程