C# 完整 TCP Server 详细实现(多线程/异步两种方案)
方案1:Async 异步高性能TCP服务(推荐,.NET Core/.NET 5+/Framework通用)
基于 Socket 异步API,支持多客户端并发、消息分包处理、心跳检测、优雅断开,工业/服务端标准写法。
完整服务端代码
csharp
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace TcpAsyncServer
{
/// <summary>
/// TCP客户端会话封装
/// </summary>
public class TcpClientSession
{
#region 基础属性
public Socket ClientSocket { get; }
public string ClientIp { get; }
public int ClientPort { get; }
public DateTime ConnectTime { get; }
private readonly byte[] _buffer;
private const int BufferSize = 4096; // 缓冲区4K,可按需调整
private readonly TcpAsyncServer _server;
#endregion
public TcpClientSession(Socket clientSocket, TcpAsyncServer server)
{
ClientSocket = clientSocket;
_server = server;
var ep = (IPEndPoint)clientSocket.RemoteEndPoint;
ClientIp = ep.Address.ToString();
ClientPort = ep.Port;
ConnectTime = DateTime.Now;
_buffer = new byte[BufferSize];
Console.WriteLine($"【新客户端接入】{ClientIp}:{ClientPort}");
}
#region 异步接收消息循环
public async Task StartRecvLoopAsync()
{
try
{
while (ClientSocket.Connected)
{
// 异步读取数据
int readLen = await ClientSocket.ReceiveAsync(new ArraySegment<byte>(_buffer), SocketFlags.None);
if (readLen <= 0)
{
// 客户端主动关闭连接
break;
}
// 截取有效数据
byte[] recvData = new byte[readLen];
Array.Copy(_buffer, recvData, readLen);
// 处理收到的消息(业务逻辑入口)
await ProcessRecvDataAsync(recvData);
}
}
catch (SocketException ex)
{
Console.WriteLine($"【客户端异常断开】{ClientIp}:{ClientPort} 错误:{ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"【接收数据异常】{ClientIp}:{ClientPort} {ex}");
}
finally
{
// 释放资源,从服务端会话池移除
Disconnect();
}
}
#endregion
#region 业务数据处理逻辑
private async Task ProcessRecvDataAsync(byte[] data)
{
// 示例:UTF8字符串解析,可替换为自定义协议(Modbus/私有包头长度协议)
string recvStr = Encoding.UTF8.GetString(data);
Console.WriteLine($"【收到{ClientIp}:{ClientPort}】{recvStr}");
// 回显测试:原样返回客户端
byte[] response = Encoding.UTF8.GetBytes($"服务端已收到:{recvStr}");
await SendDataAsync(response);
}
#endregion
#region 异步发送数据
public async Task SendDataAsync(byte[] data)
{
if (!ClientSocket.Connected || data == null || data.Length == 0)
return;
try
{
await ClientSocket.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
}
catch (Exception ex)
{
Console.WriteLine($"【发送数据失败】{ClientIp}:{ClientPort} {ex.Message}");
Disconnect();
}
}
#endregion
#region 断开连接释放资源
public void Disconnect()
{
if (!ClientSocket.Connected) return;
try
{
ClientSocket.Shutdown(SocketShutdown.Both);
}
catch { }
ClientSocket.Close();
_server.RemoveSession(this);
Console.WriteLine($"【客户端断开】{ClientIp}:{ClientPort} 在线时长:{(DateTime.Now - ConnectTime).TotalSeconds:F2}秒");
}
}
/// <summary>
/// TCP服务端主类
/// </summary>
public class TcpAsyncServer
{
#region 配置与字段
private Socket _listenSocket;
private readonly int _port;
private readonly IPAddress _listenIp;
// 线程安全存储所有在线客户端会话
private readonly ConcurrentDictionary<TcpClientSession, bool> _clientSessions = new ConcurrentDictionary<TcpClientSession, bool>();
private CancellationTokenSource _cts;
public bool IsRunning => _listenSocket != null && _listenSocket.IsBound;
#endregion
/// <summary>
/// 构造TCP服务
/// </summary>
/// <param name="listenIp">监听IP,IPAddress.Any监听所有网卡</param>
/// <param name="port">监听端口</param>
public TcpAsyncServer(IPAddress listenIp, int port)
{
_listenIp = listenIp;
_port = port;
}
#region 启动服务
public async Task StartServerAsync()
{
if (IsRunning)
{
Console.WriteLine("服务已在运行,无需重复启动");
return;
}
_cts = new CancellationTokenSource();
// 创建监听Socket,IPv4,流式TCP
_listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 端口复用,快速重启服务不占用端口
_listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
_listenSocket.Bind(new IPEndPoint(_listenIp, _port));
// 最大挂起连接数100,可调整
_listenSocket.Listen(100);
Console.WriteLine($"=====================================");
Console.WriteLine($"TCP异步服务启动成功");
Console.WriteLine($"监听地址:{_listenIp}:{_port}");
Console.WriteLine($"=====================================");
// 持续异步接受客户端连接
_ = AcceptClientLoopAsync(_cts.Token);
await Task.CompletedTask;
}
#endregion
#region 循环接受客户端连接
private async Task AcceptClientLoopAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
// 异步等待客户端连接
Socket clientSocket = await _listenSocket.AcceptAsync(token);
// 创建会话对象
TcpClientSession session = new TcpClientSession(clientSocket, this);
// 加入会话池
_clientSessions.TryAdd(session, true);
// 启动该客户端的接收循环(异步后台运行)
_ = session.StartRecvLoopAsync();
}
catch (OperationCanceledException)
{
// 服务停止,正常退出循环
break;
}
catch (Exception ex)
{
Console.WriteLine($"【接受客户端异常】{ex.Message}");
await Task.Delay(100); // 异常短暂休眠防止死循环
}
}
}
#endregion
#region 会话池管理
public void RemoveSession(TcpClientSession session)
{
_clientSessions.TryRemove(session, out _);
Console.WriteLine($"当前在线客户端总数:{_clientSessions.Count}");
}
/// <summary>
/// 广播消息给所有在线客户端
/// </summary>
public async Task BroadcastAllAsync(byte[] data)
{
foreach (var session in _clientSessions.Keys)
{
await session.SendDataAsync(data);
}
}
#endregion
#region 停止服务,释放全部资源
public void StopServer()
{
if (!IsRunning) return;
// 取消监听循环
_cts?.Cancel();
// 关闭监听Socket
_listenSocket?.Close();
_listenSocket = null;
// 断开所有在线客户端
foreach (var session in _clientSessions.Keys)
{
session.Disconnect();
}
_clientSessions.Clear();
_cts?.Dispose();
Console.WriteLine("TCP服务已完全停止");
}
#endregion
}
// 程序入口测试
class Program
{
static async Task Main(string[] args)
{
// 监听本机所有网卡,端口8899
var tcpServer = new TcpAsyncServer(IPAddress.Any, 8899);
await tcpServer.StartServerAsync();
Console.WriteLine("输入exit关闭服务");
while (true)
{
string input = Console.ReadLine();
if (input.Equals("exit", StringComparison.OrdinalIgnoreCase))
{
tcpServer.StopServer();
break;
}
// 输入其他内容可作为广播消息
byte[] broadcastData = Encoding.UTF8.GetBytes($"服务端广播:{input}");
_ = tcpServer.BroadcastAllAsync(broadcastData);
}
}
}
}
方案2:传统TcpListener同步多线程(简单入门,适合小型工具)
封装简单,逻辑直观,适合新手学习基础TCP流程
csharp
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace TcpSyncServerDemo
{
class SyncTcpServer
{
private readonly TcpListener _listener;
private bool _isRun = false;
private readonly List<TcpClient> _clientList = new List<TcpClient>();
private readonly object _lockObj = new object();
public SyncTcpServer(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
}
public void Start()
{
if (_isRun) return;
_isRun = true;
_listener.Start();
Console.WriteLine($"同步TCP服务启动,端口:{((IPEndPoint)_listener.LocalEndpoint).Port}");
// 开启新线程循环接收客户端
Thread acceptThread = new Thread(AcceptClientLoop);
acceptThread.IsBackground = true;
acceptThread.Start();
}
private void AcceptClientLoop()
{
while (_isRun)
{
try
{
TcpClient client = _listener.AcceptTcpClient();
lock (_lockObj)
{
_clientList.Add(client);
}
IPEndPoint clientEp = (IPEndPoint)client.Client.RemoteEndPoint;
Console.WriteLine($"客户端接入 {clientEp.Address}:{clientEp.Port}");
// 每个客户端单独线程处理收发
Thread clientThread = new Thread(ClientDataHandle);
clientThread.IsBackground = true;
clientThread.Start(client);
}
catch
{
if (!_isRun) break;
}
}
}
private void ClientDataHandle(object obj)
{
TcpClient client = obj as TcpClient;
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[4096];
int readLen;
try
{
while ((readLen = stream.Read(buffer, 0, buffer.Length)) > 0)
{
string msg = Encoding.UTF8.GetString(buffer, 0, readLen);
Console.WriteLine($"收到消息:{msg}");
// 回发数据
byte[] resp = Encoding.UTF8.GetBytes($"服务端应答:{msg}");
stream.Write(resp, 0, resp.Length);
}
}
catch
{
IPEndPoint ep = (IPEndPoint)client.Client.RemoteEndPoint;
Console.WriteLine($"客户端断开 {ep.Address}:{ep.Port}");
}
finally
{
lock (_lockObj)
{
_clientList.Remove(client);
}
client.Close();
}
}
public void Stop()
{
_isRun = false;
_listener.Stop();
lock (_lockObj)
{
foreach (var client in _clientList)
{
client.Close();
}
_clientList.Clear();
}
Console.WriteLine("服务已停止");
}
static void Main(string[] args)
{
SyncTcpServer server = new SyncTcpServer(8899);
server.Start();
Console.WriteLine("输入stop关闭服务");
while (Console.ReadLine() != "stop") { }
server.Stop();
}
}
}
关键功能说明
1. 异步方案优势(生产推荐)
- IO多路复用:异步Socket无阻塞等待,上万客户端连接仅消耗少量线程
- 线程安全会话池 :
ConcurrentDictionary管理在线客户端,支持广播群发 - 资源自动释放:客户端异常/主动断开自动回收Socket,无句柄泄漏
- 可扩展私有协议 :
ProcessRecvDataAsync可修改为 包头+长度+数据 工业标准协议
2. 核心扩展改造点
(1)自定义分包协议(解决TCP粘包)
TCP是流式协议,多条消息会粘连,推荐固定4字节包头标识数据长度:
csharp
// 发送时封装包头
byte[] body = Encoding.UTF8.GetBytes("测试消息");
byte[] header = BitConverter.GetBytes(body.Length);
byte[] sendAll = header.Concat(body).ToArray();
await session.SendDataAsync(sendAll);
// 接收时解析长度,完整读取一条消息
(2)心跳检测
在 StartRecvLoopAsync 增加超时计时,长时间无数据主动断开无效连接。
(3)日志替换
代码中Console.WriteLine可替换为log4net/NLog日志组件,落地日志文件。
3. 运行测试
- 编译运行程序,监听端口8899
- 测试工具:NetAssist、Telnet、自己写C# TcpClient客户端
- 发送字符串,服务端自动回显,输入
exit关闭服务
4. 跨版本兼容
- .NET Framework 4.5+:完全支持异步Socket API
- .NET Core 3.1 / .NET5/6/7/8:原生支持,无兼容性问题
- 可部署Windows服务、Linux(.NET跨平台)