c# socket心跳超时检测的思路(适用于超大量TCP连接情况下)

让我来详细讲解C# Socket心跳超时检测的思路和实现方法。

什么是心跳超时检测?

在Socket编程中,心跳超时检测就是指客户端和服务端之间保持网络连接的一种机制。当客户端和服务端之间的网络连接闲置一段时间后,为了避免网络连接被认为已经中断,我们需要在一定时间间隔内发送心跳数据包来维持网络连接。如果在规定的时间内没有收到心跳数据包,就意味着网络连接已经中断,就需要重新建立连接。

实现思路

  • 在服务器端,创建一个容器,存储所有心跳正常的客户端连接对象;
  • 在客户端和服务端之间,定期交换心跳数据包,以维持网络连接;
  • 在服务端,开启一个定时器,定期检测容器中的客户端连接是否已经超时,如果超时,就将这些连接从容器中剔除并关闭连接。

代码实现

在服务器端

public class Server
{
    private List<Socket> _clients = new List<Socket>();    // 存储所有可用的客户端连接
    private Timer _timer;   // 定时器,用于定期检测心跳是否超时

    public Server()
    {
        // 在构造函数中初始化定时器
        _timer = new Timer(OnTimerTick, null, 1000, 1000);
    }

    private void OnTimerTick(object state)
    {
        lock (_clients)
        {
            // 检测每个客户端是否已经超时,如果超时,则关闭连接
            foreach (var client in _clients)
            {
                UserData userData = client.Tag as UserData;    // 获取客户端连接对象的自定义数据
                if (userData != null && DateTime.Now.Subtract(userData.LastActiveTime).TotalSeconds > 30)
                {
                    Console.WriteLine($"{client.RemoteEndPoint}已经超时,强制关闭连接");
                    client.Close();
                }
            }

            // 从容器中删除已经关闭的连接
            _clients.RemoveAll(c => !c.Connected);
        }
    }

    private void OnClientConnected(IAsyncResult ar)
    {
        Socket client = _server.EndAccept(ar);
        lock (_clients)
        {
            _clients.Add(client);
        }

        Console.WriteLine($"{client.RemoteEndPoint}已经连接");

        StartReceive(client);   // 开始接收客户端发送的数据
        StartSend(client);      // 开始向客户端发送数据
    }

    private void StartReceive(Socket client)
    {
        // 在Socket连接中设置自定义数据
        UserData userData = new UserData();
        userData.Buffer = new byte[1024];
        userData.Socket = client;
        userData.LastActiveTime = DateTime.Now;
        client.Tag = userData;

        try
        {
            client.BeginReceive(userData.Buffer, 0, userData.Buffer.Length, SocketFlags.None, OnDataReceived, client);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取数据出错:{ex.Message}");
            client.Close(); // 出现异常关闭连接
        }
    }

    private void OnDataReceived(IAsyncResult ar)
    {
        UserData userData = ar.AsyncState as UserData;
        if (userData == null) return;

        try
        {
            int length = userData.Socket.EndReceive(ar);

            // 如果接收到的数据长度为0表示连接已经断开
            if (length <= 0)
            {
                Console.WriteLine($"{userData.Socket.RemoteEndPoint}已经断开连接");
                userData.Socket.Close();
                return;
            }

            userData.LastActiveTime = DateTime.Now;  // 更新客户端连接的最近活动时间

            string data = Encoding.UTF8.GetString(userData.Buffer, 0, length);
            Console.WriteLine($"接收到客户端({userData.Socket.RemoteEndPoint})的数据:{data}");

            // 处理接收到的数据...

            // 继续从客户端接收数据
            userData.Socket.BeginReceive(userData.Buffer, 0, userData.Buffer.Length, SocketFlags.None, OnDataReceived, userData);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取数据出错:{ex.Message}");
            userData.Socket.Close();    // 出现异常关闭连接
        }
    }

    private void StartSend(Socket client)
    {
        // 给客户端发送心跳数据包
        byte[] buffer = Encoding.UTF8.GetBytes("Heartbeat");

        while (client.Connected)
        {
            if (client.Poll(0, SelectMode.SelectWrite))
            {
                try
                {
                    client.Send(buffer);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"发送数据出错:{ex.Message}");
                    client.Close(); // 出现异常关闭连接
                    return;
                }
            }
            Thread.Sleep(3000); // 每隔3秒发送一次心跳数据包
        }
    }

    public void Start()
    {
        // 在本机的8080端口监听客户端连接
        _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _server.Bind(new IPEndPoint(IPAddress.Any, 8080));
        _server.Listen(10);

        Console.WriteLine("服务器已经启动,等待客户端连接...");

        // 开始接受客户端连接
        _server.BeginAccept(OnClientConnected, null);
    }
}

public class UserData
{
    public Socket Socket { get; set; }              // 当前连接的Socket对象
    public byte[] Buffer { get; set; }              // 保存从客户端接收到的数据
    public DateTime LastActiveTime { get; set; }    // 记录最近一次活动时间
}

在客户端

public class Client
{
    private Socket _client; // 客户端Socket对象
    private Timer _timer;   // 定时器,用于定期发送心跳数据包

    public Client()
    {
        _timer = new Timer(OnTimerTick, null, 1000, 1000);
    }

    private void OnTimerTick(object state)
    {
        try
        {
            // 给服务端发送心跳数据包
            byte[] buffer = Encoding.UTF8.GetBytes("Heartbeat");
            _client.Send(buffer);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"发送心跳数据出错:{ex.Message}");
            _client.Close(); // 出现异常关闭连接
        }
    }

    public void Connect(string ip, int port)
    {
        // 连接到指定的服务器端口
        _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        try
        {
            _client.Connect(ip, port);
            Console.WriteLine($"连接到服务端({ip}:{port})成功");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接到服务端({ip}:{port})失败:{ex.Message}");
            return;
        }

        StartReceive(); // 开始接受服务端发送的数据
        StartSend();    // 开始向服务端发送数据
    }

    private void StartReceive()
    {
        // 创建自定义数据对象,用于保存和处理客户端接收到的数据
        UserData userData = new UserData();
        userData.Buffer = new byte[1024];
        userData.Socket = _client;

        try
        {
            // 开始接收服务端发送的数据
            _client.BeginReceive(userData.Buffer, 0, userData.Buffer.Length, SocketFlags.None, OnDataReceived, userData);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取数据出错:{ex.Message}");
            _client.Close(); // 出现异常关闭连接
        }
    }

    private void OnDataReceived(IAsyncResult ar)
    {
        UserData userData = ar.AsyncState as UserData;
        if (userData == null) return;

        try
        {
            int length = userData.Socket.EndReceive(ar);

            // 如果数据的长度为0表示服务端已经断开连接
            if (length <= 0)
            {
                Console.WriteLine($"服务端({userData.Socket.RemoteEndPoint})已经断开连接");
                userData.Socket.Close();
                return;
            }

            userData.LastActiveTime = DateTime.Now;  // 更新客户端连接的最近活动时间

            string data = Encoding.UTF8.GetString(userData.Buffer, 0, length);
            Console.WriteLine($"接收到服务端数据:{data}");

            // 处理接收到的数据...

            // 继续从服务端接收数据
            userData.Socket.BeginReceive(userData.Buffer, 0, userData.Buffer.Length, SocketFlags.None, OnDataReceived,
                userData);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"读取数据出错:{ex.Message}");
            userData.Socket.Close();    // 出现异常关闭连接
        }
    }

    private void StartSend()
    {
        // 向服务端发送数据
        string data = "Hello World";
        byte[] buffer = Encoding.UTF8.GetBytes(data);

        while (_client.Connected)
        {
            if (_client.Poll(0, SelectMode.SelectWrite))
            {
                try
                {
                    _client.Send(buffer);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"发送数据出错:{ex.Message}");
                    _client.Close(); // 出现异常关闭连接
                    return;
                }
            }
            Thread.Sleep(2000); // 每隔2秒向服务端发送一次数据
        }
    }
}

public class UserData
{
    public Socket Socket { get; set; }              // 当前连接的Socket对象
    public byte[] Buffer { get; set; }              // 保存从客户端接收到的数据
    public DateTime LastActiveTime { get; set; }    // 记录最近一次活动时间
}

在上述代码中,客户端和服务端都在定时器中定期发送心跳数据包来维持连接,而服务端在定时器中还要检测是否有连接超时的情况。对于客户端,可以在定时器中定期发送心跳数据包,如果发送数据出错,说明连接已经中断,就可以关闭客户端连接。对于服务端,在定时器中定期检测客户端连接的最近活动时间,如果超过指定的时间,就认为该客户端已经超时,就可以从服务器容器中移除该客户端并关闭连接,以释放服务器资源。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c# socket心跳超时检测的思路(适用于超大量TCP连接情况下) - Python技术站

(0)
上一篇 2023年6月1日
下一篇 2023年6月1日

相关文章

  • C#目录和文件管理操作详解

    C#目录和文件管理操作详解 概述 在C#中,我们可以通过System.IO命名空间下的类来实现对目录和文件的管理操作。其中,常用的类有: File:用于对文件进行操作的类,包含文件的创建、复制、删除、移动、读取、写入等方法。 Directory:用于对目录进行操作的类,包含目录的创建、删除、移动、获取目录信息等方法。 Path:用于对路径进行操作的类,包含获…

    C# 2023年5月15日
    00
  • jQuery获取地址栏参数插件(模仿C#)

    jQuery获取地址栏参数插件(模仿C#)是一个可以方便的获取URL参数的工具。下面是详细的攻略: 1. 插件的引入 首先需要在页面中引入jQuery库,然后才能引入该插件。可以使用以下代码: <script src="https://code.jquery.com/jquery-3.5.1.min.js"></scri…

    C# 2023年5月15日
    00
  • 基于C#的图表控件库 ScottPlot编译visual studio 2022

    ScottPlot是什么? ScottPlot是一个可视化绘图的C#控件库,提供了丰富的图表类型和交互式功能。ScottPlot基于.NET Core 3.0和.NET Framework 4.6.1开发,支持WinForms、WPF以及控制台程序等多种应用类型。 ScottPlot的安装 在Visual Studio 2022中安装ScottPlot有两种…

    C# 2023年6月3日
    00
  • 基于C#实现简单离线注册码生成与验证

    前言本篇攻略将介绍如何使用C#语言实现一个简单的离线注册码生成器与验证器,可适用于需要对软件进行授权的情形,如商业软件、企业内部软件等。首先我们将讲解注册码的生成方法,然后是注册码的验证方法,最后我们会给出完整的示例说明,希望这篇攻略对大家有所帮助。 一、注册码生成器的实现注册码生成器主要包括两个功能:生成唯一的机器码和根据机器码生成注册码。下面是代码实现:…

    C# 2023年6月6日
    00
  • C#如何遍历Dictionary

    C#提供了许多方法,可以对Dictionary进行遍历操作。下面是三个常见的遍历方式: 1. 使用foreach循环遍历Dictionary Dictionary<string, int> dict = new Dictionary<string, int>(); // 添加元素 dict.Add("a", 1);…

    C# 2023年6月1日
    00
  • 武装你的WEBAPI-OData Versioning

    本文属于OData系列 目录 武装你的WEBAPI-OData入门 武装你的WEBAPI-OData便捷查询 武装你的WEBAPI-OData分页查询 武装你的WEBAPI-OData资源更新Delta 武装你的WEBAPI-OData之EDM 武装你的WEBAPI-OData常见问题 武装你的WEBAPI-OData使用Endpoint 武装你的WEBAP…

    C# 2023年5月5日
    00
  • OData WebAPI实践-OData与EDM

    本文属于 OData 系列 引言 在 OData 中,EDM(Entity Data Model) 代表“实体数据模型”,它是一种用于表示 Web API 中的结构化数据的格式。EDM 定义了可以由 OData 服务公开的数据类型、实体和关系。 EDM 也提供了一些规则来描述数据模型中的实体之间的关系,例如继承、关联和复合类型。EDM 是 OData 协议的…

    C# 2023年5月11日
    00
  • c#异常处理示例分享

    接下来我将分享一下关于C#异常处理的示例,包括异常处理的基本概念、常见异常类型以及如何捕获、处理异常的方法。本文共包含以下内容: 前言 异常是指在程序执行过程中出现的错误或异常情况。C#中通过异常处理机制来处理这些异常情况。一旦程序运行出现异常,程序就会停止执行,并在控制台输出相应的错误信息。 异常处理的基本概念 在C#中,异常处理机制的核心就是try-ca…

    C# 2023年5月15日
    00
合作推广
合作推广
分享本页
返回顶部