C# WinForm TCP聊天室(服务端+客户端)

一、项目整体功能考点

本项目为多客户端TCP聊天室,支持:

  • 服务端监听、多客户端同时在线

  • 客户端上线自动刷新在线列表(下拉框)

  • 私聊:对选中单个客户端发消息

  • 广播:对所有在线客户端群发消息

  • 自定义断开协议:n1 协议标记

  • 客户端下线自动从在线列表移除

  • 服务端关闭主动通知所有客户端下线

  • 窗口关闭自动释放资源、防止端口占用

二、核心数据结构考点(必考)

2.1 双容器存储客户端(核心设计 + 对应代码)

cs 复制代码
// 字典:用于快速查找客户端(Key:终端唯一标识)
Dictionary<EndPoint, Socket> clients = new Dictionary<EndPoint, Socket>();

// 泛型集合:绑定下拉框数据源,展示在线客户端
List<Client> clients2 = new List<Client>();

考点解释

  • Dictionary:保证客户端唯一性,防止重复连接

  • List<Client>:自定义实体类,专门用于UI下拉框绑定

2.2 自定义 Client 实体类(知识点 + 完整代码)

作用:封装客户端终端地址和Socket对象,适配下拉框 DisplayMember / ValueMember 绑定

cs 复制代码
public class Client
{
    public EndPoint EndPoint { get; set; } // 客户端IP+端口(展示)
    public Socket Socket { get; set; }     // 客户端套接字(取值发送)
}

三、自定义通信协议 n1(超级重点)

3.1 为什么不用系统自带断开?

TCP是流式协议,客户端异常掉线、断网,服务端无法立刻感知,会造成在线列表残留脏数据。

3.2 自定义 n1 协议规则(考试必背 + 对应代码片段)

客户端下线发送n1核心代码

cs 复制代码
// 客户端主动下线,发送自定义协议告知服务端
socketClient.Send(Encoding.UTF8.GetBytes("n1"));

服务端识别下线n1核心代码

cs 复制代码
// 服务端接收消息判断下线协议
if (message == "n1")
{
    // 移除离线客户端、刷新列表
    Client c1 = clients2.Find(x => x.EndPoint == end);
    clients2.Remove(c1);
}

服务端关闭全员推送n1代码

cs 复制代码
// 服务端关闭,通知所有客户端下线
foreach (var item in clients2)
{
    item.Socket.Send(Encoding.UTF8.GetBytes("n1"));
    item.Socket.Disconnect(false);
}

客户端接收n1自动断连代码

cs 复制代码
if (message == "n1")
{
    DisConnectServer(); // 服务端掉线,客户端主动断开
}
  • 客户端主动下线 :先向服务端发送字符串 n1

  • 服务端收到 n1:判定该客户端正常下线,从集合、下拉框中移除

  • 服务端关闭 :主动向所有客户端发送 n1

  • 客户端收到 n1:自动断开连接、重置状态

四、服务端核心流程知识点

4.1 服务端启动流程(StartServer)流程考点 + 完整代码

cs 复制代码
public void StartServer()
{
    // 1.创建TCP服务端套接字
    socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    // 2.组装IP端口终端
    IPAddress ip = IPAddress.Parse(textBox1.Text);
    int port = int.Parse(textBox2.Text);
    IPEndPoint endPoint = new IPEndPoint(ip, port);
    // 3.绑定端口 + 开启监听
    socketServer.Bind(endPoint);
    socketServer.Listen(1000);
}
  1. 实例化 TCP 流式 Socket

  2. 读取文本框IP、端口,组装 IPEndPoint

  3. Bind 绑定端口

  4. Listen 开启监听

4.2 多客户端监听机制(Accept 双层Task 考点 + 核心代码)

cs 复制代码
// 双层Task异步监听,支持多客户端并发
cts1 = new CancellationTokenSource();
Task.Run(() =>
{
    while (!cts1.IsCancellationRequested)
    {
        // 外层:接收新客户端连接
        Socket socketClient = socketServer.Accept();
        
        // 内层:每个客户端独立线程收消息
        Task.Run(() =>
        {
            while (!cts1.IsCancellationRequested)
            {
                byte[] buffer = new byte[1024 * 10];
                int length = socketClient.Receive(buffer);
            }
        }, cts1.Token);
    }
}, cts1.Token);
  • 外层Task:循环 Accept,不停接收新客户端连接

  • 内层Task:为每个客户端开启独立线程,循环接收消息

  • 使用 CancellationTokenSource 安全终止线程,防止内存泄漏

4.3 客户端上线逻辑考点 + 对应代码

cs 复制代码
// 客户端连接成功后,判重添加集合
if (!clients.ContainsKey(socketClient.RemoteEndPoint))
{
    // 加入字典(去重)
    clients.Add(socketClient.RemoteEndPoint, socketClient);
    // 加入UI绑定集合
    clients2.Add(new Client()
    {
        EndPoint = socketClient.RemoteEndPoint,
        Socket = socketClient
    });

    // Invoke跨线程刷新下拉框UI
    Invoke(new Action(() =>
    {
        comboBox1.DataSource = null;
        comboBox1.DataSource = clients2;
        comboBox1.DisplayMember = "EndPoint";
        comboBox1.ValueMember = "Socket";
    }));
}
  1. 客户端连接成功

  2. 判断字典是否包含该终端,不包含则加入

  3. 添加实体到 List 集合

  4. Invoke刷新UI:重置下拉框数据源,刷新在线列表

4.4 客户端下线逻辑考点 + 对应代码

cs 复制代码
// 接收下线协议n1,执行下线逻辑
if (message == "n1")
{
    EndPoint end = socketClient.RemoteEndPoint;
    Client c1 = clients2.Find(x => x.EndPoint == end);
    clients2.Remove(c1);
    clients.Remove(end);

    // 刷新在线客户端下拉框
    Invoke(new Action(() =>
    {
        comboBox1.DataSource = null;
        if (clients2.Count > 0)
        {
            comboBox1.DataSource = clients2;
            comboBox1.DisplayMember = "EndPoint";
            comboBox1.ValueMember = "Socket";
        }
    }));
}
  1. 服务端接收客户端消息,判断是否为 n1

  2. 根据终端地址查找对应 Client 对象

  3. 从 List、字典中移除客户端

  4. 刷新下拉框在线列表(无客户端则清空数据源)

4.5 服务端关闭流程(CloseServer)考点 + 完整代码

cs 复制代码
private void CloseServer()
{
    if (socketServer != null)
    {
        // 1.全员推送下线协议n1
        foreach (var item in clients2)
        {
            item.Socket.Send(Encoding.UTF8.GetBytes("n1"));
            item.Socket.Disconnect(false);
        }
        // 2.关闭服务端套接字
        socketServer.Close();
        socketServer = null;
        // 3.终止后台线程
        cts1.Cancel();
        // 4.清空集合与UI
        clients.Clear();
        clients2.Clear();
        comboBox1.DataSource = null;
        button1.Text = "启动服务器";
    }
}

完整安全释放流程(考试高频):

  1. 遍历所有在线客户端,统一发送 n1 下线通知

  2. 强制断开所有客户端连接

  3. 关闭服务端Socket、置空对象

  4. 取消异步线程任务

  5. 清空客户端集合、清空UI下拉框

五、消息发送两大模式考点

5.1 私聊模式(默认)考点 + 对应代码

cs 复制代码
// 获取下拉框选中的单个客户端,单独发送
Socket s1 = (Socket)comboBox1.SelectedValue;
if (s1 != null && s1.Connected)
{
    s1.Send(Encoding.UTF8.GetBytes("服务器说:" + textBox3.Text));
}
  • 获取下拉框选中的客户端 Socket

  • 单独向该客户端发送消息

5.2 广播模式(勾选复选框)考点 + 对应代码

cs 复制代码
// 遍历所有在线客户端,全员群发
if (checkBox1.Checked)
{
    foreach (var item in clients2)
    {
        item.Socket.Send(Encoding.UTF8.GetBytes("服务器说:" + textBox3.Text));
    }
}
  • 遍历所有在线客户端集合

  • 逐个Send发送消息,实现全员广播

六、客户端核心知识点

6.1 客户端连接流程 考点 + 完整代码

cs 复制代码
public void ConnetSrever()
{
    // 1.创建客户端Socket
    socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    // 2.组装服务端终端
    IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));
    // 3.连接服务端
    socketClient.Connect(endPoint);
    // 4.上线提示 + 修改按钮状态
    socketClient.Send(Encoding.UTF8.GetBytes("建立连接成功"));
    button1.Text = "断开服务器";
}
  1. 判断IP、端口非空校验

  2. 创建客户端Socket

  3. 指定服务端IP端口终端

  4. Connect 连接服务端

  5. 上线发送提示消息,修改按钮状态

6.2 客户端断开流程 考点 + 完整代码

cs 复制代码
private void DisConnectServer()
{
    if (socketClient != null)
    {
        // 1.发送下线协议
        socketClient.Send(Encoding.UTF8.GetBytes("n1"));
        // 2.断开、关闭、释放资源
        socketClient.Disconnect(false);
        socketClient.Close();
        socketClient = null;
        // 3.终止后台接收线程
        cts.Cancel();
        // 4.重置按钮状态
        button1.Text = "连接服务器";
    }
}
  1. 主动发送 n1 告知服务端即将下线

  2. 断开Socket连接、关闭、置空

  3. 取消后台接收线程

  4. 重置按钮状态

6.3 客户端被动下线 考点 + 对应代码

cs 复制代码
// 客户端接收服务端消息,识别服务端下线指令
string message = Encoding.UTF8.GetString(buffer);
if (message == "n1")
{
    DisConnectServer();// 自动断开连接
}

客户端接收消息,若消息为 n1,代表服务端关闭,自动执行断开连接方法。

七、必考通用考点(TCP聊天室通用)

  • 子线程不能操作UI:所有控件更新必须使用 Invoke

  • TCP无边界问题:原生代码使用 Available(了解即可,考试不扣分)

  • 双集合优点:字典查改快、List适配UI绑定

  • 自定义协议意义:解决TCP断线不感知、在线列表残留问题

  • 资源释放:窗口关闭、服务器关闭必须彻底释放Socket和线程

  • 按钮状态互斥:连接/断开、启动/关闭文字切换,防止重复操作

八、简答题满分模版(直接背诵)

本TCP聊天室采用服务端监听、多客户端接入模式。服务端通过字典和泛型集合双容器管理在线客户端,实现客户端唯一性校验与UI在线列表展示。通过自定义n1断线协议,解决TCP协议无法主动感知客户端掉线的问题,保证在线列表数据准确。程序支持单客户端私聊与全员广播两种消息发送模式,通过双层异步任务实现多客户端并发通信。所有子线程UI更新采用Invoke调度,所有连接关闭时执行完整资源释放,保证程序稳定运行、无端口占用与内存泄漏。

相关推荐
Shawn Dev1 小时前
没有公网 IP,如何让家里的服务器通过域名访问?——Cloudflare Tunnel 实战指南
服务器·网络协议·tcp/ip
多弗朗皮卡丘1 小时前
C语言梦开始的地方16:结构体
c语言·开发语言·windows
polarislove02141 小时前
Windows安全中心网络盘打开文件提示“打开这些文件可能会对你的计算机有害”的解决方法
网络·windows·安全
动力 continue1 小时前
MySQL 与网络基础 · 外键约束及 TCP/UDP 协议
网络·tcp/ip·mysql
李少兄2 小时前
Windows 系统 Hosts 文件完全指南
windows
七仔啊2 小时前
C#、.NET 、ASP.NET Core、Visual Studio全系列快速入门
c#·asp.net·.net
录大大i2 小时前
Windows前端开发环境搭建终极实战文档(Nvm+Node+Pnpm+Nrm)
windows
Dovis(誓平步青云)2 小时前
心情日记的安全编辑流程:输入校验、删除确认与状态回收
android·运维·服务器·开发语言·windows·安全
sg_knight3 小时前
openCode 安装与初始化配置(Windows / macOS / Linux)
linux·windows·macos·llm·agent·ai编程·opencode