基于WebSocket实现的后台服务

这篇具有很好参考价值的文章主要介绍了基于WebSocket实现的后台服务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

基于WebSocket实现的后台服务,用于接收客户端的心跳消息,并根据心跳消息来维护客户端连接。

具体实现中,服务启动后会创建一个HttpListener对象,用于监听客户端的WebSocket连接请求。当客户端连接成功后,服务会为每个连接创建一个Task实例,用于接收客户端发送的心跳消息,并根据心跳消息更新心跳时间戳。服务还会定期向客户端发送心跳消息,以保持连接的活跃状态。

如果服务在一定时间内没有收到客户端发送的心跳消息,就会认为客户端已经掉线,服务会关闭连接并从连接列表中移除该客户端。

此服务适用于需要实现长连接的场景,例如实时消息推送、在线游戏等。需要注意的是,此服务只能用于WebSocket通信,客户端必须实现WebSocket协议。文章来源地址https://www.toymoban.com/news/detail-603765.html

using Microsoft.Extensions.Hosting;
using MSEBP.Kernel.Common.Logging;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Authorization.WebApi
{
    /// <summary>
    /// 此代码只能用于 websocket通信,客户端必须websocket实现,暂时无用。
    /// </summary>
    public class WebSocketBackgroundService : IHostedService, IDisposable
    {
        private const int _heartBeatInterval = 30000; // 心跳间隔(毫秒)
        private const int _heartBeatTimeout = 60000; // 心跳超时时间(毫秒)
        private const int _clientIdLength = 10;

        private readonly CancellationTokenSource _cts = new CancellationTokenSource();
        private readonly ConcurrentDictionary<string, WebSocket> _clients = new ConcurrentDictionary<string, WebSocket>();
        private readonly ILogger _logger;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="logger"></param>
        public WebSocketBackgroundService(ILogger logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task StartAsync(CancellationToken cancellationToken)
        {
            IPAddress localIp = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
            if (localIp == null)
            {
                throw new Exception("Cannot find local IP address.");
            }

            IPEndPoint localEndPoint = new IPEndPoint(localIp, 8181);
            HttpListener listener = new HttpListener();
            //listener.Prefixes.Add($"http://{localEndPoint}/");
            listener.Start();

            _ = Task.Run(async () =>
            {
                try
                {
                    while (!_cts.IsCancellationRequested)
                    {
                        HttpListenerContext context = await listener.GetContextAsync();
                        if (context.Request.IsWebSocketRequest)
                        {
                            WebSocket webSocket = await AcceptWebSocketAsync(context);
                            _ = Task.Run(async () =>
                            {
                                await ReceiveHeartbeatAsync(webSocket);
                            }, _cts.Token);
                        }
                        else
                        {
                            context.Response.StatusCode = 400;
                            context.Response.Close();
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error(ex, "WebSocket server error.");
                }
            }, _cts.Token);
        }

        private async Task<WebSocket> AcceptWebSocketAsync(HttpListenerContext context)
        {
            HttpListenerWebSocketContext wsContext = await context.AcceptWebSocketAsync(null);
            WebSocket webSocket = wsContext.WebSocket;
            return webSocket;
        }

        private async Task ReceiveHeartbeatAsync(WebSocket webSocket)
        {
            byte[] buffer = new byte[1024];
            CancellationToken token = _cts.Token;
            DateTime lastHeartbeatTime = DateTime.UtcNow;

            try
            {
                while (webSocket.State == WebSocketState.Open && !token.IsCancellationRequested)
                {
                    WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

                    if (result.CloseStatus.HasValue)
                    {
                        await CloseWebSocketAsync(webSocket, result.CloseStatus.Value, result.CloseStatusDescription);
                        break;
                    }
                    else if (result.MessageType == WebSocketMessageType.Text)
                    {
                        string message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim();
                        if (message.StartsWith("heartbeat"))
                        {
                            lastHeartbeatTime = DateTime.UtcNow;
                            string clientId = message.Substring(0, Math.Min(message.Length, _clientIdLength));
                            _clients.TryAdd(clientId, webSocket);
                        }
                        else if (string.IsNullOrEmpty(message))
                        {
                            await CloseWebSocketAsync(webSocket, WebSocketCloseStatus.NormalClosure, "Closed by client");
                            break;
                        }
                        else
                        {
                            // 处理业务逻辑
                        }
                    }

                    // 检测心跳超时
                    if ((DateTime.UtcNow - lastHeartbeatTime).TotalMilliseconds > _heartBeatTimeout) 
                    { 
                        await CloseWebSocketAsync(webSocket, WebSocketCloseStatus.NormalClosure, "Heartbeat timeout");
                        break;
                    }
                }
            }
            catch (WebSocketException ex) when (ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely)
            {
                // WebSocket 连接被意外关闭,忽略异常
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "WebSocket error.");
            }
            finally
            {
                // 移除客户端连接
                foreach (var item in _clients)
                {
                    if (item.Value == webSocket)
                    {
                        _clients.TryRemove(item.Key, out _);
                        break;
                    }
                }
                await CloseWebSocketAsync(webSocket, WebSocketCloseStatus.NormalClosure, "Closed by server");
            }
        }

        private async Task CloseWebSocketAsync(WebSocket webSocket, WebSocketCloseStatus closeStatus, string closeStatusDescription)
        {
            try
            {
                await webSocket.CloseAsync(closeStatus, closeStatusDescription, CancellationToken.None);
            }
            catch (WebSocketException ex) when (ex.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely)
            {
                // WebSocket 连接已经关闭,忽略异常
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Failed to close WebSocket.");
            }
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            _cts.Cancel();
            await Task.CompletedTask;
        }

        public void Dispose()
        {
            _cts.Dispose();
        }
    }
}

到了这里,关于基于WebSocket实现的后台服务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++实现websocket服务端客户端(基于boost,亲测可行!)

       整篇文章基本参考了https://blog.csdn.net/jianghuan0122/article/details/123528907,文章记录了如何在现有条件下实现该参考示例(参考示例存在报错,并且参考示例没有介绍环境安装,正确源码附于文末)    自身环境:ubuntu18.04+gcc7.5.0+boost1.7,3   gcc或者g++一般都有,这里主要介绍

    2024年02月11日
    浏览(32)
  • 如何使用websocket+node.js实现pc后台与小程序端实时通信

    实现功能:实现pc后台与小程序端互发通信能够实时检测到 1.安装ws依赖 2.创建index.js 3.打开终端,启动服务 这里是手动点击连接按钮,发起的websocket连接,可自行更改到其他合适的地方连接websocket 1.创建两个按钮,连接按钮,发送按钮 2.定义事件,连接ws

    2024年02月03日
    浏览(35)
  • 两种实现WebSocket的方式,基于Java实现WebSocket。

    首先我们声明WebSocker方便打字为ws。 WebSocker ws = new WebSocket(); 或者说启用spring框架,因为spring已经整合了ws。 配置类:把spring中的ServerEndpointEx porter对象注入进来 用iava注解来 @ServerEndpoint          监听连接、 @OnOpen          连接成功、 @OnClose        连接关闭、 @

    2024年01月21日
    浏览(34)
  • 使用JMeter测试基于WebSocket协议的服务

    示例:WebSocket是一种双向网络通信协议,与HTTP不同,它以ws://或wss://开头。它是一个有状态协议,这意味着客户端和服务器之间的连接将保持活动状态,直到被客户端或服务器中的任何一方关闭连接之后,连接将从两端终止。 初次接触 WebSocket的人,都会问同样的问题:我们

    2024年02月06日
    浏览(42)
  • WebSocket:基于 Spring Cloud 配置注解实现 WebSocket 集群方案

    上一篇:WebSocket 的具体介绍与内部执行原理 WebSocket 大家应该是再熟悉不过了,如果是单体应用确实不会有什么问题,但是当我们的项目使用微服务架构时,就可能会存在问题 比如 服务A 有两个实例 A1 和 A2 ,前端的 WebSocket 客户端 C 通过网关的负载均衡连到了 A1 ,这个时候

    2024年02月10日
    浏览(35)
  • websocket基于java实现

    随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出, 它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据 。 我们知道,传统的HTTP协议是无状态

    2024年02月06日
    浏览(39)
  • 服务端(后端)主动通知前端的实现:WebSocket(springboot中使用WebSocket案例)

    我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有两种主流实现方式: 轮询:缺点很多,但是实现简单 websocket:在浏览器和服务器之间建立 tcp 连接,实现全双工通信 springboot 使用 websocket 有

    2023年04月14日
    浏览(58)
  • Golang实现简单WebSocket服务

    我们每天接触到各类应用,如社交、在线文档、直播等,后端都需要使用WebSocket技术提供实时通信能力。本文介绍如何使用Golang实现实时后端WebSocket服务,首先使用Gin框架搭建http服务,然后使用 gorilla/websocket 库实现简单后端WebSocket服务,示例实现从0到1的过程,适合初学者快

    2024年02月16日
    浏览(32)
  • C#搭建WebSocket服务实现通讯

    在学习使用websocket之前我们先了解一下websocket: WebSocket是一种在单个TCP连接上进行全双工通信的通信协议。与HTTP协议不同,它允许服务器主动向客户端发送数据,而不需要客户端明确地请求。这使得WebSocket非常适合需要实时或持续通信的应用程序,例如在线聊天、实时游戏

    2024年02月07日
    浏览(25)
  • Go 实现 Websocket 服务以及代理

    WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。Websocket 主要用在B/S架构的应用程序中,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建

    2024年02月20日
    浏览(18)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包