C# TCP通信方法 C#如何实现TCP Socket编程

来源:这里教程网 时间:2026-02-21 17:42:11 作者:

TcpClient
TcpListener
快速建立连接

最直接的方式是用 .NET 封装好的高层类,避开原始

Socket
的细节。它们底层仍基于
Socket
,但自动处理地址解析、连接状态管理等常见逻辑。

客户端用

TcpClient
,服务端用
TcpListener
,两者配对使用即可通信。注意:一个
TcpClient
实例只对应一个连接;
TcpListener
启动后需调用
AcceptTcpClient()
AcceptSocket()
才真正接收连接。

服务端监听必须指定
IPAddress.Any
或具体 IP,不能用
localhost
字符串(会解析失败)
TcpClient.Connect()
是同步阻塞的,超时默认无限等待,建议改用带超时的重载或配合
CancellationToken
读写前务必检查
NetworkStream.CanRead/CanWrite
,断连后流可能仍可读但返回 0 字节
var listener = new TcpListener(IPAddress.Any, 8080);
listener.Start();
using var client = listener.AcceptTcpClient(); // 阻塞直到有连接
using var stream = client.GetStream();
byte[] buffer = new byte[1024];
int len = stream.Read(buffer, 0, buffer.Length); // 返回实际读取字节数

手动管理
Socket
时必须处理的三个状态点

原始

Socket
更灵活,但也更易出错。关键不是“怎么发”,而是“连接是否还活着”“对方是否已关闭”“缓冲区是否满”。这三个状态不显式判断,程序大概率在收发时卡死或抛异常。

典型错误现象:

Socket.Receive()
返回 0 表示对端已关闭连接,不是“没数据”;
Socket.Send()
返回值小于请求长度,说明内核发送缓冲区已满,需重试;
Socket.Connected
属性不可靠,它只反映最后一次 I/O 操作后的状态,不是实时连接状态。

永远用
Send()
Receive()
的返回值判断实际传输量,别假设一次调用就完成全部数据
检测对端关闭:当
Receive()
返回 0,应主动
Shutdown(SocketShutdown.Both)
Close()
避免轮询
Connected
,改用异步方法(如
BeginReceive
)或
Poll()
+
SelectMode.SelectRead
判断可读性

NetworkStream
不能直接用于大文件传输

TcpClient.GetStream()
返回的
NetworkStream
默认无缓冲,且不支持
Seek()
Length
。如果直接拿它套
BinaryReader
StreamReader
读长消息,容易因粘包或半包导致解析失败。

常见场景:发送 JSON 或自定义协议消息时,必须约定长度前缀(如 4 字节 int 表示后续内容长度),否则接收方无法知道一次消息何时结束。

不要依赖
NetworkStream.Read()
一次性读完整条消息——它只保证至少读 1 字节,最多读缓冲区剩余空间
发送前先写长度头,再写内容;接收时先读 4 字节长度,再循环读够指定字节数 若用
StreamReader.ReadLine()
,确保发送端每行末尾是
\r\n
,且未禁用
AutoFlush
// 发送带长度头的消息
var data = Encoding.UTF8.GetBytes("hello");
var header = BitConverter.GetBytes(data.Length);
stream.Write(header, 0, 4);
stream.Write(data, 0, data.Length);

异步操作中别混用
async/await
和回调模式

.NET 提供两套 API:基于

BeginXXX/EndXXX
的 APM 模式和基于
XXXAsync
的 TAP 模式。二者底层机制不同,混用会导致资源泄漏或未触发回调。

例如,在

TcpClient
上调用
BeginConnect()
后,不能再对同一实例调用
ConnectAsync()
;同样,用
Socket.AcceptAsync()
初始化的
SocketAsyncEventArgs
,不能拿去传给
SendAsync()

新项目统一用 TAP(
ConnectAsync
ReadAsync
WriteAsync
),它与
async/await
天然契合
APM 模式仅用于维护旧代码,且必须配对使用
BeginXxx
EndXxx
SocketAsyncEventArgs
是高性能场景专用,需手动复用对象、管理缓冲区,普通业务没必要上

真正的难点不在语法,而在如何安全地在线程间传递连接上下文、如何设计消息边界、以及断线后重连时的状态清理——这些不会报编译错误,但会让程序在高并发下悄无声息地丢数据或卡死。

相关推荐