news 2026/9/5 19:10:54

freeCodeCamp 实战教程:在 Advanced Node and Express 课程中用 Socket.IO 处理断线事件(Handle a Disconnect)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
freeCodeCamp 实战教程:在 Advanced Node and Express 课程中用 Socket.IO 处理断线事件(Handle a Disconnect)

freeCodeCamp 实战教程:在 Advanced Node and Express 课程中用 Socket.IO 处理断线事件(Handle a Disconnect)

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本篇基于 freeCodeCamp 开源仓库中advanced-node-and-express课程的 "Handle a Disconnect" 挑战文档,讲解 Socket.IO 实时应用中一个最容易被忽视的细节:客户端断开连接时,服务端如何感知并同步在线用户数。读完本篇,你将掌握socket.on('disconnect')的注册位置与监听层级、在线计数(currentUsers)的增减配对逻辑,以及该项目测试断言如何校验你的实现。

课程背景:这一步在整个聊天项目中的位置

"Handle a Disconnect" 是 advanced-node-and-express 挑战块 中的第 18 个挑战(id:589fc831f9fc0f352b528e76),该块隶属于 Quality Assurance 认证(见 quality-assurance 超块结构)。整个挑战块后半段(第 17–21 题)共同构成一个逐步搭起来的实时聊天室项目,Handle a Disconnect正处于承上启下的关键位置:

  1. Set up the Environment:用http.createServer(app)创建原生 HTTP 服务器,通过require('socket.io')(http)挂载 Socket.IO,并在服务端io.on('connection', socket => { ... })中监听connection事件,客户端则用let socket = io();建立连接;
  2. Communicate by Emitting:定义let currentUsers = 0;,在每次新用户连接时++currentUsers,然后用io.emit('user count', currentUsers)把最新计数广播给所有客户端,客户端用socket.on('user count', ...)接收;
  3. Handle a Disconnect(本文主题):补上"断开"这一半,让计数在用户离开时正确减一;
  4. Authentication with Socket.IO:用passport.socketio中间件解析 session cookie,使socket.request.user可用,从而知道"是谁"连接;
  5. Announce New Users:把 connect/disconnect 两个事件统一改名为'user'事件,携带usernamecurrentUsersconnected三个字段,完成"xx 已加入/离开聊天室"的广播;
  6. Send and Display Chat Messages:完成聊天消息的收发与展示。

核心概念:disconnect 事件必须挂在 socket 上,而不是 io 上

原始文档(589fc831f9fc0f352b528e76.md)给出的核心论点是:

处理用户断开(disconnect)和处理初始连接(connect)一样简单,区别在于 disconnect 必须监听在每一个 socket 上,而不是整个 server 上

这正是 Socket.IO 事件模型的分层结构:

  • io.on('connection', socket => { ... }):服务器级事件。每当有新的客户端建立连接时触发一次,回调参数socket代表这一个新连接的客户端。它回答的是"谁来了";
  • socket.on('disconnect', ...):socket 级事件。Socket.IO 会在每个 socket 自身上发出disconnect事件,回答的是"这个特定的客户端走了"。

因为"断开"是某个具体连接的生命周期终点,所以它的监听器只能注册在socket对象上。从文档给出的注册位置要求看,这个监听器要写在现有的'connect'监听器内部——因为只有在connection回调的作用域里,socket变量才可用:

io.on('connection', socket => { // 连接时:计数加一并广播(上一步已实现) ++currentUsers; io.emit('user count', currentUsers); // 断开时:在同一作用域内为这个 socket 注册 disconnect 监听 socket.on('disconnect', () => { /*anything you want to do on disconnect*/ }); });

文档还特别提醒:'disconnect'不需要通过任何数据(no data passed through)传递。断开本身不携带业务负载,服务端在回调里做日志、清理或计数即可。最简单的验证方式就是在回调里console.log一句"某用户已断开",然后在控制台观察效果。

文档原文中的Note也点出了这个作用域规律:

就像'disconnect'一样,socket 可以发出的所有其他事件,都应该在有socket定义的那个 connection 监听器内部处理。

完整实现:断开时递减计数并广播

只打印日志还不够。要让所有在线客户端持续看到准确的当前在线人数,断开时必须做两件事:

  1. currentUsers减 1;
  2. 用更新后的计数再次 emit'user count'事件。

结合前一步的代码,服务端server.js中完整的 connect/disconnect 配对逻辑如下:

let currentUsers = 0; io.on('connection', socket => { // —— 连接:计数 +1 —— ++currentUsers; io.emit('user count', currentUsers); // —— 断开:计数 -1 —— socket.on('disconnect', () => { --currentUsers; io.emit('user count', currentUsers); }); });

客户端client.js侧不需要任何改动——它在前一步已经注册了'user count'监听器:

socket.on('user count', function(data) { console.log(data); });

于是形成闭环:任何客户端接入或离开,服务端都会把最新计数广播出去,所有客户端的控制台(或后续步骤中的界面)都能实时刷新。

测试断言分析:项目如何校验你的实现

该挑战属于 freeCodeCamp 的challengeType: 2(带测试断言的实战项目题)。从原始文档的# --hints--段落看,共两条断言,分别通过抓取你的/server.js/public/client.js源码做正则匹配:

断言 1 —— 服务端必须处理 socket 的 disconnect 事件

const url = new URL("/_api/server.js", code); const res = await fetch(url); const data = await res.text(); assert.match(data, /socket.on.*('|")disconnect('|")/s, '');

正则/socket.on.*('|")disconnect('|")/s中的s标志允许.跨行匹配,因此它验证的是:存在socket.on,且在其后(允许跨多行)出现以单引号或双引号包裹的disconnect事件名。这正对应文档强调的"监听在 socket 上"的要求——如果你错写成了io.on('disconnect', ...),这条断言就无法通过。

断言 2 —— 客户端必须监听 'user count' 事件

const url = new URL("/public/client.js", code); const res = await fetch(url); const data = await res.text(); assert.match( data, /socket.on.*('|")user count('|")/s, 'Your client should be connection to server with the connection defined as socket' );

这条断言复用于本步骤与上一步:它确保客户端用socket.on监听了'user count',保证断开后广播出去的计数确实有接收方。

从源码结构看,这些断言采用"抓取用户代码文本 + 正则匹配"的宽松策略,只检查关键 API 调用形态而非运行时行为;因此在通过断言之外,仍建议按文档所说在浏览器多开几个标签页实际连入、断开,用控制台日志验证计数增减是否符合预期。

小结:connect 与 disconnect 的对称设计

把原始文档的操作要点归纳起来:

要点内容依据
监听对象socket(每个连接),不是io(服务器)原始文档描述
注册位置现有'connect'监听器内部(socket作用域内)原始文档描述
事件负载disconnect不传递任何数据原始文档描述
计数逻辑断开时--currentUsers原始文档描述
广播事件更新后io.emit('user count', currentUsers)原始文档描述
作用域规律socket 可发出的所有事件都应在 connection 回调内处理原始文档 Note

这套"connect 加一 / disconnect 减一"的对称结构,是后续 Announce New Users 一步的基础——那里会把这两个事件统一升级为携带usernamecurrentUsersconnected三字段的'user'事件,从而在聊天室里实时显示"xx 已加入/离开"。理解了本文的 socket 级事件注册位置与作用域规则,后续步骤中的io.emit('user', {...})改造就可以水到渠成地完成。

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 19:10:32

Gopeed 下载器完全指南:4 个场景覆盖命令行到浏览器

Gopeed 下载器完全指南:4 个场景覆盖命令行到浏览器 【免费下载链接】gopeed A fast, modern download manager for HTTP, BitTorrent, Magnet, and ed2k. Cross-platform, built with Golang and Flutter. 项目地址: https://gitcode.com/GitHub_Trending/go/go…

作者头像 李华
网站建设 2026/9/5 19:05:19

开源中文字体霞鹜文楷:从下载到免费商用的完整指南

开源中文字体霞鹜文楷:从下载到免费商用的完整指南 【免费下载链接】LxgwWenKai An open-source Chinese font derived from Fontworks Klee One. 一款开源中文字体,基于 FONTWORKS 出品字体 Klee One 衍生。 项目地址: https://gitcode.com/GitHub_T…

作者头像 李华
网站建设 2026/9/5 19:04:43

游戏旧功能下线全流程:从配置开关到代码清理的工程实践

“郿坞夺宝的任务真应该删除”这句话,表面看像是一句玩家吐槽,但如果切换到项目和运营视角,它其实提示了一个很典型的遗留模块问题:这个任务上线时间很长,玩法不再是重点,但它还占着任务列表入口、定时调度…

作者头像 李华
网站建设 2026/9/5 19:01:42

DeepSeek Harness开发者预览版:插件化架构与本地部署排查指南

DeepSeek Harness 的开发者预览版一出现,核心信息就是“一切皆插件”。这个消息对做 AI 工程落地的开发者来说,值得关注的不是“又出了一个新工具”,而是 Harness 这个名字背后的设计思路:把一条完整开发链路拆成可装载、可替换、…

作者头像 李华
网站建设 2026/9/5 19:01:39

GTA增强版启动失败?从R星启动器登录到游戏闪退的排查指南

开头先给结论:GTA 增强版最近一次更新之后,很多人的问题不是游戏不好玩,而是过不了启动这道门:R星启动器反复提示登录失败,或者点下开始游戏,进程闪一下就没了。这两个现象经常连在一起,因为增强…

作者头像 李华
网站建设 2026/9/5 19:01:37

DeepSeek Harness开发者预览版:一切皆插件的AI运行时解析

最近 AI 圈最热闹的消息之一,莫过于 DeepSeek Harness 开发者预览版的出现。大家关心的不只是模型能力本身,还有“一切皆插件”这个定位带来的想象空间:模型不再只是聊天框里的对话引擎,而是可以被工具、技能、业务逻辑自由扩展的…

作者头像 李华