C# WinForm 基于 Socket 实现 TCP 通信(解决粘包问题,附完整源码)
·
摘要
本文使用 C# WinForm 实现 TCP 服务端与客户端通信,采用自定义长度头协议解决 TCP 粘包、半包问题,实现多客户端连接、消息收发与广播,适合新手学习 Socket 网络编程。
标签
C# Socket TCP通信 WinForm 网络编程 粘包
正文
一、前言
在 C# 开发中,Socket 是实现网络通信的基础。TCP 协议是流式传输,会出现粘包、半包问题,直接收发数据会导致消息错乱。本文实现:
- WinForm 服务端:监听端口、接收多客户端、消息广播
- WinForm 客户端:连接服务端、发送接收消息
- 自定义协议:4 字节数据长度 + 消息内容,彻底解决粘包
二、核心原理:解决 TCP 粘包
TCP 是流式协议,操作系统会合并 / 拆分数据包。约定格式:[4字节int:消息长度] + [UTF-8消息内容]
- 发送:先写入长度,再写内容
- 接收:先读 4 字节长度,再读取对应字节内容
三、项目结构
- SocketCommon:公共类库(消息打包解包)
- SocketServer:WinForm 服务端
- SocketClient:WinForm 客户端
四、完整源码
1. 公共类 MessageBody.cs(两个项目都引用)
using System;
using System.Text;
namespace SocketCommon
{
/// <summary>
/// 消息封装类,处理粘包
/// </summary>
public class MessageBody
{
public string Content { get; set; }
/// <summary>
/// 编码:4字节长度 + 内容
/// </summary>
public byte[] Encode()
{
byte[] data = Encoding.UTF8.GetBytes(Content);
byte[] buffer = new byte[4 + data.Length];
BitConverter.GetBytes(data.Length).CopyTo(buffer, 0);
data.CopyTo(buffer, 4);
return buffer;
}
/// <summary>
/// 解码
/// </summary>
public static string Decode(byte[] buffer)
{
int len = BitConverter.ToInt32(buffer, 0);
return Encoding.UTF8.GetString(buffer, 4, len);
}
}
}
2. WinForm 服务端 SocketServer
界面控件:
- ListBox:
lstLog - Button:
btnStart(启动服务)
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using SocketCommon;
namespace SocketServer
{
public partial class Form1 : Form
{
private Socket _serverSocket;
private readonly List<Socket> _clientList = new List<Socket>();
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void btnStart_Click(object sender, EventArgs e)
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Any, 8888);
_serverSocket.Bind(ipPoint);
_serverSocket.Listen(10);
Log("服务端启动成功,监听端口:8888");
new Thread(AcceptClient) { IsBackground = true }.Start();
}
/// <summary>
/// 持续接收客户端连接
/// </summary>
private void AcceptClient()
{
while (true)
{
Socket client = _serverSocket.Accept();
_clientList.Add(client);
Log($"客户端已连接:{client.RemoteEndPoint}");
new Thread(() => ReceiveData(client)) { IsBackground = true }.Start();
}
}
/// <summary>
/// 接收客户端消息
/// </summary>
private void ReceiveData(Socket client)
{
byte[] buffer = new byte[1024 * 1024];
while (true)
{
try
{
int len = client.Receive(buffer);
if (len <= 0) break;
string msg = MessageBody.Decode(buffer);
Log($"收到消息:{msg}");
Broadcast(client, msg);
}
catch
{
_clientList.Remove(client);
Log($"客户端断开:{client.RemoteEndPoint}");
client.Close();
break;
}
}
}
/// <summary>
/// 广播消息给所有在线客户端
/// </summary>
private void Broadcast(Socket sender, string msg)
{
foreach (var client in _clientList)
{
if (client != sender && client.Connected)
{
byte[] data = new MessageBody { Content = msg }.Encode();
client.Send(data);
}
}
}
private void Log(string text)
{
lstLog.Items.Add($"{DateTime.Now:HH:mm:ss} {text}");
}
}
}
3. WinForm 客户端 SocketClient
界面控件:
- ListBox:
lstLog - TextBox:
txtSend - Button:
btnConnect、btnSend
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using SocketCommon;
namespace SocketClient
{
public partial class Form1 : Form
{
private Socket _clientSocket;
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void btnConnect_Click(object sender, EventArgs e)
{
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.Connect(IPAddress.Parse("127.0.0.1"), 8888);
Log("连接服务端成功");
new Thread(ReceiveData) { IsBackground = true }.Start();
}
private void btnSend_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtSend.Text)) return;
byte[] data = new MessageBody { Content = txtSend.Text }.Encode();
_clientSocket.Send(data);
Log($"发送:{txtSend.Text}");
txtSend.Clear();
}
private void ReceiveData()
{
byte[] buffer = new byte[1024 * 1024];
while (true)
{
try
{
int len = _clientSocket.Receive(buffer);
if (len <= 0) break;
string msg = MessageBody.Decode(buffer);
Log($"收到广播:{msg}");
}
catch
{
Log("与服务端断开连接");
break;
}
}
}
private void Log(string text)
{
lstLog.Items.Add($"{DateTime.Now:HH:mm:ss} {text}");
}
}
}
五、运行步骤
- 启动 SocketServer,点击【启动服务】
- 启动 SocketClient,点击【连接服务】
- 输入消息发送,支持多客户端广播
- 自动处理 TCP 粘包,消息不会错乱
六、总结
- TCP 是流协议,必须自定义长度协议解决粘包
- WinForm + Socket 适合做局域网聊天、设备通信
- 多线程接收客户端,保证通信不阻塞界面
- 可扩展:加数据库、登录、心跳包、断线重连
更多推荐


所有评论(0)