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#实现将程序锁定到Win7任务栏的方法

    下面是“C#实现将程序锁定到Win7任务栏的方法”的完整攻略: 前言 Win7任务栏可以将运行的程序任务以缩略图的形式展示在任务栏上,方便用户随时切换不同的任务,提高工作效率。一般情况下,我们可以在任务栏上固定自己常用的程序,以便下次快速启动。但有些时候,我们需要将我们的程序直接锁定到任务栏上,方便用户一键启动我们的程序。本篇攻略将讲解如何使用C#实现这一功…

    C# 2023年6月7日
    00
  • 浅析如何截获C#程序产生的日志

    浅析如何截获C#程序产生的日志 在处理C#程序的开发过程中,我们通常会遇到需要对程序产生的日志进行截获的情况,这有助于我们更好地掌握程序的执行情况,进行问题排查和优化。那么如何进行日志截获呢?下面我将以两个示例来分别说明。 示例1: 使用log4net进行日志输出 首先,我们需要在程序中引入log4net。在Visual Studio中,可以通过以下步骤来实…

    C# 2023年6月6日
    00
  • js内存泄露的几种情况详细探讨

    JS内存泄露的几种情况详细探讨 什么是内存泄露 内存泄漏指的是一个无用的对象仍然存在于内存中,因此该对象占用的内存无法被回收。在一个长时间运行的应用程序中,内存泄漏可能会导致内存耗尽并导致应用程序崩溃。 在JS中,有很多常见的情况会导致内存泄漏,下面将详细探讨几种情况。 几种常见的内存泄露情况 1. 意外的全局变量 意外的全局变量可能是最常见的内存泄漏场景。…

    C# 2023年6月7日
    00
  • .Net Winform开发笔记(四)透过现象看本质

    接下来我将详细讲解“.Net Winform开发笔记(四)透过现象看本质”的完整攻略。 攻略概述 本篇攻略主要介绍如何透过现象看本质进行Winform开发,内容分为三个部分: 理解Winform的本质: 认识Winform的结构 理解Winform的生命周期 常用Winform控件的本质: 理解各种Winform控件的特点和用途 能够选择正确的控件实现需要的…

    C# 2023年5月31日
    00
  • C#:使用ffmpeg将图片合并成视频

      最近遇到公司的一个项目,需要将多张图片合并成一个播放的视频,找了很多资料和尝试了工具,遇到很多的坑,这里记下来,希望大家也能顺利解决遇到的问题。   合并视频,主要可以借用OpenCV 和 ffmpeg,这里是尝试用ffmpeg.exe的工具去实现图片文件合并成视频。   输入存储视频文件的路径,通过ProcessStartInfo 调用ffmpeg.e…

    C# 2023年4月30日
    00
  • 详解Java中的checked异常和unchecked异常区别

    详解Java中的checked异常和unchecked异常区别 Java中的异常类型可以分为两种:checked异常和unchecked异常。两种异常的区别主要在于程序编译时期是否必须进行异常处理。 checked异常: checked异常即编译器在编译Java程序时检查出的异常,通常与I/O操作和网络连接相关。程序在编译时必须要强制进行处理,这意味着这些异…

    C# 2023年5月15日
    00
  • C# 向二进制文件进行读写的操作方法

    C# 向二进制文件进行读写的操作方法 在 C# 中,我们可以通过 FileStream 和 BinaryWriter/BinaryReader 类来进行二进制文件的读写操作。 1. 二进制文件写入操作示例 string fileName = "test.dat"; using (FileStream fs = new FileStream…

    C# 2023年6月1日
    00
  • asp.net(c#) RSS功能实现代码

    ASP.NET(C#)中实现RSS功能一般可分为以下步骤: 第一步:准备数据 RSS需要的数据格式一般是XML,所以我们需要准备好相应的XML数据。在ASP.NET中可以使用Linq to XML技术来生成XML,下面是一个示例代码: XElement rss = new XElement("rss", new XAttribute(&q…

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