一、心跳策略(Heartbeat)
原理:客户端定期向服务端发送心跳包,服务端监控客户端存活状态,超时未收到心跳则判定客户端离线
- 服务端代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Threading;
public class HeartbeatServer
{
private TcpListener _listener;
private Dictionary<string, DateTime> _clients = new Dictionary<string, DateTime>();
public void Start(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
Console.WriteLine("Heartbeat Server started...");
// 启动心跳监控线程
new Thread(MonitorClients).Start();
while (true)
{
var client = _listener.AcceptTcpClient();
new Thread(() => HandleClient(client)).Start();
}
}
private void HandleClient(TcpClient client)
{
string clientId = client.Client.RemoteEndPoint.ToString();
Console.WriteLine($"Client connected: {clientId}");
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream))
{
try
{
while (true)
{
var message = reader.ReadLine();
if (message == "HEARTBEAT")
{
lock (_clients)
{
_clients[clientId] = DateTime.Now; // 更新最后心跳时间
}
}
}
}
catch
{
Console.WriteLine($"Client disconnected: {clientId}");
lock (_clients) { _clients.Remove(clientId); }
client.Close();
}
}
}
private void MonitorClients()
{
while (true)
{
Thread.Sleep(5000); // 每5秒检查一次
lock (_clients)
{
foreach (var client in _clients.Keys.ToList())
{
if ((DateTime.Now - _clients[client]).TotalSeconds > 10) // 超时10秒
{
Console.WriteLine($"Client timeout: {client}");
_clients.Remove(client);
}
}
}
}
}
}
- 客户端代码
using System;
using System.Net.Sockets;
using System.Threading;
public class HeartbeatClient
{
private TcpClient _client;
private Thread _heartbeatThread;
public void Connect(string serverIp, int port)
{
_client = new TcpClient();
_client.Connect(serverIp, port);
Console.WriteLine("Connected to server.");
// 启动心跳线程
_heartbeatThread = new Thread(SendHeartbeat);
_heartbeatThread.Start();
}
private void SendHeartbeat()
{
var writer = new StreamWriter(_client.GetStream()) { AutoFlush = true };
while (true)
{
try
{
writer.WriteLine("HEARTBEAT");
Thread.Sleep(2000); // 每2秒发送一次
}
catch
{
Console.WriteLine("Connection lost.");
break;
}
}
}
}
二、Ping/Echo 策略
原理:客户端主动发送 Ping 请求,服务端必须回复 Echo 响应,超时未响应则判定服务端离线。
- 客户端代码
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class PingEchoClient
{
private TcpClient _client;
private Timer _pingTimer;
public void Connect(string ip, int port)
{
_client = new TcpClient();
_client.Connect(ip, port);
Console.WriteLine("Connected to server.");
// 每2秒发送一次Ping
_pingTimer = new Timer(SendPing, null, 0, 2000);
}
private void SendPing(object state)
{
NetworkStream stream = _client.GetStream();
byte[] pingMsg = Encoding.UTF8.GetBytes("PING");
byte[] buffer = new byte[1024];
try
{
// 发送Ping并等待Echo(超时1秒)
stream.Write(pingMsg, 0, pingMsg.Length);
Console.WriteLine("Sent PING.");
var cts = new CancellationTokenSource(1000); // 超时1秒
int bytesRead = stream.ReadAsync(buffer, 0, buffer.Length, cts.Token).Result;
string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);
if (response == "ECHO")
{
Console.WriteLine("Received ECHO.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Server timeout: {ex.Message}");
Environment.Exit(1);
}
}
public static void Main()
{
var client = new PingEchoClient();
client.Connect("127.0.0.1", 8081);
Thread.Sleep(Timeout.Infinite); // 保持连接
}
}
2.服务端代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
public class PingEchoServer
{
private TcpListener _listener;
private bool _isRunning = true;
public void Start(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
Console.WriteLine("Ping/Echo Server started...");
while (_isRunning)
{
TcpClient client = _listener.AcceptTcpClient();
ThreadPool.QueueUserWorkItem(HandleClient, client);
}
}
private void HandleClient(object obj)
{
TcpClient client = (TcpClient)obj;
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
try
{
while (true)
{
int bytesRead = stream.Read(buffer, 0, buffer.Length);
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
if (message == "PING")
{
byte[] response = Encoding.UTF8.GetBytes("ECHO");
stream.Write(response, 0, response.Length);
Console.WriteLine("Responded to PING.");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Client disconnected: {ex.Message}");
client.Close();
}
}
public static void Main()
{
var server = new PingEchoServer();
server.Start(8081);
}
}
三、两种策略对比
四、运行说明
心跳策略
启动 HeartbeatServer 和 HeartbeatClient。
停止服务端后,客户端将在5秒内检测到超时。
Ping/Echo策略
启动 PingEchoServer 和 PingEchoClient。
停止服务端后,客户端下一次Ping会触发超时。
五、扩展建议
心跳策略优化:可结合 UDP 广播减少连接数,或使用 WebSocket 协议。
Ping/Echo增强:添加序列号或时间戳,防止重放攻击。
集群化:将客户端扩展为多节点,服务端通过负载均衡(如Nginx)分发请求。