C#基于Socket实现TCP简易聊天室:原理与完整代码
本文介绍如何使用C#语言结合Socket API构建一个基于TCP协议的多玩家简易聊天室系统。该方案包含独立的服务器端用于连接管理与消息分发,以及运行于Unity环境的客户端负责界面交互与数据收发。核心机制依赖于多线程处理异步网络连接与实时消息流,适合初学者理解局域网即时通讯应用的基础架构。
一、系统设计与核心逻辑
实现简易聊天室主要分为两个角色:服务器端(Server)与客户端(Client)。服务器作为中心节点,负责维持所有活跃连接的列表,并充当消息中转站,将单一客户端发出的消息广播给其他已连接的客户端。客户端则负责建立与服务器的持久连接,并向服务器发送本地输入的消息及从服务器接收到的广播消息。
第1步:初始化服务器套接字
目的:创建监听端口,准备接受外部连接请求。
操作:在C#中实例化 Socket 对象,配置为 IPv4 (InterNetwork) 和 TCP (Stream) 协议,绑定特定IP与端口,并开启监听。
using System.Net;
using System.Net.Sockets;
// 创建Socket实例
Socket tcpSever = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 绑定IP地址和端口号 (示例IP: 192.168.1.2, 端口: 8899)
tcpSever.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8899));
// 设置最大等待连接队列长度
tcpSever.Listen(100);
Console.WriteLine("Begin to listen...");
第2步:主循环与连接管理
目的:持续检测新的连接请求,并为每个新客户端创建对应的会话管理对象。
操作:在一个无限循环中使用 Accept() 方法阻塞等待连接。一旦检测到新连接,便将该连接的 Socket 封装进专门的 Client 类实例中,并存入全局列表以便统一维护。
using System.Collections.Generic;
namespace 聊天室_Socket_TCP_服务器端
{
class Program
{
static List<Client> clients = new List<Client>();
static List<Client> notClients = new List<Client>();
static void Main(string[] args)
{
// ... (此处省略第一步的socket绑定代码)
while (true)
{
// 阻塞等待并获取新的客户端Socket
Socket clientSocket = tcpSever.Accept();
if (clientSocket!=null)
{
Console.WriteLine("A client has connneted...");
// 创建客户端会话管理类
Client client = new Client(clientSocket);
// 加入在线列表
clients.Add(client);
}
}
Console.ReadKey();
}
}
}
第3步:实现消息广播功能
目的:确保收到的一条消息能被准确地发送给所有当前处于活跃状态的客户端,同时清理掉已断开的连接。
参数:message 待发送的字符串内容。
状态:遍历在线列表,对有效连接发送数据,移除无效连接。
/// <summary>
/// 广播消息给所有在线客户端
/// </summary>
public static void CastMessageTOAllConnetedClients(string message)
{
foreach (var client in clients)
{
if (client.Conneted)
{
// 转发消息给当前客户端
client.CastMessage(message);
}
else
{
// 标记断开连接的客户端,暂存以便后续删除
notClients.Add(client);
}
}
// 清理离线客户端
foreach (var temp in notClients)
{
clients.Remove(temp);
}
}
第4步:单客户端通信线程与心跳检测
目的:独立于主线程的后台任务,专门负责接收特定客户端的数据并实时检测其连接状态。
入口:Client 类的构造函数中启动。
动作:
1. 使用 Poll(SelectMode.SelectRead) 进行非阻塞式检查,若发现连接关闭则终止线程并关闭资源。
2. 使用 Receive 方法读取缓冲区字节。
3. 解码UTF-8字符串并通过服务器的广播方法转发。
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace 聊天室_Socket_TCP_服务器端
{
class Client
{
public Socket clientSocket;
private Thread mesManageTherad;
private byte[] bufffer=new byte[20]; // 注意:实际应用中建议扩大缓冲区或处理分包
public Client(Socket soc)
{
clientSocket = soc;
// 开启新线程处理该客户端的消息
mesManageTherad = new Thread(MessageSendFromClient);
mesManageTherad.Start();
}
private void MessageSendFromClient()
{
while (true)
{
// 轮询检查读状态,Poll返回true表示无数据可读或连接已关闭
if (clientSocket.Poll(10, SelectMode.SelectRead)==true)
{
clientSocket.Close();
break; // 终止本线程,触发清理逻辑
}
// 接收数据
int byteNum = clientSocket.Receive(bufffer);
string mes = Encoding.UTF8.GetString(bufffer, 0 , byteNum);
Console.WriteLine("客户端发送过来的消息:"+mes);
// 调用服务器的广播方法
Program.CastMessageTOAllConnetedClients(mes);
}
}
/// <summary>
/// 向该客户端发送消息
/// </summary>
public void CastMessage(string message)
{
byte[] data = Encoding.UTF8.GetBytes(message);
clientSocket.Send(data);
}
/// <summary>
/// 判断是否断开连接
/// </summary>
public bool Conneted
{
get
{
return clientSocket.Connected;
}
}
}
}
二、Unity 客户端开发与集成
为了演示完整的交互流程,客户端采用 Unity 引擎开发,结合 NGUI 插件实现基础的用户界面。脚本逻辑挂载在 UI 控件上,主要职责包括与服务器建立握手连接、在 Update 循环中更新显示文本,以及处理发送按钮的点击事件。
第5步:客户端连接与后台接收线程
目的:在游戏启动时自动连接服务器,并在后台静默接收数据,避免在主线程造成卡顿。
参数:ipadress 服务器IP,port 端口号。
注意:Unity 的主线程严禁直接操作 UI 组件(如修改 Text Label 的内容),因此必须通过变量中转或在主线程中执行 UI 更新。
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
public class ChatManager : MonoBehaviour {
private string _ipAdress = "192.168.1.2";
private int _port=8899;
EndPoint remotPoint;
Socket clientSocket;
public UIInput buttonInput;
private bool isCanSend=false;
private string buttonMessage=null;
Thread receiveThread;
byte[] bufferReceive = new byte[1024];
public UILabel chatWindowLable;
private string message = "";//用于跨线程传递数据的缓冲变量
// Use this for initialization
void Start () {
ConnetToSever(_ipAdress, _port);
}
void ConnetToSever(string ipadress,int port)
{
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
remotPoint = new IPEndPoint(IPAddress.Parse(ipadress),port);
// 建立连接
clientSocket.Connect(remotPoint);
// 开启后台线程接收消息
receiveThread = new Thread(ReceiveMessageFormSever);
receiveThread.Start();
}
// ... (其他方法见下文)
第6步:消息接收与非UI线程安全更新
目的:在不阻塞游戏帧率的前提下,获取服务器发来的广播消息并暂存。
动作:在 ReceiveMessageFormSever 线程中不断循环调用 Receive。接收到数据后,仅更新私有变量 message。
private void ReceiveMessageFormSever()
{
while (true)
{
if (clientSocket.Connected)
{
int length = clientSocket.Receive(bufferReceive);
message = Encoding.UTF8.GetString(bufferReceive, 0, length);
// ps:不要在单独的线程里面操作unity组件
}
else
{
break;
}
}
}
// Update is called once per frame
void Update () {
// 只有在主线程Update中访问message变量才安全地更新UI
if (message!=null && message!="")
{
// 追加显示并清空缓存
chatWindowLable.text += "\n" + message;
message = "";
}
}
第7步:用户输入与消息发送
目的:响应用户界面操作,将文本编码为字节流发送至服务器。
操作:监听NGUI输入框的变化或直接处理按钮点击事件,获取文本并使用 Encoding.UTF8.GetBytes 转换,最后通过 Socket.SendTo 发出。
private void SendMessage(string message)
{
byte [] buffer= Encoding.UTF8.GetBytes(message);
// SendTo 适用于未Connect的Socket,对于已Connect的Socket也可用
clientSocket.SendTo(buffer,remotPoint);
}
public void OnSendButtonClickS()
{
if (buttonInput.value!=null)
{
buttonMessage = buttonInput.value;
}
else
{
buttonMessage = "输入框为空!";
}
SendMessage(buttonMessage);
// 清空输入框
buttonInput.value = "";
}
三、关键注意事项与调试建议
在上述示例代码的运行过程中,有几个关键点需要特别注意,以确保系统的稳定性:
- 缓冲区大小问题:服务器端的
bufffer初始化为byte[20]在实际长消息测试中会导致数据截断。生产环境中应增大缓冲区(如1024字节以上),并注意 TCP 粘包/拆包的处理机制。 - 线程安全性:Unity 客户端使用了典型的“双线程”模式(接收线程 + 主渲染线程),这是解决 C# Socket 异步接收与 Unity UI 刷新冲突的标准做法之一。
- 异常捕获:示例代码为了保持逻辑简洁,省略了大量的
try-catch块。网络编程中,网络波动导致的SocketException是常态,务必对Receive、Send和Connect进行异常保护,防止程序因单个客户端断连而崩溃。
四、总结
通过上述步骤,我们构建了一个基于 C# Socket 的基础 TCP 聊天室原型。服务器负责监听与广播,客户端(Unity)负责展示与发送。这种架构清晰地展示了 C/S 模式下网络通信的生命周期,包括绑定、监听、接受、收发消息及断开连接。以上就是C#基于Socket实现简单聊天室功能的详细内容,更多关于C#网络编程Socket技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









