C# WinForm TCP 服务器+WinForm TCP 客户端

一、项目整体架构说明

1.1 项目核心功能

  • TCP 服务器初始化:自动获取本机 IPv4 地址,绑定指定端口,完成服务器基础配置

  • 多客户端并发处理:通过异步任务实现多客户端连接监听,支持同时处理多个客户端的消息接收

  • 实时消息展示:接收客户端消息后,跨线程更新 UI 控件,实现消息实时显示

  • 单/多客户端模式切换:提供单客户端与多客户端两种接收方法,适配不同使用场景

1.2 核心技术依赖

  • TCP 套接字(Socket):基于流模式(Stream)实现网络通信,采用 IPv4 地址族,遵循 TCP 协议

  • 异步任务(Task):避免 UI 卡顿,实现客户端并发监听与消息接收的非阻塞操作

  • CancellationTokenSource:异步任务取消源,用于终止监听与接收任务,实现资源优雅释放

  • DNS 解析:通过 Dns 类自动获取本机 IPv4 地址,无需手动配置 IP,提升灵活性

  • WinForm 控件:通过 RichTextBox 实时展示客户端消息,实现界面交互与状态反馈

1.3 核心变量与方法说明

  • Socket server:服务器端 Socket 对象,全局初始化,负责监听客户端连接请求,是整个服务器的核心对象

  • CancellationTokenSource cts/cts2:异步任务取消源,cts 用于终止客户端监听任务,cts2 用于终止单个客户端的数据接收任务,实现任务的可控终止

  • AcceptDataMul 方法:多客户端接收模式,通过两层异步任务实现多客户端并发连接与消息接收,是项目核心功能方法

  • AcceptDataSigle 方法:单客户端接收模式,仅处理单个客户端的连接与消息接收,适合测试或单客户端场景


二、核心代码逐行解析

2.1 命名空间引用

复制代码
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;

解析:引入网络通信(Socket、IPEndPoint、DNS)、异步任务(CancellationTokenSource、Task)、WinForm 控件等核心命名空间,为服务器初始化、客户端监听、消息接收及界面交互提供基础依赖,确保各类网络操作与界面更新可正常执行。

2.2 窗体类与构造函数

复制代码
namespace _1服务端
{
    public partial class Form1 : Form
    {
        // 服务器 Socket 对象,全局初始化,用于监听客户端连接请求
        Socket server = null;

        public Form1()
        {
            InitializeComponent();
        }
    }
}

解析:定义项目命名空间与窗体类,声明全局服务器 Socket 对象,构造函数仅完成窗体初始化,无额外业务逻辑,核心功能通过窗体加载事件实现,保证代码结构清晰。

2.3 窗体加载事件(Form1_Load):服务器初始化核心流程

复制代码
private void Form1_Load(object sender, EventArgs e)
{
    // 1. 实例化服务器 Socket 对象
    // 参数1:AddressFamily.InterNetwork 指定使用 IPv4 地址族,InterNetworkV6 对应 IPv6
    // 参数2:SocketType.Stream 指定数据传输采用流模式,是 TCP 协议的专属传输方式
    // 参数3:ProtocolType.Tcp 指定通信协议为 TCP,实现可靠的面向连接的通信
    server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // 2. 绑定服务器 IP 与端口
    string ipStr = ""; // 存储本机 IPv4 地址字符串
    int port = 63323;  // 端口号,范围0-65535,建议使用高位端口(1024以上),避免与系统预留端口冲突

    // 获取本机主机名,通过 DNS 解析获取所有 IP 地址
    // Dns.GetHostName():获取当前计算机的主机名称
    // Dns.GetHostEntry():通过主机名解析对应的 IP 地址信息
    IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());

    // 遍历 IP 地址列表,筛选出 IPv4 地址
    foreach (IPAddress item in host.AddressList)
    {
        // 判断地址类型是否为 IPv4,避免获取到 IPv6 地址导致绑定失败
        if (item.AddressFamily == AddressFamily.InterNetwork)
        {
            ipStr = item.ToString();
        }
    }

    // 将字符串格式的 IP 转换为 IPAddress 对象,用于后续绑定
    IPAddress address = IPAddress.Parse(ipStr);
    // 构建终端对象(包含 IP 与端口),是服务器绑定的核心对象
    IPEndPoint endPoint = new IPEndPoint(address, port);
    server.Bind(endPoint); // 绑定 IP 与端口,完成服务器基础配置,绑定后服务器可监听该端口的请求

    // 3. 开启服务器监听,参数100表示最大等待连接数,即最多可容纳100个客户端同时等待连接
    server.Listen(100);

    // 4. 启动多客户端接收任务(也可替换为单客户端接收方法 AcceptDataSigle)
    // 多客户端模式:支持同时处理多个客户端连接与消息接收
    AcceptDataMul();
}

解析:该方法是服务器初始化的核心,遵循"实例化 Socket→绑定 IP 端口→开启监听→启动接收任务"的标准流程,自动获取本机 IPv4 地址,无需手动配置,同时默认启动多客户端接收模式,适配多数场景需求,代码注释详细标注了每一步的作用与注意事项。

2.4 多客户端接收方法(AcceptDataMul)

复制代码
// 异步任务取消源,用于终止客户端监听任务
CancellationTokenSource cts;
// 异步任务取消源,用于终止单个客户端的数据接收任务
CancellationTokenSource cts2;

public void AcceptDataMul()
{
    // 初始化监听任务取消源,用于控制监听任务的终止
    cts = new CancellationTokenSource();
    // 启动异步任务,避免 UI 卡顿,循环监听客户端连接请求
    Task.Run(() =>
    {
        // 循环监听,直到任务被取消(cts.IsCancellationRequested 为 true)
        while (!cts.IsCancellationRequested)
        {
            // 接收客户端连接请求,返回客户端 Socket 对象
            // Accept() 方法会阻塞线程,直到有客户端连接,因此必须放在异步任务中
            Socket client = server.Accept();
            if (client == null)
            {
                Console.WriteLine("客户端未连接");
                break;
            }

            // 确认客户端正常连接,启动数据接收任务
            if (client.Connected)
            {
                // 初始化当前客户端数据接收任务取消源,用于控制单个客户端接收任务的终止
                cts2 = new CancellationTokenSource();
                // 启动异步任务,循环接收当前客户端发送的数据
                Task.Run(() =>
                {
                    while (!cts2.IsCancellationRequested)
                    {
                        try
                        {
                            // 初始化接收缓冲区,长度为客户端可用数据长度,避免缓冲区溢出
                            byte[] buffer = new byte[client.Available];
                            // 接收客户端数据,返回实际接收的字节数
                            // Receive() 方法会阻塞线程,直到接收到数据,因此放在异步任务中
                            int length = client.Receive(buffer);

                            // 跨线程更新 UI,将接收的消息追加到 RichTextBox
                            // 因为异步任务无法直接操作 UI 控件,需通过 Invoke 实现跨线程更新
                            Invoke(new Action(() =>
                            {
                                richTextBox1.AppendText(Encoding.UTF8.GetString(buffer));
                            }));
                        }
                        catch (Exception)
                        {
                            // 接收异常,退出当前客户端接收循环,避免任务崩溃
                            break;
                        }
                    }
                }, cts2.Token); // 传入取消令牌,支持任务的可控终止
            }
        }
    }, cts.Token); // 传入取消令牌,支持监听任务的可控终止
}

解析:该方法实现多客户端并发处理,核心逻辑为"循环监听客户端连接→为每个客户端启动独立接收任务→跨线程更新 UI",通过两层异步任务实现客户端并发与数据实时接收,同时通过 CancellationTokenSource 实现任务的优雅终止,避免资源泄漏与线程阻塞,是项目的核心功能实现。

2.5 单客户端接收方法(AcceptDataSigle)

复制代码
public void AcceptDataSigle()
{
    // 初始化任务取消源,用于控制接收任务的终止
    cts = new CancellationTokenSource();
    // 启动异步任务,处理单个客户端的连接与数据接收
    Task.Run(() =>
    {
        // 接收单个客户端连接请求
        Socket client = server.Accept();

        // 循环接收客户端数据,直到任务被取消
        while (!cts.IsCancellationRequested)
        {
            // 初始化接收缓冲区,长度为客户端可用数据长度
            byte[] buffer = new byte[client.Available];
            // 接收客户端数据
            int length = client.Receive(buffer);

            // 跨线程更新 UI,将接收的消息追加到 RichTextBox
            Invoke(new Action(() =>
            {
                richTextBox1.AppendText(Encoding.UTF8.GetString(buffer));
            }));
        }
    }, cts.Token);
}

解析:该方法仅支持单个客户端连接,逻辑相对简单,适合测试或单客户端场景,核心流程与多客户端模式一致,仅去掉了并发处理逻辑,仅处理单个客户端的消息接收,代码简洁易懂,可作为多客户端模式的基础参考。

2.6 核心步骤总结(代码注释提炼)

  1. 创建服务器 Socket 对象:new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp),指定 IPv4、流模式、TCP 协议

  2. 绑定服务器终端:Bind(IPEndPoint),需指定本机 IPv4 地址与高位端口,完成服务器基础配置

  3. 开启服务器监听:Listen(最大等待连接数),设置可容纳的客户端等待数量

  4. 接收客户端连接请求:Accept(),返回客户端 Socket 对象,需放在异步任务中避免阻塞

  5. 接收客户端数据:Receive(),将接收的字节数组解码为字符串,通过 Invoke 跨线程更新 UI


三、核心易错点与注意事项

3.1 线程安全易错点

  • 异步任务中禁止直接操作 UI 控件,必须使用 Invoke 实现跨线程更新,否则会抛出线程安全异常,导致程序崩溃

  • 客户端接收任务需独立启动,避免阻塞主线程与其他客户端的接收操作,保证多客户端并发处理的流畅性

  • server.Accept() 与 client.Receive() 均为阻塞方法,必须放在异步任务中执行,否则会导致 UI 卡顿

3.2 资源释放易错点

  • 当前代码未实现服务器关闭与资源释放逻辑,需补充窗体关闭事件,调用 cts.Cancel() 终止异步任务,关闭 server 与 client 套接字,避免资源泄漏

  • 客户端接收异常时,需及时关闭客户端 Socket,避免无效连接占用系统资源,可在 catch 块中添加 client.Close() 逻辑

  • 多客户端模式下,每个客户端的接收任务需独立使用 CancellationTokenSource,避免任务终止相互影响,确保每个客户端的接收任务可独立控制

3.3 网络通信易错点

  • 端口号需避免使用低位端口(0-1023),此类端口多为系统预留,易导致绑定失败,建议使用1024以上的高位端口

  • IP 地址筛选需确认 AddressFamily 为 InterNetwork(IPv4),避免获取到 IPv6 地址导致绑定失败,可通过断点调试确认 ipStr 的值

  • 接收数据时需判断接收长度,避免处理空数据,同时需统一编码格式(本项目使用 UTF8),防止中文消息乱码,与客户端编码保持一致

  • 服务器绑定的端口需确保未被其他程序占用,否则会抛出绑定异常,可通过命令行(netstat -ano)查看端口占用情况

3.4 其他注意事项

  • 可在接收数据时添加客户端标识(如 IP+端口),区分不同客户端的消息,便于界面展示,提升用户体验

  • 当前代码中 cts2 为全局变量,会导致多客户端接收任务的取消源相互覆盖,需改为局部变量,每个客户端的接收任务独立使用自己的取消源

  • 可优化服务器初始化逻辑,将 IP、端口改为可配置输入框,避免硬编码,提升程序的灵活性与可扩展性

WinForm TCP 客户端

一、项目整体架构说明

1.1 项目核心功能

  • TCP 客户端初始化:创建客户端 Socket,异步连接指定服务器

  • 消息发送功能:向服务器发送文本消息,支持发送状态提示

  • 异常处理与资源释放:连接/发送失败时,自动关闭客户端并释放资源

  • 界面状态联动:连接成功后切换按钮状态,避免重复连接或无效发送

1.2 核心技术依赖

  • TCP 套接字(Socket):基于流模式(Stream)实现网络通信,采用 IPv4 地址族

  • 异步编程(async/await):实现非阻塞连接,避免 UI 卡顿

  • WinForm 控件:通过按钮、文本框、标签实现界面交互与状态展示

  • 编码处理:统一使用 UTF8 编码,避免中文消息乱码

1.3 核心变量与方法说明

  • Socket client:客户端 Socket 对象,负责与服务器建立连接、发送数据

  • button1_Click 方法:连接服务器按钮事件,实现异步连接与状态切换

  • button2_Click 方法:发送消息按钮事件,实现数据发送、异常处理与资源释放


二、核心代码逐行解析

2.1 命名空间引用

复制代码
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.Tasks;
using System.Windows.Forms;

解析:引入网络通信(Socket、IPEndPoint)、异步任务(Task)、WinForm 控件等核心命名空间,为客户端连接、消息发送及界面交互提供基础依赖,确保各类网络操作可正常执行。

2.2 窗体类与构造函数

复制代码
namespace _2客户端
{
    public partial class Form1 : Form
    {
        // 客户端 Socket 对象,全局初始化,用于与服务器通信
        Socket client;

        public Form1()
        {
            InitializeComponent();
        }
    }
}

解析:定义项目命名空间与窗体类,声明全局客户端 Socket 对象,构造函数仅完成窗体初始化,无额外业务逻辑,核心功能通过按钮点击事件实现。

2.3 连接服务器按钮事件(button1_Click)

复制代码
private async void button1_Click(object sender, EventArgs e)
{
    // 开始连接,捕获异常保证程序稳定性
    try
    {
        // 1. 创建客户端 Socket 实例
        // 参数1:AddressFamily.InterNetwork 指定使用 IPv4 地址族
        // 参数2:SocketType.Stream 指定数据传输采用流模式(TCP 专属)
        // 参数3:ProtocolType.Tcp 指定通信协议为 TCP
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        // 2. 创建服务器终端对象(IP+端口),需与服务器配置一致
        IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("192.168.219.12"), 63323);

        // 3. 异步连接服务器,避免 UI 卡顿,await 等待连接结果
        // 同步连接方式:client.Connect(endpoint); 会阻塞线程
        await client.ConnectAsync(endpoint);

        // 4. 连接成功,更新界面状态
        label1.Text = "连接成功";
        button1.Enabled = false; // 禁用连接按钮,避免重复连接
        button2.Enabled = true;  // 启用发送按钮,允许发送消息
    }
    catch (Exception)
    {
        // 连接失败,提示用户并返回
        MessageBox.Show("服务器未打开");
        return;
    }
}

解析:核心实现客户端异步连接服务器,流程为"实例化客户端 Socket→创建服务器终端→异步连接→更新界面状态",采用 async/await 实现非阻塞连接,同时通过异常捕获处理连接失败场景,保证程序稳定性。

2.4 发送消息按钮事件(button2_Click)

复制代码
private void button2_Click(object sender, EventArgs e)
{
    // 1. 校验客户端状态:未初始化或未连接时,提示用户并返回
    if (client == null || !client.Connected)
    {
        MessageBox.Show("还没连接到服务器,请先连接服务器");
        return;
    }

    // 2. 校验发送内容:为空时提示用户并返回
    if (string.IsNullOrEmpty(textBox1.Text))
    {
        MessageBox.Show("发送数据不能为空");
        return;
    }

    // 3. 发送数据,捕获异常处理发送失败场景
    try
    {
        // 将文本内容转换为 UTF8 编码的字节数组
        byte[] data = Encoding.UTF8.GetBytes(textBox1.Text);
        // 向服务器发送数据
        client.Send(data);
        // 更新状态提示
        label1.Text = "发送成功";
    }
    catch (Exception)
    {
        // 发送失败,更新状态提示
        label1.Text = "发送失败";

        // 4. 资源释放:发送失败时,关闭客户端连接并释放资源
        if (client != null)
        {
            // 若客户端仍处于连接状态,先关闭连接
            if (client.Connected)
                client.Shutdown(SocketShutdown.Both);
            // 关闭客户端 Socket
            client.Close();
            // 释放客户端资源
            client.Dispose();
        }

        // 5. 恢复界面状态,允许重新连接
        button1.Enabled = true;
        button2.Enabled = false;
    }
}

解析:实现消息发送功能,核心逻辑为"校验状态→编码数据→发送消息→异常处理→资源释放",发送前严格校验客户端状态与消息内容,发送失败时自动关闭客户端并释放资源,同时恢复界面状态,便于用户重新连接。

2.5 核心步骤总结

  1. 创建客户端 Socket 对象:new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

  2. 创建服务器终端:IPEndPoint(服务器 IP, 端口),需与服务器配置一致

  3. 异步连接服务器:await client.ConnectAsync(endpoint),避免 UI 卡顿

  4. 校验发送条件:客户端已连接、消息内容非空

  5. 发送数据:client.Send(字节数组),统一使用 UTF8 编码

  6. 异常处理与资源释放:发送失败时,关闭客户端并释放资源,恢复界面状态


三、核心易错点与注意事项

3.1 连接相关易错点

  • 服务器 IP 与端口必须与服务器端配置完全一致,否则无法建立连接

  • 连接操作需使用异步方式(await ConnectAsync),同步连接会阻塞 UI 线程,导致界面卡顿

  • 连接成功后需及时禁用连接按钮,避免重复创建客户端连接,导致资源浪费

3.2 发送消息相关易错点

  • 发送前必须校验客户端状态(client != null && client.Connected),避免向未连接的客户端发送消息,引发异常

  • 消息编码需统一使用 UTF8,避免中文消息乱码,与服务器端编码保持一致

  • 发送数据时需判断消息内容非空,避免发送空数据,浪费网络资源

3.3 资源释放易错点

  • 发送失败时,必须通过 Shutdown、Close、Dispose 三步释放客户端资源,避免资源泄漏

  • Shutdown 需传入 SocketShutdown.Both,同时关闭发送与接收通道,确保连接彻底断开

  • 资源释放后需恢复界面按钮状态,允许用户重新连接服务器

3.4 其他注意事项

  • 客户端 Socket 为全局变量,避免重复实例化,仅在发送失败或重新连接时重新创建

  • 可优化界面设计,将服务器 IP、端口改为可配置输入框,避免硬编码,提升灵活性

  • 可添加消息接收功能,实现客户端与服务器的双向通信,完善聊天室功能

相关推荐
牧码岛19 分钟前
阿里云服务器新端口上线SOP(Windows+Nginx+HTTPS)
服务器·nginx·阿里云
RD_daoyi30 分钟前
谷歌改写了76%的标题:超60字符的,95%会被谷歌自己重写
大数据·服务器·开发语言·前端·搜索引擎·html
csdn_aspnet32 分钟前
C# 多个串口多个线程发送数据和接收数据
c#·串口·thread·com·serialport
张小姐的猫41 分钟前
【Linux】网络编程 —— 手动部署 & 测试内网穿透
linux·运维·服务器·网络·tcp/ip
深念Y1 小时前
ZTE B860AV1.1-T2 机顶盒改造记录:从 Android 到 Linux 服务器
android·linux·运维·服务器·boot·cma·机顶盒
祖力551 小时前
进程相关的函数与exec函数簇
linux·运维·服务器·进程·exec函数簇·进程相关的函数
Discipline~Hai1 小时前
Linux应用软件编程04-线程
linux·服务器·c语言·开发语言·linux应用软件编程
淡淡的香烟2 小时前
Android中tcp的通信
android·物联网·tcp/ip
Loongproxy2 小时前
抖音企业号更换公司主体怎么操作?矩阵账号配静态长效IP的稳定方案
服务器·网络·网络协议·tcp/ip