Node.js原生实现TCP客户端、UDP服务端与HTTP对比示例

发布时间:2026/6/7 8:01:18

Node.js原生实现TCP客户端、UDP服务端与HTTP对比示例 本文还有配套的精品资源点击获取简介提供三个开箱即用的Node.js脚本TCPC2.js是纯原生TCP客户端支持连接、发送、接收及异常处理UDPS2.js为UDP服务端演示无连接模式下的数据收发全流程httpserver.js作为轻量HTTP服务参照便于协议行为对比和本地调试。所有代码仅依赖Node.js内置net和dgram模块无第三方依赖覆盖socket创建、端口监听、消息解析、响应构造、超时与错误捕获等核心环节。注释详尽结构扁平适合快速理解TCP三次握手/四次挥手的实际编码体现、UDP面向报文的通信特性以及Node.js事件驱动模型在不同网络协议中的落地方式。可直接在本地Node环境v14中运行无需构建或安装额外工具。1. 为什么这三个脚本值得你花30分钟认真跑一遍我带过不少刚转服务端的前端同学也辅导过不少想补网络基础的应届生。他们常问一个问题“TCP和UDP到底差在哪HTTP又凭什么能稳坐Web协议头把交椅”——不是概念记不住是没在真实代码里“摸”过它们的脉搏。这组脚本就是专为这种“手痒型学习者”设计的它不讲抽象理论而是让你亲手启动一个TCP客户端去连、断、重连让UDP服务端在终端里实时打印出每一个发来的字节再用同一个端口跑起一个HTTP服务三者并排运行你改一行代码、发一条命令就能肉眼看到协议行为的差异。核心关键词Node.js、TCP客户端、UDP服务端、net模块、dgram模块全部落在实操层面。比如TCPC2.js里那行socket.connect()背后是三次握手的完整生命周期UDPS2.js中server.on(message)的触发时机直接对应UDP“来了就收、不问来路”的无连接本质而httpserver.js里res.writeHead(200)的响应头构造恰恰是HTTP建立在TCP之上的典型证据——它必须等TCP连接稳定后才能发而UDP服务端连“连接”这个概念都没有。这些不是PPT里的箭头图是你在终端里敲node TCPC2.js后亲眼看到connected日志、data回显、close事件依次打出的过程。更重要的是所有逻辑都扎根于 Node.js 原生能力。没有 Express 的封装没有 Socket.IO 的抽象只有net.createConnection()和dgram.createSocket()这两个最底层的入口。这意味着你学到的不是某个框架的用法而是 Node.js 如何把操作系统 socket API 翻译成 JavaScript 事件connect、data、end、error、message……这些事件名就是网络协议在代码世界的指纹。当你以后看 Koa 源码、调试 WebSocket 连接异常、甚至写一个自定义协议解析器时你会突然意识到哦原来socket.setTimeout()控制的不只是超时它是在 TCP 连接空闲时主动触发 FIN 包的开关buffer.length在 UDP 里必须严格校验因为一个 IP 包最大就65507字节超了直接被内核丢弃——这些细节只有亲手写过原生 socket 才会刻进肌肉记忆。这套脚本特别适合两类人一类是正在准备后端面试的同学TCP状态机、UDP丢包场景、HTTP长连接复用全是高频考点另一类是做 IoT 或实时音视频的开发者设备上报常用 UDP控制信令走 TCP而 HTTP 只用于配置下发或状态查询——三者共存是常态。你不需要部署到服务器node httpserver.js node UDPS2.js node TCPC2.js三条命令本地环境Node v14开箱即用。接下来的内容我会带你一层层拆开这三个文件不仅告诉你“怎么写”更告诉你“为什么这么写”、“不这么写会怎样”以及我在真实项目里踩过的坑。2. 整体架构与设计思路为什么是这三个脚本而不是一个大项目2.1 三脚架式结构解耦协议聚焦本质很多人初学网络编程喜欢写一个“全能服务端”既监听TCP又接收UDP还跑HTTP。结果代码一长逻辑全混在一起最后连自己都搞不清哪个回调属于哪个协议。这组脚本反其道而行之采用“三脚架”结构每个脚本只干一件事且彼此完全独立。TCPC2.js是纯客户端角色它不监听任何端口只发起连接UDPS2.js是纯服务端角色它不主动连接别人只守着端口收包httpserver.js则是标准的请求-响应模型它必须等待客户端发起连接后才处理。这种刻意的“功能割裂”恰恰是为了还原协议的真实分工。TCP 是面向连接的所以客户端和服务端角色天然不对称客户端负责connect()发起握手服务端负责listen()被动等待。UDP 是无连接的所谓“服务端”只是个监听者它甚至不知道发包的客户端是谁message事件携带的rinfo对象里只有 IP 和端口没有会话状态。HTTP 则是应用层协议它必须依附于传输层——http.createServer()底层调用的正是net.createServer()也就是说HTTP 服务本质上是一个特殊的 TCP 服务端。这三个脚本并列摆放就是在视觉上强制你建立这种分层认知UDP 和 TCP 是平级的传输层协议HTTP 是跑在 TCP 上的应用层协议。2.2 模块选型逻辑为什么只用 net 和 dgram不用 http 模块你可能会疑惑httpserver.js明明用了http模块这不是违背“纯原生”原则吗这里需要厘清一个关键点http模块是 Node.js 内置模块和net、dgram同属标准库它不依赖任何 npm 包也不引入额外抽象层。它的存在恰恰是为了演示“协议叠加”——HTTP 协议规范定义了请求行、头部、空行、正文的格式http模块做的只是按 RFC 7230 解析这些文本并提供req.url、req.headers这样的便利属性。但如果你打开 Node.js 源码会发现http.Server的_connectionListener方法里最终调用的还是net.Socket的read()和write()。换句话说http模块是net模块的语法糖而TCPC2.js和UDPS2.js则是直接操作net和dgram的“裸金属”。不引入第三方库如socket.io、ws是经过深思熟虑的。以socket.io为例它为了兼容老旧浏览器会在 TCP 连接之上再封装一层长轮询或 WebSocket 降级逻辑中间夹杂心跳包、ACK确认、命名空间路由等大量业务无关代码。初学者一旦依赖它很容易把“连接成功”归因于socket.io而忽略了底层 TCP 的SYN/SYN-ACK/ACK三次握手才是真正的起点。这组脚本坚持“最小可行实现”就是为了让你看清网络通信的基石永远是操作系统提供的 socket 接口Node.js 只是把它变成了 JavaScript 事件。2.3 错误处理策略为什么每个脚本都单独处理超时、断连、解析失败网络编程最残酷的真相是90% 的代码不是在实现功能而是在防御失败。TCP 可能因防火墙被重置RSTUDP 包可能在网络中彻底消失HTTP 请求可能被恶意构造得极长导致内存溢出。这三个脚本的错误处理不是“锦上添花”而是“生存必需”。TCPC2.js中socket.setTimeout(5000)设置了 5 秒连接超时这是防止客户端卡死在connect()阶段socket.on(error)捕获ECONNREFUSED目标端口无服务、ETIMEDOUT网络不可达等系统级错误而socket.on(close)的had_error参数则明确区分了“正常关闭”和“异常断开”。UDPS2.js更激进它对每个收到的Buffer都做buffer.length 65507校验因为 IPv4 下 UDP 数据报最大有效载荷就是 65507 字节65535 - 20字节IP头 - 8字节UDP头超了内核直接丢弃服务端根本收不到同时用try...catch包裹JSON.parse()因为 UDP 不保证顺序和可靠客户端发的可能根本不是合法 JSON。httpserver.js则在req.on(data)中累积缓冲区但设置了req.socket.setTimeout(30000)防止慢速攻击并在req.on(end)后立即res.end()避免连接长时间挂起。这些不是教科书式的“最佳实践”而是我在一个物联网网关项目里血泪换来的教训当时 UDP 服务端没做长度校验某台设备固件 bug 导致发送超长包整个进程因 Buffer 分配失败而 OOM 崩溃。从此我养成了习惯只要涉及网络输入第一行代码必是边界检查。3. 核心细节解析与实操要点从代码注释读懂协议心跳3.1 TCPC2.jsTCP客户端的“呼吸节奏”我们先看TCPC2.js的核心骨架const net require(net); const client net.createConnection({ host: 127.0.0.1, port: 8080 }, () { console.log(✅ 已连接到服务器); client.write(Hello from TCP Client\n); }); client.setTimeout(5000); client.on(timeout, () { console.log(⏰ 连接超时正在关闭...); client.destroy(); }); client.on(data, (data) { console.log( 收到服务器响应: ${data.toString().trim()}); }); client.on(end, () { console.log( 服务器主动关闭连接); }); client.on(close, (had_error) { console.log(had_error ? 连接异常关闭 : ✅ 连接已关闭); }); client.on(error, (err) { console.error(❌ 连接错误: ${err.code} - ${err.message}); });这段代码看似简单却浓缩了 TCP 客户端的全部生命节律。createConnection()的回调函数就是三次握手完成的精确时刻——当内核返回connect()成功Node.js 才会触发这个回调。此时你看到的✅ 已连接到服务器对应的是 TCP 状态机中的ESTABLISHED状态。紧接着的client.write()是向已建立的连接发送数据这会触发内核将数据拷贝到发送缓冲区并由 TCP 协议栈负责分片、重传、确认。setTimeout(5000)的作用常被误解。它不是设置整个连接的存活时间而是设置“空闲超时”如果在 5 秒内既没有收到数据也没有发送数据socket 就会触发timeout事件。这在实际项目中极为关键。比如你连接一个远程设备设备休眠后无法响应客户端若不主动探测连接会一直挂着浪费资源。这里的 5 秒是我根据经验设定的平衡点太短如 1 秒容易误判网络抖动太长如 30 秒则故障发现延迟过高。client.on(end)和client.on(close)的区别是初学者最容易混淆的点。end事件表示对方服务器调用了socket.end()即发出了 FIN 包TCP 连接进入半关闭状态而close事件表示 socket 彻底销毁无论是正常destroy()还是异常断开。had_error参数就是你的“事故报告单”为true说明是ECONNRESET、EPIPE等硬性错误导致的关闭这时你需要记录日志并尝试重连为false则可能是对方优雅下线你可以安静退出。提示在真实设备通信中我常在end事件后加一句setTimeout(() client.connect(), 1000)实现自动重连。但要注意不能在error事件里直接重连否则网络彻底中断时会疯狂创建 socket耗尽文件描述符。3.2 UDPS2.jsUDP服务端的“来者不拒”哲学UDPS2.js的代码风格截然不同它体现的是 UDP 的无状态哲学const dgram require(dgram); const server dgram.createSocket(udp4); server.on(error, (err) { console.error(❌ UDP服务器错误: ${err.stack}); server.close(); }); server.on(message, (msg, rinfo) { console.log( 收到来自 ${rinfo.address}:${rinfo.port} 的消息: ${msg.toString().trim()}); // 严格长度校验IPv4 UDP 最大有效载荷为 65507 字节 if (msg.length 65507) { console.warn(⚠️ 警告收到超长UDP包 (${msg.length}字节)已丢弃); return; } try { const data JSON.parse(msg.toString()); console.log( 解析为JSON:, data); // 构造响应 const response JSON.stringify({ echo: data, timestamp: Date.now() }); server.send(response, rinfo.port, rinfo.address, (err) { if (err) console.error( 响应发送失败: ${err.message}); }); } catch (e) { console.warn(⚠️ JSON解析失败忽略该包:, e.message); } }); server.on(listening, () { const address server.address(); console.log( UDP服务器监听中: ${address.address}:${address.port}); }); server.bind(8081);dgram.createSocket(udp4)创建的是一个 UDPv4 socket它不像 TCP 那样需要listen()和accept()而是直接bind()到指定端口。message事件是 UDP 的心脏每次内核收到一个完整的 UDP 数据报就会触发一次。注意rinfo对象它包含address客户端IP和port客户端端口但没有socketId或sessionId——因为 UDP 本身不维护连接状态每个包都是独立的。长度校验if (msg.length 65507)是生死线。我曾在一个车载终端项目中遇到过诡异问题设备固件升级后UDP 心跳包长度从 32 字节涨到 128 字节服务端没做校验结果某些 Linux 内核版本在处理超长包时会静默丢弃日志里没有任何提示排查了三天才发现是内核参数net.ipv4.udp_mem不足。从此我给所有 UDP 服务端都加上了这行校验并在日志里明确标出警告级别。server.send()的用法也暗藏玄机。它必须传入rinfo.port和rinfo.address因为 UDP 没有“默认对端”的概念你必须显式告诉内核“把包发给谁”。而且这个发送是异步的回调里的err可能是EMSGSIZE响应包太大被内核拒绝这时你需要降级处理比如只返回{ status: error, code: MSG_TOO_LARGE }这样的精简响应。注意UDP 不保证送达所以server.send()成功只代表包已交给内核发送队列并不意味着客户端收到了。这就是为什么 IoT 设备常用“UDP 应用层 ACK”模式客户端发指令服务端回响应客户端收到响应才认为指令生效收不到就重发。这组脚本没实现 ACK但你在message事件里加个console.timeLog(recv)再在客户端加个console.timeLog(send)就能直观看到网络延迟波动有多大。3.3 httpserver.jsHTTP服务端的“协议契约”httpserver.js表面看最“高级”实则最“脆弱”因为它严格遵守 HTTP 协议契约const http require(http); const server http.createServer((req, res) { console.log( 收到HTTP请求: ${req.method} ${req.url}); // 设置超时防止慢速攻击 req.socket.setTimeout(30000); req.socket.on(timeout, () { console.warn(⏳ 请求超时关闭连接); req.socket.destroy(); }); // 处理POST数据 let body ; req.on(data, (chunk) { body chunk.toString(); }); req.on(end, () { console.log( 请求体: ${body.substring(0, 200)}${body.length 200 ? ... : }); // 构造响应 res.writeHead(200, { Content-Type: application/json; charsetutf-8, X-Powered-By: Node.js Native HTTP }); const response { message: Hello from HTTP Server, timestamp: new Date().toISOString(), method: req.method, url: req.url, headers: req.headers }; res.end(JSON.stringify(response, null, 2)); }); }); server.on(error, (err) { console.error(❌ HTTP服务器错误: ${err.message}); }); server.listen(8080, 127.0.0.1, () { console.log(️ HTTP服务器运行在 http://127.0.0.1:8080); });http.createServer()的回调函数是 HTTP 协议解析完成后的“业务入口”。Node.js 的http模块已经帮你完成了繁重的解析工作它把原始 TCP 流按\r\n\r\n分割出头部再按Content-Length或分块编码chunked提取正文最后把结果组织成req对象。你看到的req.method、req.url、req.headers都是解析后的结构化数据。但这也带来了风险如果客户端发一个畸形的请求头比如Content-Length: 999999999http模块会试图分配巨大内存导致进程 OOM。因此req.socket.setTimeout(30000)是必须的——它在 socket 层设置超时无论 HTTP 解析进行到哪一步30 秒没动静就强制断开。res.writeHead(200, {...})这行代码是 HTTP 协议的“法律文书”。状态码200表示成功Content-Type头告诉浏览器“这是 JSON请用 UTF-8 解析”X-Powered-By则是可选的标识。注意res.end()必须被调用否则连接会一直挂着。在早期 Node.js 版本中忘记res.end()是最常见的内存泄漏原因——每个未结束的响应都会占用一个 socket直到超时。有趣的是httpserver.js和TCPC2.js默认监听同一端口8080但这并不冲突因为它们使用的是不同协议栈。TCP 和 UDP 的端口号是独立的命名空间HTTP 走 TCP 8080UDP 服务端走 UDP 8081就像一栋楼里TCP 是电梯通道UDP 是楼梯通道它们可以共用“8080号房间号”但走的是完全不同的物理路径。4. 实操过程与核心环节实现从零开始部署与对比验证4.1 环境准备与一键启动首先确认你的 Node.js 版本不低于 v14推荐 v18 LTSnode -v # 输出应为 v14.x 或更高然后进入项目目录确保三个脚本都在当前路径下ls -l *.js # 应看到 TCPC2.js UDPS2.js httpserver.js现在我们用最朴素的方式启动它们。打开三个终端窗口或使用 tmux/screen分别执行终端1HTTP服务端node httpserver.js # 输出️ HTTP服务器运行在 http://127.0.0.1:8080终端2UDP服务端node UDPS2.js # 输出 UDP服务器监听中: 0.0.0.0:8081终端3TCP客户端node TCPC2.js # 输出✅ 已连接到服务器 → 收到服务器响应 → ✅ 连接已关闭此时三个服务已在本地并行运行。但光看日志还不够我们需要用工具主动“刺激”它们观察协议行为的差异。4.2 协议行为对比实验用 curl、nc、socat 亲手触摸协议实验1HTTP 的“有求必应”在新终端中用curl发送一个标准 HTTP 请求curl -v http://127.0.0.1:8080/test?nameNodeJS观察httpserver.js终端输出 收到HTTP请求: GET /test?nameNodeJS 请求体:curl -v的-v参数会显示完整的 HTTP 交互过程它先建立 TCP 连接三次握手再发送GET /test?nameNodeJS HTTP/1.1请求行和头部最后接收200 OK响应。这个过程清晰地展示了 HTTP 对 TCP 的依赖——没有稳定的 TCP 连接HTTP 报文根本无法传输。实验2TCP 的“连接即承诺”用ncnetcat模拟一个原始 TCP 客户端nc 127.0.0.1 8080 # 输入任意文本如 Hello TCP按回车此时httpserver.js终端不会有任何输出因为nc发送的是裸 TCP 数据不是 HTTP 协议格式。但TCPC2.js的日志会显示✅ 已连接到服务器证明 TCP 连接已建立。这说明TCP 层只管“通不通”不管“说什么”而 HTTP 层才管“说的对不对”。实验3UDP 的“一锤子买卖”用nc发送 UDP 包echo {cmd:ping,id:123} | nc -u 127.0.0.1 8081观察UDPS2.js终端输出 收到来自 127.0.0.1:54321 的消息: {cmd:ping,id:123} 解析为JSON: { cmd: ping, id: 123 }注意rinfo.port是54321这是一个临时端口由nc随机分配。如果你再次执行这条命令端口号会变。这印证了 UDP 的无连接特性服务端不保存任何客户端信息每次message事件都是全新的、孤立的。实验4压力与边界测试用abApache Bench对 HTTP 服务施压ab -n 1000 -c 100 http://127.0.0.1:8080/观察httpserver.js终端是否出现⏳ 请求超时或❌ HTTP服务器错误。正常情况下100 并发 1000 次请求应该平稳完成。如果出现错误说明你的req.socket.setTimeout()设置过短或者机器资源不足。对 UDP 服务端用socat发送超长包# 生成一个 70000 字节的随机字符串 python3 -c print(A * 70000) | socat - udp4-datagram:127.0.0.1:8081UDPS2.js终端应立刻输出⚠️ 警告收到超长UDP包 (70000字节)已丢弃证明长度校验生效。4.3 关键参数详解与调优建议参数脚本默认值为什么这个值调优建议socket.setTimeout()TCPC2.js5000ms平衡网络抖动与故障发现速度内网可设为 2000ms公网建议 10000msserver.send()响应大小UDPS2.js无硬限制UDP 包最大 65507 字节响应体务必控制在 1400 字节内避免 IP 分片req.socket.setTimeout()httpserver.js30000ms防御慢速攻击Slowloris静态资源可设为 5000msAPI 接口建议 15000msdgram.createSocket()类型UDPS2.jsudp4明确指定 IPv4避免双栈歧义若需 IPv6改为udp6但需确保系统支持实操心得在生产环境中我从不在 UDP 服务端直接JSON.stringify()大对象。而是先用JSON.stringify(obj).length计算长度如果超过 1400 字节就返回{ error: PAYLOAD_TOO_LARGE, max: 1400 }。因为 IP 分片后任何一个分片丢失整个 UDP 包就作废而 1400 字节是大多数网络路径的 MTUMaximum Transmission Unit安全值。5. 常见问题与排查技巧实录那些年我们一起踩过的坑5.1 “Connection refused” 错误的七种可能当你运行node TCPC2.js却看到❌ 连接错误: ECONNREFUSED - Connection refused别急着怀疑代码。这是开发中最常见的“假警报”原因多达七种目标端口无服务httpserver.js没启动或启动时指定了其他端口如server.listen(3000)。用lsof -i :8080macOS/Linux或netstat -ano | findstr :8080Windows确认端口占用。绑定地址错误httpserver.js中server.listen(8080)默认绑定0.0.0.0但若写成server.listen(8080, 127.0.0.1)则只能被本机访问而TCPC2.js若连接localhost在某些 hosts 配置下可能解析为::1IPv6导致失败。统一用127.0.0.1最稳妥。防火墙拦截公司电脑或云服务器防火墙可能屏蔽非标准端口。临时关闭防火墙测试sudo ufw disable或 Windows 防火墙设置。端口被占用另一个进程占用了 8080。lsof -i :8080查看 PIDkill -9 PID结束它。Node.js 版本兼容性v12 以下版本net.createConnection()的选项对象语法略有不同。确保host和port是顶层属性。DNS 解析失败TCPC2.js中host: example.com若域名无法解析会报ENOTFOUND。换成127.0.0.1排查。SELinux 限制Linux 服务器某些发行版默认启用 SELinux会阻止 Node.js 绑定端口。临时禁用sudo setenforce 0测试。我的快速排查清单①curl http://127.0.0.1:8080能否访问 HTTP 服务②telnet 127.0.0.1 8080能否建立 TCP 连接③nc -u 127.0.0.1 8081发送后UDP 服务端是否有日志三步下来90% 的连接问题都能定位。5.2 UDP 包“石沉大海”的真相UDPS2.js启动后echo test | nc -u 127.0.0.1 8081却没有任何日志输出别怀疑人生UDP 的“不可靠”在此刻显露无疑。可能原因客户端没发出去nc -u默认使用 IPv6而UDPS2.js绑定的是udp4。强制指定 IPv4echo test | nc -4u 127.0.0.1 8081。服务端没收到server.bind()后server.address()返回的address是0.0.0.0表示监听所有接口。但如果机器有多个网卡nc可能发到了错误的接口。用ifconfig确认127.0.0.1是否在lo网卡上。包被内核丢弃dmesg | tail查看内核日志可能有UDP: bad checksum或UDP: short packet。这通常是因为客户端发的包损坏或nc版本 bug。Node.js 事件循环阻塞message事件回调里如果有同步耗时操作如JSON.parse()解析超大 JSON会阻塞后续包的处理。用setImmediate()或process.nextTick()将耗时操作移出事件循环。5.3 HTTP 服务“假死”现象深度解析httpserver.js启动后curl第一次能通第二次就卡住curl -v显示* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)后再无反应这是典型的“响应未结束”陷阱。根源在于res.end()被遗漏或放在异步回调里。例如// ❌ 错误写法res.end() 在 setTimeout 里永远不会执行 req.on(end, () { setTimeout(() { res.end(done); // 这行永远不会运行 }, 100); }); // ✅ 正确写法res.end() 必须在当前事件循环中调用 req.on(end, () { res.end(done); // 立即执行 });Node.js 的 HTTP 服务器是“请求-响应”模型每个请求必须有且仅有一个res.end()。漏掉它socket 就会一直挂着直到超时你设的 30 秒期间无法处理新请求。5.4 三协议共存时的端口冲突与调试技巧当三个脚本同时运行如何确保它们互不干扰我的调试技巧端口规划表在项目根目录建一个PORTS.md文件明确记录| 服务 | 协议 | 端口 | 说明 | |------------|------|------|--------------------------| | HTTP Server| TCP | 8080 | 主要调试端口 | | TCP Client | TCP | - | 连接 8080不监听端口 | | UDP Server | UDP | 8081 | 避免与 HTTP 端口冲突 |进程管理用pkill -f node.*\.js一键杀死所有 Node.js 脚本避免旧进程残留。网络抓包用tcpdump抓取特定端口流量bash# 抓取 TCP 8080 端口的所有包sudo tcpdump -i lo port 8080 -w tcp8080.pcap# 抓取 UDP 8081 端口的包sudo tcpdump -i lo udp port 8081 -w udp8081.pcap 然后用 Wireshark 打开.pcap 文件你能清晰看到 TCP 的 SYN/SYN-ACK/ACKUDP 的单个数据报以及 HTTP 的明文请求/响应——这才是协议学习的终极形态。最后分享一个小技巧在TCPC2.js的client.on(data)里加一行console.log( 数据长度:, data.length, 字节)在UDPS2.js的message事件里加console.log( UDP包长度:, msg.length, 字节)在httpserver.js的req.on(data)里加console.log( HTTP正文长度:, chunk.length, 字节)。运行后对比三者的数字你会直观感受到TCP 是流式传输数据被拆成小块UDP 是报文式传输整包到达HTTP 是基于 TCP 的应用层协议正文长度取决于请求内容。这种肉眼可见的差异比一百页文档都管用。我在实际项目中常把这三个脚本作为新同事的“入职第一课”。不讲原理只让他们改几行代码、发几条命令然后盯着终端日志看。当他们第一次看到ECONNRESET错误时的困惑第一次收到 UDP 响应时的惊喜第一次用tcpdump抓到三次握手时的震撼——那一刻网络协议就不再是纸上的名词而成了他们指尖可触的现实。这组脚本的价值不在于它多复杂而在于它足够简单简单到能让最基础的概念在真实的字节流中活过来。本文还有配套的精品资源点击获取简介提供三个开箱即用的Node.js脚本TCPC2.js是纯原生TCP客户端支持连接、发送、接收及异常处理UDPS2.js为UDP服务端演示无连接模式下的数据收发全流程httpserver.js作为轻量HTTP服务参照便于协议行为对比和本地调试。所有代码仅依赖Node.js内置net和dgram模块无第三方依赖覆盖socket创建、端口监听、消息解析、响应构造、超时与错误捕获等核心环节。注释详尽结构扁平适合快速理解TCP三次握手/四次挥手的实际编码体现、UDP面向报文的通信特性以及Node.js事件驱动模型在不同网络协议中的落地方式。可直接在本地Node环境v14中运行无需构建或安装额外工具。本文还有配套的精品资源点击获取

相关新闻