心跳策略(Heartbeat) 和 Ping/Echo 策略

发布于:2025-05-09 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、心跳策略(Heartbeat)
原理:客户端定期向服务端发送心跳包,服务端监控客户端存活状态,超时未收到心跳则判定客户端离线

  1. 服务端代码
    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);
                }
            }
        }
    }
}

}

  1. 客户端代码
    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 响应,超时未响应则判定服务端离线。

  1. 客户端代码
    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)分发请求。


网站公告

今日签到

点亮在社区的每一天
去签到