ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Coding Train WebSockets 教程 12.3:用 Socket.io 打通 Node.js 服务器与 p5.js 客户端

Coding Train WebSockets 教程 12.3:用 Socket.io 打通 Node.js 服务器与 p5.js 客户端 教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载本篇对应 Coding Train《WebSockets》系列_Tutorials/12-websockets/index.md的第 3 期视频核心任务是在已经用 Express 搭好静态服务器的 Node.js 应用中引入 socket.io同时让浏览器里的 p5.js 客户端建立同一条实时双向通道。读完本文你将掌握 socket.io 在服务端与客户端的接入方式、自定义事件收发emit/on、广播broadcast机制并能直接运行仓库中配套的 Node/sockets 示例为下一步实现多人共享画布打好基础。一、系列定位本节在整个教程中的角色该系列围绕一个明确目标展开使用 Node.js、p5.js 与 socket.ioWebSockets在多个浏览器会话之间创建共享的 HTML5 画布。整条学习路径在仓库中按视频顺序组织章节文档内容12.1_Tutorials/12-websockets/12.1-introduction-to-node.mdNode.js 基础与运行环境12.2_Tutorials/12-websockets/12.2-using-express-with-node.md用 Express 托管静态文件12.3本篇_Tutorials/12-websockets/12.3-socket-io.md把 socket.io 同时接入服务器与客户端建立连接12.4_Tutorials/12-websockets/12.4-shared-drawing-canvas.md整合全部组件实现共享画布与消息广播本节12.3处于通管道的关键位置12.1、12.2 解决了服务器能跑、静态页面能出的问题本节则引入实时双向通信层12.4 再基于它完成业务功能。文档标题 Connecting Client to Server with Socket.io 直白点明了本节唯一的主题——把客户端和服务器连起来。本节配套代码目录为 Node/sockets包含 4 个文件Node/sockets/ ├── package.json # 依赖声明与入口 ├── server.js # Express socket.io 服务器 └── public/ ├── index.html # 引入 socket.io / p5.js 客户端库 └── sketch.js # p5.js 客户端连接、收发事件、画点二、服务端把 socket.io 挂到已有 HTTP 服务器上1. 依赖声明与入口配置Node/sockets/package.json 声明了两个运行时依赖express: ^4.13.4—— 提供 HTTP 服务与静态文件托管socket.io: ^2.4.0—— 提供 WebSocket 实时通信能力。main字段指向server.js即服务器入口。安装依赖只需在本地副本目录执行npm install2. 服务器骨架Express 先跑起来Node/sockets/server.js 的前半部分是 12.2 已经讲过的 Express 标准骨架// Using express: http://expressjs.com/ var express require(express); // Create the app var app express(); // Set up the server // process.env.PORT is related to deploying on heroku var server app.listen(process.env.PORT || 3000, listen); // This call back just tells us that the server has started function listen() { var host server.address().address; var port server.address().port; console.log(Example app listening at http:// host : port); } app.use(express.static(public));几个值得注意的细节端口策略app.listen(process.env.PORT || 3000)优先读取环境变量process.env.PORT缺省回退到3000。源码注释明确说明这是为部署在 Heroku 等 PaaS 平台准备的——云平台通常会注入一个动态端口本地开发则固定用 3000。静态托管app.use(express.static(public))把public目录下的文件作为静态资源直接提供给浏览器因此客户端只需要index.html和sketch.js两个文件无需任何构建步骤。启动回调listen()在服务器就绪时打印实际监听地址与端口用于确认进程已正常运行。3. 关键一步require(socket.io)(server)在 Express 服务器就绪之后第 25 行是本节的核心动作Node/sockets/server.js// WebSocket Portion // WebSockets work with the HTTP server var io require(socket.io)(server);这里必须把已经创建的 HTTPserver对象传给 socket.io 工厂函数而不是新开一个独立端口。原因在于WebSocket 连接以 HTTP 握手为前提通过Upgrade请求从同一端口升级为长连接。这样客户端访问http://localhost:3000拉取静态页面的同时也能在同一个端口上建立 WebSocket 通道服务端无需再维护第二套监听。源码注释 WebSockets work with the HTTP server 正是对这一设计意图的说明。4.connection事件每个客户端一个socket对象接下来注册全局连接回调Node/sockets/server.jsio.sockets.on(connection, // We are given a websocket object in our function function (socket) { console.log(We have a new client: socket.id); socket.on(mouse, function(data) { // Data comes in as whatever was sent, including objects console.log(Received: mouse data.x data.y); // Send it to all other clients socket.broadcast.emit(mouse, data); } ); socket.on(disconnect, function() { console.log(Client has disconnected); }); } );逐点解读io.sockets.on(connection, callback)每当有客户端建立连接时触发。回调参数socket代表当前这一个客户端的专属连接对象每个用户都会拿到独立的一份。socket.idsocket.io 为每个连接分配的唯一标识服务端用它区分不同的客户端。示例里每次新客户端接入都会打印一行We have a new client: id。socket.on(mouse, function(data) {...})监听名为mouse的自定义事件。data是客户端发来的任意数据——可以是字符串、数字也可以是如源码注释所说whatever was sent, including objects的完整 JavaScript 对象。这里收到的就是客户端传上来的{x, y}鼠标坐标对象。socket.broadcast.emit(mouse, data)这是本节最核心的广播语义——把mouse事件连同数据转发给除发送者之外的所有其他客户端。发送者自己不需要这条回显因为它的本地画布已经画过了。socket.on(disconnect, ...)监听该客户端的断开事件便于服务端做清理与日志记录。三、客户端在浏览器里建立 socket 连接1. 引入 socket.io 客户端库客户端第一步是在 HTML 里加载 socket.io 的浏览器端库Node/sockets/public/index.htmlscript typetext/javascript srchttps://cdn.socket.io/socket.io-1.4.5.js/script script typetext/javascript srchttps://cdn.jsdelivr.net/npm/p51.4.1/lib/p5.min.js/script script typetext/javascript srcsketch.js/script页面依次加载socket.io 客户端库、p5.js 核心库、本地sketch.js。注意 socket.io 客户端库必须先于sketch.js 加载否则io全局对象在setup()中不可用。2. 建立连接io.connect(url)在 Node/sockets/public/sketch.js 的setup()中建立连接第 11-13 行// Keep track of our socket connection var socket; function setup() { createCanvas(400, 400); background(0); // Start a socket connection to the server // Some day we would run this server somewhere else socket io.connect(http://localhost:3000); ... }io.connect(http://localhost:3000)显式指定服务器地址。源码注释 Some day we would run this server somewhere else 提示一旦部署到远程主机这里应替换为实际域名或 IP而非写死本地地址。连接建立是异步的返回的socket对象即后续所有收发操作的门面。3. 接收事件socket.on(event, callback)紧接着注册mouse事件的接收回调第 16-25 行socket.on(mouse, // When we receive data function(data) { console.log(Got: data.x data.y); // Draw a blue circle fill(0,0,255); noStroke(); ellipse(data.x, data.y, 20, 20); } );语义与服务端完全对称监听名为mouse的事件收到数据后读取data.x、data.y在对应坐标画一个半径为 20 的蓝色圆点。蓝色圆点 来自其他客户端经服务器转发的鼠标轨迹这是识别消息是否走了一圈回来的直观视觉信号。4. 发送事件socket.emit(event, data)发送侧由 p5.js 的mouseDragged()驱动第 32-53 行function mouseDragged() { // Draw some white circles fill(255); noStroke(); ellipse(mouseX,mouseY,20,20); // Send the mouse coordinates sendmouse(mouseX,mouseY); } // Function for sending to the socket function sendmouse(xpos, ypos) { // We are sending! console.log(sendmouse: xpos ypos); // Make a little object with and y var data { x: xpos, y: ypos }; // Send that object to the socket socket.emit(mouse,data); }要点本地先画mouseDragged()先在本机画一个白色圆点再把坐标发出去——白色圆点 自己的鼠标轨迹。数据序列化坐标被打包成{x, y}对象后通过socket.emit(mouse, data)发送。socket.io 在底层自动完成 JSON 序列化服务端收到的data就是可直接访问属性的对象。事件名约定客户端emit(mouse)与服务端socket.on(mouse)、以及服务端broadcast.emit(mouse)与客户端socket.on(mouse)四处的字符串mouse必须完全一致事件名是双方约定的频道。四、完整消息链路一次拖拽发生了什么把服务端与客户端串起来一次跨浏览器拖拽的完整链路是客户端 A 拖拽 │ mouseDragged() 触发 │ 本地画白色圆点 │ sendmouse(mouseX, mouseY) 构造 {x, y} ▼ socket.emit(mouse, data) ──────────► 服务器 socket.on(mouse, data) │ 打印 Received: mouse x y ▼ socket.broadcast.emit(mouse, data) │ ┌─────────────────────────────────────┘ ▼ 客户端 B / C 的 socket.on(mouse, data) │ 打印 Got: x y ▼ 在 (data.x, data.y) 画蓝色圆点A 自己只看到白色轨迹本地绘制B、C 等所有其他客户端看到来自 A 的蓝色轨迹服务器转发服务器全程只做接收与转发同时打印日志用于调试。这一白点本地画、蓝点远端画的双色约定让多标签页测试时能一眼确认实时链路是否打通。五、运行与验证在本地副本中按以下步骤运行仓库本身是只读归档需先将其克隆或复制到本地# 1. 进入示例目录 cd Node/sockets # 2. 安装依赖 npm install # 3. 启动服务器 node server.js # 输出类似Example app listening at http://:::3000 # 4. 浏览器访问 http://localhost:3000验证要点单页自测打开http://localhost:3000拖拽鼠标应看到白色圆点轨迹浏览器控制台交替打印sendmouse: x y发送与Got: x y接收。跨页验证再开一个新标签页访问同一地址或直接复制标签页。在任意一个页面拖拽另一个页面应实时出现蓝色圆点轨迹——这就是多个浏览器会话共享画布的最小雏形。服务端日志终端里能看到每次连接的We have a new client: id、每条消息的Received: mouse x y以及关闭页面时的Client has disconnected。本地环境提示io.connect(http://localhost:3000)仅适用于本机开发若页面通过其他域名/端口访问需同步修改客户端连接地址。六、进阶对比broadcast与发给所有人本节示例只用到了socket.broadcast.emit()发给除发送者外的所有客户端。仓库中另一个更完整的实战示例 CodingChallenges/CC_032.2_agar.io_sockets/Nodeagar.io 风格多人游戏展示了 socket.io 另外两种消息模式的组合用法// 服务器维护全局状态 var blobs []; setInterval(heartbeat, 33); function heartbeat() { io.sockets.emit(heartbeat, blobs); // 发给所有人包括发送者 }对比三种发送方式参考 Node/sockets/server.js 与 CC_032.2 的 server.js调用方式语义适用场景socket.emit(...)只发给当前这一个客户端服务端对单客户端的应答socket.broadcast.emit(...)发给除发送者外的所有其他客户端共享画布中的转发别人的轨迹io.sockets.emit(...)发给当前连接的所有客户端含发送者全局状态同步、心跳广播agario 示例还展示了服务端持有权威状态blobs数组的做法客户端通过socket.emit(start)注册自己、socket.emit(update)持续上报位置服务器用setInterval(heartbeat, 33)约 30fps定时把全体状态推给所有客户端。这种客户端上报 服务器广播的模式正是从本节单事件转发迈向多人在线同步的典型演进路径。七、版本与兼容性注意事项以仓库实际内容为准本节示例存在两处版本口径需要特别留意客户端 CDNNode/sockets/public/index.html 引入的是socket.io-1.4.5.jssocket.io 1.x 时代的浏览器端库服务端依赖Node/sockets/package.json 声明的是socket.io: ^2.4.0socket.io 2.x。客户端库版本与服务器依赖版本分属两个大版本区间实际运行时可能存在协议握手兼容性差异。从源码结构看这是归档仓库中示例代码的原始状态若要在新项目中复刻本教程建议将客户端 CDN 与服务器端 socket.io 统一到同一大版本例如都使用 2.x 对应的客户端库以保证连接稳定。同理express ^4.13.4、p5.js 1.4.1 等版本也以仓库当前声明为准。八、后续延伸12.4 共享画布本节的连接与事件机制将在 _Tutorials/12-websockets/12.4-shared-drawing-canvas.md 中被整合为完整的多人协作画布并系统讲解如何在客户端之间广播消息。前置基础若对 p5.js 还不熟悉可先学习 beginners/p5js 入门系列数据与异步回调相关背景可参考 _Tutorials/10-working-with-data。实战范本CodingChallenges/CC_032.2_agar.io_sockets/Node 展示了同一套Express socket.io p5.js技术栈在多人实时游戏中的应用包含服务器状态维护、心跳广播与客户端位置同步的完整实现。至此客户端与服务器之间的实时通道已经打通服务器用require(socket.io)(server)在同一端口上挂载 WebSocket 能力客户端用io.connect()建立连接双方以emit/on按自定义事件名收发数据并用broadcast实现跨客户端转发。下一步就可以在这个通道上装载真正的业务逻辑了。赞分享教程文档示例工程【免费下载链接】website-archiveArchive of the Coding Train website (first version)项目地址https://gitcode.com/gh_mirrors/we/website-archive点击查看免费下载相关推荐基于 Node.js、Express、socket.io 与 p5.js 的多人共享协作画布实战指南Coding Train WebSockets 教程基于 Node.js、Express、socket.io 与 p5.js 的多人共享协作画布实战指南Coding Train WebSockets 教程 导教程文档示例工程基于 Node.js、Express、Socket.io 与 p5.js 构建多人共享绘图画布Coding Train WebSockets 系列实战指南基于 Node.js、Express、Socket.io 与 p5.js 构建多人共享绘图画布Coding Train WebSockets 系列实战指南 本教程文档示例工程BepisPlugins解锁Illusion游戏无限可能的7大必备插件集合 BepisPlugins解锁Illusion游戏无限可能的7大必备插件集合 你是否厌倦了Illusion/Illgames旗下游戏的有限功能想要为你的上一篇uWebSockets.js中的Pub/Sub模式实现详解下一篇技术视角OpenCore Legacy Patcher如何重塑旧Mac的生命周期管理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表