Web 开发 30

1 HTTP(超文本传输协议)的一个核心局限性

HTTP(超文本传输协议)的一个核心局限性:通信是 "请求 - 响应" 模式,且由客户端(Client)主动发起,服务器(Server)无法 "主动推送" 数据给客户端,除非客户端先发送请求

1. 图中内容拆解

  • 流程逻辑 :客户端(左侧界面)向服务器(右侧象征 "处理 / 响应" 的大脑)发送 GET /api/chat 请求 → 服务器处理后,把 response(响应数据)返回给客户端。
  • 核心限制 :服务器不能在**"客户端没发请求"** 的情况下,主动给客户端发数据。比如,若服务器有新消息要推送给客户端,也必须等客户端先发起下一次请求,才能把新消息塞到 "响应" 里返回。

2. 拓展:HTTP 这种局限性带来的问题

实时性要求高的场景中,这种 "客户端必须主动轮询(反复发请求)" 的模式会很尴尬,典型场景如:

  • 即时通讯(IM) :像在线聊天,如果依赖 HTTP,客户端得每隔几秒发一次请求("轮询" 服务器有没有新消息)。这会导致:
    • 实时性差:新消息要等 "下一次轮询请求" 才能拿到,可能延迟几秒。
    • 资源浪费:即使没新消息,客户端也得反复发请求,消耗服务器和网络资源。
  • 实时协作工具:比如多人在线文档,用户 A 改了内容,用户 B 要 "实时看到改动",若用纯 HTTP,也得靠 B 不断轮询,体验和效率都不好。

3. 怎么解决这个局限性?

为了突破 HTTP 的 "被动响应" 限制,后来出现了很多实时通信技术,比如:

  • WebSocket :它是一种 "全双工" 通信协议,建立连接后,客户端和服务器能双向主动发消息。服务器可以主动推数据给客户端,不需要客户端反复发请求,特别适合实时聊天、实时数据同步等场景。
  • Server - Sent Events(SSE):服务器可以 "单向主动" 给客户端推送事件 / 数据,适合 "服务器推、客户端少发" 的场景(比如新闻推送)。
  • 长轮询(Long Polling):是对 HTTP 的 "折中优化"------ 客户端发请求后,服务器若没有数据,会 " hold 住请求不立刻响应",直到有数据或超时,再返回响应;客户端收到响应后,立刻再发新的请求,模拟 "长连接" 效果。但本质还是基于 HTTP 的请求 - 响应,效率和实时性不如 WebSocket。

简单说,HTTP 的 "请求 - 响应、客户端主导" 模式,在静态网页时代问题不大,但面对现代 web 应用的实时性需求(如即时聊天、实时监控),就需要更灵活的通信方式来补充或替代。

2 WebSocket 实现 "广播" 的原理

拆解两张图的逻辑:

第一张图:"发起消息" 的流程
  • 角色:Fluffy(代表一个客户端)要发消息。
  • 动作:Fluffy 通过 POST /api/message "meow"(HTTP 请求)把消息 "meow" 发给服务器(中间大脑图标)。
  • 目的:这一步是客户端向服务器提交消息,和之前 HTTP 的 "请求 - 响应" 类似,但重点在后续服务器怎么把消息 "广播" 给所有人。
第二张图:"广播消息" 的流程
  • 角色:服务器(中间大脑 + 喇叭,象征 "主动推送")。
  • 动作:服务器通过 WebSocket(图中标注 (ws) ,把新消息 new msg "meow" 主动推送给所有在线的客户端(Fluffy、Whiskers、Joe 对应的界面)。
  • 核心:WebSocket 建立连接后,服务器能主动 "广播" 数据给多个客户端,不需要客户端反复发 HTTP 请求,完美解决了 HTTP 无法主动推送的局限性。

拓展理解:

  • Socket 是更底层的 "套接字" 概念,支持双向通信;而 WebSocket 是基于 Socket 的应用层协议,专门为 Web 实时通信设计(比如网页版聊天、实时协作)。
  • 对比 HTTP:HTTP 是 "客户端问,服务器答";WebSocket 是 "一旦连好,双方想发就发",所以能高效实现 "服务器广播消息给所有客户端" 的场景。

简单说,这两张图用 "猫咪发消息,服务器广播给所有猫咪" 的例子,生动展示了 WebSocket 如何突破 HTTP 限制,实现实时广播。

3 WebSocket(通常结合 Socket.IO 类库)实现 "服务器向所有已连接客户端广播消息" 以及 "客户端监听消息" 的基础用法

逐部分解释:

1. 服务器端:广播消息给所有连接的客户端
  • 代码示例:socketManager.getIo().emit("event_name", data)

    • socketManager.getIo():获取 WebSocket 的核心管理对象(比如 Socket.IO 中的 io 对象,负责管理所有客户端连接)。图中 "We coded this for you!" 表示这部分 "获取连接管理器" 的逻辑已经帮你写好了,不用关心细节。
    • emit("event_name", data)
      • event_name:是事件名称(相当于 "消息的标题"),用来标识这是哪一类消息(比如 "新聊天消息""系统通知")。
      • data:是要发送的数据,可以是对象、列表、字符串等任意格式(比如聊天内容、用户信息)。
    • 效果:服务器通过这个方法,把带有 event_namedata 的消息,广播给所有当前连接到服务器的客户端
  • 配图(大脑 + 喇叭):象征 "服务器像用喇叭广播一样,把消息推送给所有人"。

2. 客户端:监听服务器发来的消息
  • 代码示例:socket.on("event_name", someFunction)

    • socket:客户端的 WebSocket 实例(代表 "当前客户端与服务器的连接")。
    • on("event_name", someFunction)
      • event_name:要监听的事件名称 ,必须和服务器端 emit 时用的 event_name 一致(相当于 "只听标题匹配的消息")。
      • someFunction:是一个回调函数,当客户端收到 "event_name 对应的消息" 时,就会执行这个函数。图中解释 "what to do when you get a socket emit of that title will look like (data) => { do something with data }",意思是:这个函数的形式通常是 (data) => { /* 用 data 做一些事 */ }------ 也就是拿到服务器发来的 data,执行后续逻辑(比如把聊天消息显示在页面上)。
  • 配图(耳朵 + 猫咪):象征 "客户端像用耳朵听一样,接收服务器广播的消息"。

核心逻辑总结

  • 服务器是 "广播者":用 emit 把带 "标题(event_name)" 和 "内容(data)" 的消息,推送给所有客户端。
  • 客户端是 "听众":用 on 监听特定 "标题(event_name)" 的消息,收到后执行自定义逻辑(处理 data)。

这种模式让 Web 应用能实现实时互动(比如聊天时,A 发消息,服务器广播给 B、C 等所有在线用户,大家能立刻看到),弥补了 HTTP "只能客户端主动问,服务器被动答" 的不足。

【web运行的端口被监听,动态消息会被统一广播websocket转发到所有客户端,这个图想表达这个吗?】

你的理解方向是对的,这张图和你描述的逻辑是一脉相承 的,不过图更聚焦于 "WebSocket 广播与监听的代码级用法",而你说的是更宏观的 "端口监听 + 消息流转" 逻辑,我们可以结合起来看:

1. 图的核心:WebSocket 广播与监听的 "代码怎么写"

图里详细讲了服务器如何广播消息客户端如何监听消息具体 API 用法

  • 服务器端:用 socketManager.getIo().emit("event_name", data) 把消息(带 "事件名 event_name" 和 "数据 data")广播给所有连接的客户端。
  • 客户端:用 socket.on("event_name", someFunction) 监听 "事件名匹配" 的消息,收到后执行 someFunction 处理数据。

2. 你描述的:"端口监听 + 消息流转" 的宏观逻辑

  • "Web 运行的端口被监听":服务器要先监听端口(比如 3000 端口),这样客户端才能通过这个端口与服务器建立 WebSocket 连接。
  • "动态消息会被统一广播 WebSocket 转发到所有客户端":当服务器有动态消息时,就会通过 WebSocket(用图里的 emit 方法)把消息转发给所有已连接的客户端。

3. 两者的关系:"宏观逻辑" 是背景,"图" 是实现它的 "代码手段"

  • 首先,服务器得 "监听端口",让客户端能连上 → 这是基础前提(你描述的部分)。
  • 然后,当需要广播消息时,服务器用图里的 emit 方法发消息 → 这是广播的 "执行动作"
  • 最后,客户端用图里的 on 方法监听消息 → 这是客户端接收的 "执行动作"

简单说,你描述的是 "为什么要这么做 + 整体流程",图是 "具体怎么用代码实现这个流程里的'广播 / 监听'环节"。

4 实时聊天应用前后端小结

这两张图分别从前端(Frontend)和后端(Backend) ,总结了一个实时聊天应用的核心结构与逻辑,我们分开详细说:

一、前端(Frontend)总结图:组件结构 + 数据模型

这部分讲的是 "用户能看到的界面,是由哪些组件组成的?这些组件之间怎么传递数据?聊天相关的数据长什么样?"

1. 组件层级与数据传递(左侧流程图)
  • App(根组件) :整个应用的最顶层组件。
    • 传递 Props: {userId: String}:把 "用户 ID(字符串类型)" 作为属性(Props),传递给下一层组件 Chatbook
  • Chatbook 组件 :管理 "聊天簿" 的逻辑,比如当前激活的聊天是哪一个。
    • 自身状态 State: {activeChat: ChatData}:用 State(状态)存储 "当前激活的聊天数据(ChatData 类型)"。
    • 传递 Props: {data: ChatData}:把 "聊天数据(ChatData 类型)" 作为属性,传递给下一层组件 Chat
  • Chat 组件 :承载具体的 "聊天窗口" 逻辑,会分支渲染两个子组件。
    • 传递 Props: {message: MessageObj}SingleMessage:把 "单条消息数据(MessageObj 类型)" 传给 SingleMessage,用于渲染历史消息。
    • 传递 Props: {recipient: UserObj}NewMessage:把 "接收者数据(UserObj 类型)" 传给 NewMessage,用于新消息的发送(比如指定发给谁)。
  • SingleMessage 组件 :渲染单条历史消息(比如你截图右侧聊天框里的每一条旧消息)。
  • NewMessage 组件 :处理新消息的输入与发送(比如截图右侧的 "输入框 + 发送按钮")。
2. 数据模型定义(右侧文字)

这部分解释了 "聊天相关的核心数据结构长什么样",是前端渲染和交互的 "数据依据":

  • ChatData :表示 "一次聊天的整体数据",包含:
    • messages: Array of MessageObjs:这是一个数组,里面每个元素都是 MessageObj 类型(存储这条聊天里的所有消息)。
    • recipient: A UserObj:表示 "这次聊天的接收者"(UserObj 类型),也就是 "我们正在和谁聊天"。
  • MessageObj :表示 "单条消息",包含:
    • sender: A UserObj:消息的发送者(UserObj 类型)。
    • content: A String:消息的内容(字符串类型,比如 "hello")。
  • UserObj :表示 "用户",包含:
    • _id: id (stored as string):用户的唯一标识(字符串类型,比如 "user123")。
    • name: A String:用户名(字符串类型,比如 "小明")。
3. 右侧界面截图

这是前端最终渲染出的聊天界面示例 :能看到 "聊天列表(左侧 Open Chats)" 和 "具体聊天窗口(右侧 Chatting with ALL CHAT)",里面的消息就是通过上面的组件和数据模型渲染出来的~

二、后端(Backend)总结图:接口与逻辑

这部分讲的是 "服务器端提供了哪些 API 接口,用来支持前端的聊天功能?"

1. 接口定义(右侧代码)

后端用类似 Express(Node.js 的 web 框架)的语法,定义了两个核心接口:

  • router.get("/chat", (req, res) => { ... })
    • 作用:// Get all of the messages → 处理获取所有聊天消息的请求。
    • 当前端发起 GET /chat 请求时,后端会执行这个回调函数((req, res) => { ... }),从数据库等地方拿取消息数据,再通过 res(响应对象)返回给前端。
  • router.post("/message", (req, res) => { ... })
    • 作用:// Send a message to everyone → 处理发送消息并广播给所有人的请求。
    • 当前端发起 POST /message 请求(通常是用户点击 "发送" 按钮时),后端会执行这个回调函数,把新消息存起来,再通过 WebSocket 等方式广播给所有在线客户端(结合之前讲的 WebSocket 广播逻辑)。
2. 左侧配图(大脑 + "server" 文字)

象征 "服务器像大脑一样,处理前端的请求,管理消息的存储与广播"~

三、前后端结合的整体逻辑

  1. 前端通过组件分层渲染界面,并用 ChatDataMessageObjUserObj 定义数据格式,实现 "聊天界面展示 + 新消息输入"。
  2. 前端通过 HTTP 请求(比如 GET /chat)从后端拿历史消息,渲染到 SingleMessage;通过 POST /message 把新消息发给后端。
  3. 后端通过 GET /chat 接口提供历史消息,通过 POST /message 接口接收新消息并广播(结合 WebSocket),让所有在线用户实时看到新消息。

这样一套流程,就实现了 "多用户实时聊天" 的核心功能。

相关推荐
@PHARAOH3 小时前
WHAT - 前端性能指标(交互和响应性能指标)
前端·交互
噢,我明白了3 小时前
前端js 常见算法面试题目详解
前端·javascript·算法
Jonathan Star3 小时前
Webpack 打包优化与骨架屏结合:双管齐下提升前端性能与用户体验
前端·webpack·ux
码事漫谈3 小时前
LLVM IR深度技术解析:架构、优化与应用
后端
码事漫谈3 小时前
C++ 中的类型转换:深入理解 static_cast 与 C风格转换的本质区别
后端
做好一个小前端3 小时前
后端接口获取到csv格式内容并导出,拒绝乱码
前端·javascript·html
第七种黄昏3 小时前
前端面试-箭头函数
前端·面试·职场和发展
Youyzq3 小时前
前端box-shadow出现兼容性问题如何处理
前端
携欢3 小时前
PortSwigger靶场之将 XSS 存储到onclick带有尖括号和双引号 HTML 编码以及单引号和反斜杠转义的事件中通关秘籍
前端·html·xss