最直接方式是用tcpclient和tcplistener配对通信,注意连接管理、超时控制、流读写判断及消息边界处理。

用 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是高性能场景专用,需手动复用对象、管理缓冲区,普通业务没必要上
真正的难点不在语法,而在如何安全地在线程间传递连接上下文、如何设计消息边界、以及断线后重连时的状态清理——这些不会报编译错误,但会让程序在高并发下悄无声息地丢数据或卡死。











