在C#网络编程里,判断网络是否掉线不能只依赖Socket的Connected属性,因为该属性只反映上次操作的态,无法感知网线被拔或远端静默崩溃。更可靠的做法是引入心跳包检测机制,即双方定期互发少量探测数据,依据是否收到回应来判定链路健康。

为什么需要心跳包
TCP协议本身有保活机制,但默认超时时间往往长达两小时,无法满足实时业务需求。通过应用层心跳包,我们可以自定义间隔与超时阈值,快速发现掉线并触发重连或资源清理。
基于TcpClient的简单实现
下面示例展示一个客户端定时发送心跳并在指定时间内未收到回应时判定掉线的核心逻辑。
using System;
using System.Net.Sockets;
using System.Threading;
using System.Text;
class HeartbeatClient
{
private TcpClient client;
private NetworkStream stream;
private Timer heartbeatTimer;
private DateTime lastReceiveTime;
private readonly object lockObj = new object();
public void Start(string ip, int port)
{
client = new TcpClient();
client.Connect(ip, port);
stream = client.GetStream();
lastReceiveTime = DateTime.Now;
// 启动接收线程
Thread recvThread = new Thread(ReceiveLoop);
recvThread.IsBackground = true;
recvThread.Start();
// 每3秒发送一次心跳
heartbeatTimer = new Timer(SendHeartbeat, null, 0, 3000);
}
private void SendHeartbeat(object state)
{
try
{
byte[] data = Encoding.ASCII.GetBytes("PING");
stream.Write(data, 0, data.Length);
}
catch
{
Console.WriteLine("发送心跳失败,连接可能已断开");
}
// 检查超时:超过6秒未收到任何数据视为掉线
lock (lockObj)
{
if ((DateTime.Now - lastReceiveTime).TotalSeconds > 6)
{
Console.WriteLine("心跳超时,网络已掉线");
// 此处可执行重连逻辑
}
}
}
private void ReceiveLoop()
{
byte[] buffer = new byte[1024];
try
{
while (true)
{
int len = stream.Read(buffer, 0, buffer.Length);
if (len == 0) break;
lock (lockObj)
{
lastReceiveTime = DateTime.Now;
}
string msg = Encoding.ASCII.GetString(buffer, 0, len);
if (msg == "PONG")
{
Console.WriteLine("收到服务端心跳回应");
}
}
}
catch
{
Console.WriteLine("接收线程异常,连接断开");
}
}
}
关键注意事项
- 心跳间隔与超时时间应根据业务容忍度设定,一般超时为间隔的2到3倍。
- 多线程访问lastReceiveTime等共享字段时必须加锁,避免竞态。
- 掉线后应及时关闭Socket并释放NetworkStream,防止句柄泄露。
- 若使用异步async/await,可用CancellationToken控制心跳任务的启停。
服务端如何配合
服务端在收到PING后应回写PONG,同时也可主动向客户端发心跳以检测死连接。若连续多次未收到某客户端心跳,则可从在线列表中移除并关闭对应Socket。
心跳包内容应尽量小,避免占用带宽,通常使用固定字符串或单字节即可。
使用Ping类做辅助检测
有时可在应用层心跳之外,用System.Net.NetworkInformation.Ping对网关做ICMP探测,但要注意部分环境禁用了ICMP,因此不能单独依赖它判断程序连接状态。
using System.Net.NetworkInformation;
public bool GatewayReachable(string host)
{
Ping ping = new Ping();
try
{
PingReply reply = ping.Send(host, 1000);
return reply.Status == IPStatus.Success;
}
catch
{
return false;
}
}
总结
C#判断网络掉线的核心在于主动探测而非被动等待。心跳包机制以低成本方式补足了TCP感知延迟的缺陷,配合合理的超时与线程管理,可显著提升网络通信的稳健性。