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# Double转化为String时的保留位数及格式方式

    下面是详细的讲解“C# Double转化为String时的保留位数及格式方式”的完整攻略。 格式化字符串 在 C# 中,可以使用 string.Format() 或 $”” 来将 double 类型转化为字符串。这两种方式都可以通过格式化字符串来进行保留位数及格式的设置。 保留固定位数 保留 double 类型变量小数点后的固定位数有两种方式: 第一种,使用…

    C# 2023年6月8日
    00
  • C#通过HttpWebRequest发送带有JSON Body的POST请求实现

    C#通过HttpWebRequest发送带有JSON Body的POST请求实现,一般包括以下几个步骤: 1. 引入命名空间 在 C# 文件中,需要引入 System.Net 命名空间,代码如下: using System.Net; 2. 创建 POST 请求对象 使用 HttpWebRequest 类创建 POST 请求对象,代码如下: HttpWebRe…

    C# 2023年5月31日
    00
  • asp.net中资源文件的使用

    当我们开发ASP.NET应用程序时,使用多语言资源文件是一种良好的实践。本文将为你介绍ASP.NET应用程序中资源文件的用法。 资源文件的定义和分类 资源文件是什么? 资源文件(Resource File)是指保存一个或多个文本字符串、图像、音频或其他类型数据的文本文件。 .NET Framework 提供了一种能够以有组织的方式存储、访问和管理资源的方式,…

    C# 2023年5月31日
    00
  • C#实现俄罗斯方块基本功能

    C#实现俄罗斯方块基本功能的攻略可以分为以下几个步骤: 第一步:搭建基本框架 创建一个控制台应用程序项目,或者一个 WPF/WinForms 窗体应用程序项目。 在项目中创建 Block 类,该类代表一个俄罗斯方块方块。 在项目中创建 GameBoard 类,该类代表整个游戏面板。 在项目中创建 GameManager 类,该类负责游戏逻辑,如方块下落、旋转…

    C# 2023年6月1日
    00
  • C#实现发送手机验证码功能

    一、生成验证码 使用Random类生成随机数字或字母,示例代码如下: Random random = new Random(); string code = ""; for (int i = 0; i < 6; i++) { code += random.Next(10); } 将随机生成的验证码保存到Session中,代码如下: …

    C# 2023年6月6日
    00
  • 使用Linq注意事项避免报错的方法

    使用Linq时要注意以下几点,以避免在代码中出现错误: 1. 空引用异常 在使用Linq时,一定要注意空引用异常,这通常是因为查询结果为 null,或者结果集中的某些数据为 null。 解决此问题的方法是,先要用 null 检查语句来确保在使用结果集中的某些属性时,结果集不为空。可以使用 ?? 运算符来处理 null 异常。 以下是一个示例代码,可以用于处理…

    C# 2023年5月14日
    00
  • C#使用linq计算执行元素在列表中出现次数的方法

    下面是使用LINQ计算执行元素在列表中出现次数的方法的完整攻略。 标题 C#使用LINQ计算执行元素在列表中出现次数的方法 内容 一般来说,计算元素在列表中出现的次数是一个很常见的需求,下面我们就介绍如何使用LINQ对列表进行查询,以计算元素在列表中出现的次数。 1. 使用GroupBy方法进行分组 要计算元素在列表中出现的次数,我们可以通过先将列表按照元素…

    C# 2023年6月1日
    00
  • C# 如何调用C++ dll string类型返回

    C# 调用 C++ DLL 的过程中,若遇到需要返回 string 类型的情况,可以使用字符缓冲区来传递字符串,并通过指针参数来返回。 以下为详细步骤: 定义 C++ 端的 DLL 接口函数 在 C++ 中,需要定义一个导出函数用于将 C# 中的字符串传递到 DLL 中,例如以下代码段: // Example.cpp extern "C"…

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