使用 Server-Sent Events 和.NET Core 构建实时通信应用程序

随着互联网的普及,实时通信在很多应用场景中已经变得不可或缺。例如,在聊天室应用程序中,用户需要实时收到其他人的消息;在在线协作应用程序中,用户需要实时看到其他人的操作等等。本文将介绍如何使用 Server-Sent Events 和.NET Core 构建一个实时通信的应用程序。

什么是 Server-Sent Events?

Server-Sent Events(简称 SSE)是一种 HTML5 技术,可以使服务端向客户端推送实时数据。它与 WebSocket 不同,WebSocket 通常用于双向通信,而 SSE 主要用于单向通信。SSE 以 HTTP 请求方式(GET)进行交互,服务器将实时事件(event)以块(chunk)的方式发送给客户端。客户端通过 JavaScript 中的 EventSource 对象进行接收和处理。

SSE 的优势

与其他实时通信技术相比,SSE 具有以下优势:

  • SSE 协议是基于 HTTP 的,因此可以通过防火墙和负载均衡器等网络设备,不需要额外的端口或协议,便于部署和维护。
  • SSE 的 API 简单易用,只需要一个 HTTP 请求和一个 EventSource 对象,即可实现实时通信。
  • SSE 内置了自动重连机制,当连接意外中断时,客户端会自动重新连接。
  • SSE 支持自定义事件类型和数据格式,可以根据应用场景进行灵活的配置和扩展。

SSE 的缺点

SSE 的缺点主要有以下几点:

  • SSE 是基于 HTTP 的,因此只能实现单向通信,无法实现双向通信。
  • SSE 虽然支持自动重连,但对于客户端来说,原始连接断开后需要一定时间才能重新连接成功,这可能会导致一定程度的延迟。
  • SSE 不支持二进制数据的传输,只能传输字符串或 JSON 形式的数据。
  • SSE 在某些浏览器上不支持或支持不完整,例如 Internet Explorer。

使用 .NET Core 实现 SSE

在 .NET Core 中,实现 SSE 的方式很简单。只需要在控制器中编写一个返回 SSE 的方法,然后将需要推送的数据通过事件的形式发送给客户端即可。

代码示例

首先,我们来定义一个 SSE 事件模型:

------ ----- --------
-
    ------ ------ ---- - ---- ---- -
    ------ ------ ---- - ---- ---- -
-

其中,Type 字段表示事件类型,可以用来进行数据分类和过滤。Data 字段表示事件的数据内容,可以是任何对象。

接下来,我们在控制器中定义一个 SSE 推送方法:

-------------------
------ ----- ------------------- --------
-
    ------------------------------------ ---------------------
    ------------------------------------- ------------

    --- --- - ----------------------------------------------------------------------

    --- ----- - -------------------------------------

    --- -------- - ----- ----------------------- ----------------------------

    ----- --------- -- -----
    -
        --- ---- - -----------------------------------
        --- ------- - -------- ---------------------- ------------
        ----- -----------------------------

        -------- - ----- ----------------------- ----------------------------
    -

    ------ --- --------------
-

在此方法中,我们首先设置响应的 MIME 类型为 “text/event-stream”,禁用缓存,并获取一个可以处理 SSE 事件的服务(后面会详细介绍)。然后,我们通过 HttpContext.TraceIdentifier 生成一个唯一的 SSE id,以便服务端可以区分不同的 SSE 连接。接着,我们循环接收 SSE 事件,并将事件内容以块的形式发送给客户端,事件类型使用 "event" 字段,数据部分使用 "data" 字段。

最后,返回一个空结果,表示完成响应。

在上述代码中,我们依赖了一个 SseService 服务,用于发送和接收 SSE 事件。下面是 SseService 的实现:

------ ----- ----------
-
    ------- -------- ---------------------------- --------- ------- - --- ---------------------------- ------------
    ------- -------- ------------------- --------

    ------ ------------------------------ -------
    -
        ------- - -------
    -

    ------ ----- ---- ---------------- ----- ------ ----- ----------------- ----------------- - --------
    -
        --- -------- - --- -------- - ---- - ----- ---- - ---- --

        --- ----- - ------------------------------- - -- --- ------------

        ----- ---------------------------- -------------------

        ---------------------------- --- ------ -------- ------
    -

    ------ ----- -------------- ------------------- --- ----------------- ----------------- - --------
    -
        --- ----- - -------------------- - -- --- ------------

        ------ ----- --------------------------------------
    -

    ------- ----- --------
    -
        ------- -------- ------------------------- ------ - --- ----------------------------
        ------- -------- ------------- -------------- - --- -----------------

        ------ ----- ---- --------------------- --------- ----------------- ----------------- - --------
        -
            -------------------------
            -------------------------

            ----- -------------------
        -

        ------ ----- -------------- ------------------------------ ----------------- - --------
        -
            ----- --------------------------------------------

            -- ---------------------- --- ----------
            -
                ------ ---------
            -

            ------ -----
        -
    -
-

此处我们使用了一个 SseService 服务,用于维护 SSE 连接和事件队列。SseService 使用了一个线程安全的 ConcurrentDictionary 来保存事件队列,根据事件类型进行区分。我们通过 SendAsync 方法向队列中添加事件,通过 ReceiveAsync 方法从队列中获取事件。

事件队列使用了一个内部类 SseQueue 来实现,在此类中,我们使用了一个 SemaphoreSlim 对象来实现一个异步的事件队列,可以很轻松地实现事件的排队和出队操作。

示例应用程序

为了更好地演示如何使用.NET Core 和 Server-Sent Events 实现实时通信,我们可以构建一个简单的聊天室应用程序。这个应用程序包括以下几个部分:

  • 前端页面:使用 HTML、JavaScript 和 Bootstrap 框架构建一个简单的聊天室页面,包括一个消息列表和一个输入框,用于发送和接收消息。
  • .NET Core 服务器:使用上述代码示例中的 SSE 服务和控制器实现服务器端的 SSE 推送和接收功能,以及处理客户端发来的消息。
  • SignalR:SignalR 是 .NET Core 中提供的一个实时通信框架,支持双向通信和多种传输协议。在本应用程序中,我们将使用 SignalR 实现双向通信和多人聊天功能。

前端页面

前端页面使用了 Bootstrap 框架,其中包括 CSS 样式、JS 文件和 HTML 页面。其中,我们使用了 js/sse.js 脚本文件,用于连接 SSE 服务,接收服务器端推送的消息。HTML 页面的代码如下:

--------- -----
------
------
    ----- --------------- --
    ----- --------------- ---------------------------- -------------------
    ------------------
    ----- -------------------------------------------------------------------------------------- -----------------
-------
------
    ---- ---------------- ------
        ---- ------------
            ---- ------------
                ------------
            ------
        ------
        ---- ------------
            ---- ------------
                --- ----------------- ---------------------------
            ------
        ------
        ---- ------------
            ---- ------------
                ----- ---------------
                    ---- --------------------
                        ------ ------------------ ----------- -------------------- ------------------ ----------------- --------------------------------
                        ------- ---------- ------------ ------------- ----------------------------
                    ------
                -------
            ------
        ------
    ------
    ------- ----------------------------------------------------------------------------
    ------- ---------------------------------------------------------------------------------------------
    ------- --------------------------
    ------- ---------------------------
-------
-------

在上述代码中,我们定义了一个基本的聊天室界面,包括一个消息列表和一个输入框。我们将会在后面的 JavaScript 脚本中实现与服务器端的连接和消息发送/接收功能。在聊天室页面中,我们需要使用消息列表和输入框中的 ID,以便在处理 JavaScript 事件时能够正确地获取相关元素。

JavaScript 代码

在前端页面中,我们通过 SSE 技术实现服务器端推送消息的功能。本部分讲解如何使用 JavaScript 脚本连接 SSE 服务和接收服务器端的消息。代码如下:

---- --------

--- ---- - -
    ------------ -----

    ----- -------- -- -
        -- --------
        ---------------- - ----------------------------------------

        -- -- --- --
        --- ------ - --- ----------------------------
        ---------------- - ---------------
        -------------- - -------------
        ------------- - -------- -- -
            ---------------------- -- --- ----------
        --
        ------------------------------- -------- -- -
            ----------------- ------------
        ---

        -- -----------
        --- ---- - -------------------------------------
        ------------------------------- ---------------
    --

    ---------- -------- ------- -
        --------------------- ------ - - ------------
        --- ---- - -----------------------
        -- ---------- --- ---------- -
            ---------------------------
        -
    --

    -------- -------- ------- -
        ------------------ ------ - - ------------------
    --

    --------- -------- ------- -
        -----------------------
        --- ----- - -----------------------------------------
        --- ---- - -------------------
        -- ------ -
            -----------------------
            ----------- - ---
        -
        --------------
    --

    ----------- -------- --------- -
        --- -- - -----------------------------
        ------------ - --------
        ---------------------------------
    --

    ------------ -------- ------ -
        -------------------- - -------- ---- -- -------- -- -
            -------------------- ----- - - ------
        ---
    -
--

---------- -- -
    ------------
---

在上述代码中,我们使用了一个 chat 对象,用于处理多个事件。首先,我们获取 HTML 页面中的消息列表元素,以便在接收到新消息时能够正确的渲染到聊天室界面中。

接着,我们使用 JavaScript 的 EventSource 对象连接 SSE 服务。我们将 SSE 服务的 URL 在 JavaScript 程序中硬编码了,实际应用中需要根据当前页面的 URL 动态生成。我们通过 onmessage 事件来处理接收到的 SSE 事件,根据事件类型(“message”)进行判断,将数据内容添加到消息列表中。

我们也可以处理 SSE 的错误事件(onerror ),输出事件的状态编号,以便在调试应用程序时更加方便。在此处,我们使用 onopen 事件和 addEventListener 方法处理一个 ping 事件,以确保 SSE 服务和客户端之间的连接状态。

同时,我们在表单提交事件中处理用户输入的消息内容。当用户点击发送按钮时,在 JavaScript 程序中处理表单提交事件,在向服务端发送用户消息之前,需要先验证消息内容是否为空,如果不为空则通过 POST 方式将消息发送给服务器。

在最后一步,我们实现了三个基本的函数:addMessage、sendMessage 和 init。其中,addMessage 用于添加消息到消息列表,sendMessage 用于发送消息到服务器。init 是 chat 对象的初始化函数,我们通过它来连接 SSE 服务器和注册表单事件。

.NET Core Controller

在服务器端,我们使用 .NET Core 的控制器来处理 SSE 事件和消息发送功能。代码如下:

---------------
-----------------------
------ ----- -------------- - --------------
-
    ------- -------- ----------------------- --------
    ------- -------- ---------- ------------
    ------- -------- ------------- ------------ - --- ---------------

    ------ ------------------------- ----------- ----------------------- -------
    -
        ----------- - -----------
        ------- - -------
    -

    -------------------
    ------ ----- ---- --------
    -
        ------------------------------------ ---------------------
        ------------------------------------- ------------

        --- ----- - -------------------------------------
        ------------------------

        ----- -----------------

        ----- -------------------------------- --------- ---------

        ----- -----------------

        ------- ---- ------- -- -------------
        -
            ----- -------------------------------- ----------- ---------
        -

        --- ----------------- - ---------------------------
        --- -------- - ----- ------------------------------- -------------------

        ----- --------- -- -----
        -
            ------------------------------- -------- -------------- ---------------

            ----- -------------------------------- ---------- ------------------

            -------- - ----- ------------------------------- -------------------
        -
    -

    ------------------
    ------ ----- ------------------- --------------- ------ --------
    -
        -- -------------------------------
        -
            ------ -------------
        -

        ----- -------------------------------- ---------

        ------ -----
    -
-

在上述代码中,我们首先注入了一个 SseService 对象和 ILogger 对象,用于处理 SSE 事件和日志功能。我们也定义了一个连接池(_connections)来保存当前所有连接的 SSE Id。

在控制器中,我们处理了 SSE 的连接请求(Stream 方法),并在连接建立后将客户端的 SSE Id 添加到连接池中。接着,我们使用 Delay 方法模拟几次 SSE 事件发送,将用户上线和加入聊天室的信息发送给所有客户端。

紧接着,我们从 SSE 服务中获取客户端发送的消息,并处理该事件。我们将消息转发给所有客户端。此处,我们使用了 ReceiveAsync 方法从 SSE 服务的队列中读取事件,每次只读取一次,以便其它 SSE 事件可以在此期间得到处理。

在发送消息到服务器的方法(Send 方法)中,我们从 POST 请求的参数中获取消息内容,并验证其是否为空。如果不为空,则通过 SSE 服务将消息发送给所有客户端。

SignalR 消息处理

在聊天室应用程序中,我们希望能够实现多人聊天的功能。因此,我们需要实现双向通信的功能,以便客户端之间能够相互通信。为了实现这个功能,我们将使用 SignalR 框架来处理消息的发送和接收。

首先,我们需要在 .NET Core 应用程序中添加 SignalR 包:

------ --- ------- ---------------------------- --------- ------

接着,在 Startup.cs 文件中,我们注册 SignalR 的服务:

----- -----------------------------
----- -----------------------------
----- -----------------------------------
----- -----------------------------------------
----- -----------------------------

--------- -------
-
    ------ ----- -------
    -
        ------ -------------- ------------- - ---- -

        ------ ---------------------- --------------
        -
            ------------- - --------------
        -

        ------ ---- ------------------------------------ ---------
        -
            ----------------------
            ------------------------------------

            --------------------------
        -

        ------ ---- ----------------------------- ---- ------------------- ----
        -
            -- ---

            -----------------

            -------------------------- --
            -
                ---------------------------
                ----------------------------------------
            ---
        -
    -
-

在本处,我们使用了 SignalR 的 AddSignalR 扩展方法,注册 SignalR 服务。这个方法的作用是注册 SignalR 的相关选项和服务,包括 SignalR 中间件和选项配置项等。我们也使用了 singleton 生命周期添加了一个 SseService 服务,用于管理 SSE 的发送和接收操作。

为了使 SignalR 功能生效,我们在 Configure 方法中调用 UseEndpoints 方法,并在其中添加 MapHub 扩展方法。MapHub 方法的作用是将 SignalR 中心的 URL 映射到当前应用程序的 URL 地址,以便客户端能够连接到正确的中心。

接着,我们定义一个 ChatHub 类,用于处理 SignalR 消息的接收和发送。

----- -----------------------
----- -----------------------------

--------- ------------
-
    ------ ----- ------- - ---
    -
        ------ ----- ---- ------------------ --------
        -
            ----- --------------------------------------- ---------
        -
    -
-

在 ChatHub 类中,我们使用 SignalR 的 Hub 基类来处理消息的接收和发送。我们定义了一个 SendMessage 方法,用于从客户端接收消息,并使用 Clients.All.SendAsync 方法将消息发送到所有连接到 SignalR 中心的客户端。

最后,在前端页面中,我们使用 SignalR 的 JavaScript 客户端来处理消息的接收和发送,代码如下:

--- ---- - -
    ------------ -----
    ----------- -----

    ----- -------- -- -
        -- --------
        ---------------- - ----------------------------------------

        -- -- ------- --
        --------------- - --- ------------------------------
            ----------------------
            ---------

        ------------------------------------ ----------------

        ------------------------------------- -- -
            ---------------------- -- ------- ----------
        ---

        -- -----------
        --- ---- - -------------------------------------
        ------------------------------- ---------------
    --

    ---------- -------- --------- -
        --------------------- -------- - - ---------
        -------------------------
    --

    --------- -------- ------- -
        -----------------------
        --- ----- - -----------------------------------------
        --- ---- - -------------------
        -- ------ -
            -----------------------
            ----------- - ---
        -
        --------------
    --

    ----------- -------- --------- -
        --- -- - -----------------------------
        ------------ - --------
        ---------------------------------
    --

    ------------ -------- ------ -
        ------------------------------------- ------------------- -- -
            -------------------- ----- - - ------
        ---
    -
--

---------- -- -
    ------------
---

此时,我们已经完成了这个简单的聊天室应用程序,包括使用 SSE 技术实现服务器端推送消息,使用 SignalR 实现双向通信和多人聊天功能。

总结

本文介绍了 Server-Sent Events 和.NET Core 的相关知识,以及如何使用两者结合实现一个实时通信的聊天室应用程序。我们使用了 SSE 技术推送消息和 SignalR 实现多人聊天功能,并详细介绍了代码实现和工作原理,希望这些内容能够对读者理解网络通信和实时通信技术有所帮助。在实际应用中,我们可以结合自身需求和技术条件,采用最合适的实时通信技术来实现应用程序。

来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/651d330495b1f8cacd4b98bb


猜你喜欢

  • ECMAScript 2020 (ES11) 新特性解析:globalThis

    ECMAScript 2020 (简称 ES11) 是 JavaScript 的最新版本,已经在 2020 年 6 月正式发布。此版本中包含了许多全新的特性和改进,其中之一就是 globalThis。

    1 年前
  • less 的 loader 在 webpack 中的使用

    什么是 Less? Less 是一种 CSS 预处理器,它扩展了 CSS 语言并提供了许多便利的功能,如变量、Mixin、嵌套规则等。使用 Less 可以更加简洁明了地书写 CSS,使样式表更易于维护...

    1 年前
  • GraphQL 开发中常见的跨域问题及解决方法

    前言 GraphQL 是一种用于 API 的查询语言,它提供了一种更高效、强大和灵活的方式来获取数据。在前端开发中,尤其是在开发单页应用程序时,GraphQL 成为了很多人的选择。

    1 年前
  • Webpack 构建 React 项目,如何处理与服务器的跨域问题

    背景 当我们使用 Webpack 进行 React 项目开发时,经常会遇到与服务器 API 的跨域问题。跨域问题是由于浏览器限制了 JavaScript 访问与当前页面不同源的资源,而产生的一种安全机...

    1 年前
  • Android 性能优化指南

    随着移动设备用户的不断增加,Android 平台上的应用程序要越来越注重性能和用户体验。应用程序的性能不仅影响到用户的使用体验,而且也会影响到应用程序的用户数量和用户留存率。

    1 年前
  • 利用 CSS Grid 实现瀑布流布局的实现方法

    瀑布流布局是一种在网页中呈现图片等元素的方式,通过将元素按照一定顺序分布在网页中,呈现出瀑布流的视觉效果。瀑布流布局一直是前端开发中比较流行的一种技术,而在 CSS Grid 出现后,利用 CSS G...

    1 年前
  • Vue.js2.0 实现 SPA 中实现异步数据渲染的技巧分享

    在现代 web 开发中,单页应用(SPA)的开发模式越来越流行。Vue.js 作为一款轻量级的前端框架,已经成为了许多开发者的首选。在实现 SPA 中异步数据渲染方面,Vue.js2.0 提供了多种方...

    1 年前
  • 理解 ECMAScript 2017(ES8)中新增的 Object.getOwnPropertyDescriptors() 方法及其使用场景

    在 ECMAScript 2017(ES8)版本中,新增了许多有用的语法和方法。其中,Object.getOwnPropertyDescriptors() 方法是一个非常实用的对象操作方法。

    1 年前
  • 《利用 ESLint 中每一个 rule 与 plugin 构建自己的代码规范》

    前端开发在不断迭代的过程中,代码规范是保证代码质量的重要一环。而 ESLint 可以帮助我们规范化,它内置了大量的规则,也可以通过插件来扩展规则。 本文将介绍如何利用 ESLint 中每一个 rule...

    1 年前
  • Socket.io 如何处理断线重新连接的问题

    Socket.io 是一个实现了双向通信的 JavaScript 库,它提供了 WebSocket 和轮询(Polling)两种通信方式,在实时应用程序开发中被广泛应用。

    1 年前
  • 如何使用 Tailwind CSS 添加自定义 CSS 样式到您的 CodeIgniter 应用程序

    在现代 web 应用程序的开发中,前端样式变得越来越重要。Tailwind CSS 是一个流行的 CSS 框架,它可以帮助开发人员快速、轻松地构建自定义样式的 web 应用程序。

    1 年前
  • RxJS 中的操作符分析与使用心得

    前言 珍爱生命,远离 callback hell。所以,RxJS 是前端工程师的好选择。但是,RxJS 中含有大量的操作符,在使用时容易出现一些问题。因此,我们需要对 RxJS 中的操作符进行一定的分...

    1 年前
  • PWA 技术如何通过谷歌分析统计网站数据?

    前言 PWA(Progressive Web App)技术是近年来前端技术发展的一个趋势,它的主要作用是将网页应用转化为类似于原生移动应用的体验。就像原生移动应用一样,PWA技术也需要进行统计网站数据...

    1 年前
  • 在 Cypress 中使用截图进行调试和故障排除

    Cypress 是一个非常易于使用的前端测试工具,它允许你通过简单的 JavaScript 代码来执行端到端测试并获得可靠的结果。然而,有时候测试会失败或运行不稳定,这时就需要使用像截图这样的工具来帮...

    1 年前
  • 「技术教程」使用 Express 构建 RESTful API

    什么是 RESTful API RESTful API 是一种面向资源的 API 设计风格,基于 HTTP 协议,使用 HTTP 方法(GET、POST、PUT、DELETE)来对资源进行操作。

    1 年前
  • 在 Express.js 中使用 Passport.js 和 JSON Web Token(JWT)实现身份验证

    当我们在开发使用 Express.js 的 Web 应用程序时,安全性是一个非常重要且需要考虑的问题。要确保只有经过身份验证的用户才能访问受保护的资源,我们需要使用身份验证和授权来控制用户的访问权限。

    1 年前
  • 如何使用 ECMAScript 2021 中的 BigInt 类型解决 JavaScript 计算溢出问题

    在 JavaScript 中,数值类型是一种非常重要的数据类型,我们经常要对数字进行计算,因为计算过程中可能涉及到非常大的整数,而 JavaScript 中的 Number 类型只能表示 64 位的浮...

    1 年前
  • 在 PM2 中使用环境变量及其优化

    什么是 PM2? PM2 是一个带有负载均衡功能的 Node.js 应用程序的进程管理器。它可以管理 Node.js 的应用程序,常常用于实现进程的守护、自动重启、监控等功能,是 Node.js 生态...

    1 年前
  • React 和 Enzyme 实现动态渲染表单

    前言 在前端开发中,表单是非常常见的组件。由于用户交互的多样性,很难通过几个静态组件来满足不同场景的需求。本文将介绍如何使用 React 和 Enzyme 来动态渲染表单,满足不同场景的需求。

    1 年前
  • Hapi 框架中使用 JWT 实现无状态 API 调用的方法

    在现代 Web 应用开发中,API 服务已经成为了许多应用的核心组成部分。而无状态的 API 服务已经变得越来越流行,因为它们更容易扩展和维护。在这种情况下,使用 JWT(JSON Web Token...

    1 年前

相关推荐

    暂无文章