一文浅析Node中的TCP和UDP

一文浅析Node中的TCP和UDP

Node 是一个面向网络而生的平台,它具有事件驱动、无阻塞、单线程等特性,具备良好的可伸缩性,使得它十分轻量,适合在分布式网络中扮演各种各样的角色。

Node 提供了 netdgramhttphttp2https 等模块,分别用于处理 TCPUDPHTTPHTTPS,适用于服务端和客户端。

构建 TCP 服务

TCP 服务在网络应用中十分常见,目前大多数的应用都是基于 TCP 搭建而成的,它的全名为传输控制协议,在 OSI 模型中属于传输层协议,许多应用层协议基于 TCP 构建,典型的 HTTPSMTPIMAP 等协议。在这里不讲 TCP 相关知识点了,如果感兴趣可以关注我 计算机网络 这个专栏进行学习。

创建 TCP 服务端

在基本了解 TCP 的工作原理之后,我们可以开始创建一个 TCP 服务器端来接受网络请求,net 模块提供了一个异步网络 API,用于创建基于 streamTCPIPC 服务器和客户端。【相关教程推荐:nodejs视频教程、编程教学】

请看下面这个例子,我们在 server.js 文件中编写以下代码,如下:

import net from "net";const server = net.createServer((socket) => {  socket.on("data", (data) => {    console.log("监听到客户端的数据:", data.toString());  });  // 监听客户端断开连接事件  socket.on("end", () => {    console.log("客户端断开连接");  });  // 发送数据给客户端  socket.end("over over over\n");});// 启动服务server.listen(3000, () => {  console.log("服务创建成功");});

我们通过 net.createServer(listener) 即可创建一个 TCP 服务器,该函数的参数是里链接事件 connection 的侦听器。

当我们在终端执行该文件时,服务创建成功 输出在了终端。

nodemon .\server.js

在前面我们通过 net.createServer 创建了一个服务端,那么接下来我们使用 net.connect 创建一个客户端进行会话,具体代码如下所示:

import net from "net";const client = net.connect({ port: 3000 }, () => {  client.write("今晚出去吃饭,收到请 over\n");});// 接收服务端的数据client.on("data", (data) => {  console.log("接收服务端的数据: ", data.toString());  // 断开连接  client.end();});// 断开连接client.on("end", () => {  console.log("断开连接");});

我们这个时候对两个文件执行,如下所示:

image.png

接下来我们还有这样的一个例子,具体代码如下图所示:

image.png

具体运行结果请看下面的动图:

动画.gif

在客户端我使用 client.write() 发送了多次数据,但是只有 setTimeout 之外的是正常的,setTimeout 里面连续发送的似乎并不是每一次一返回,而是会随机合并返回了,在这里也就出现了粘包了。

TCP 针对网络中的小数据包有一定的优化策略: Negle 算法,如果每次只发送一个字节的内容而不优化,网络中将充满只有极少数有效数据的数据报,将十分浪费网络资源,该算法针对这种情况,要求缓冲区的数据达到一定数量或者一定时间后才将其发出,所以小数据包将会被该算法合并,以此来优化网络。这种优化虽然使网络有效地使用,但是数据有可能被延迟发送。

Node 中,由于 TCP 默认启用了 Negle 算法,可以调用 socket.setNoDelay(true) 去掉 Negle 算法,使得 write() 可以立即发送数据到网络中:

image.png

关闭 Nagle 算法并不总是有效的,因为其是在服务端完成合并,TCP 接收到数据会先存放于自己的缓冲区中,然后通知应用接收,应用层因为网络或其它的原因若不能及时从 TCP 缓冲区中取出数据,也会造成 TCP 缓冲区中存放多段数据块,就又会形成粘包。

TCP原理

Node 中,调用 createServer() 等同于调用 new Server(),具体结果如下图所示:

image.png

百度文心百中 百度文心百中

百度大模型语义搜索体验中心

百度文心百中 22 查看详情 百度文心百中

这主要的原因它在 Node 源码中有如下定义,所以调用 createServer() 函数实际上调用的是 new Server(),具体代码如下图所示:

function createServer(options, connectionListener) {  return new Server(options, connectionListener);}

该构造函数的定义主要有如下所示:

function Server(options, connectionListener) {  EventEmitter.call(this);  // 注册连接到来时执行的回调  if (typeof options === "function") {    connectionListener = options;    options = {};    this.on("connection", connectionListener);  } else if (options == null || typeof options === "object") {    options = { ...options };    if (typeof connectionListener === "function") {      this.on("connection", connectionListener);    }  }  // 服务器建立的连接数  this._connections = 0;  this[async_id_symbol] = -1;  this._handle = null;  this._usingWorkers = false;  this._workers = [];  this._unref = false;  // 服务器下的所有连接是否允许半连接  this.allowHalfOpen = options.allowHalfOpen || false;  // 有连接时是否注册读事件  this.pauseOnConnect = !!options.pauseOnConnect;  this.noDelay = Boolean(options.noDelay);  // 是否支持keepAlive  this.keepAlive = Boolean(options.keepAlive);  this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);}ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype);ObjectSetPrototypeOf(Server, EventEmitter);

listen

它返回的是一个普通的 JavaScript 对象,接着调用 listen 函数监听端口,listen 方法支持多种使用方式主要有以下这几种方法:

传入的是一个已经创建的 TCP 服务器,而不是需要创建的一个服务器;传进来是一个对象,并且带了 fd 字段;创建了一个 TCP 服务器,并启动该服务器,如果传入了 host 会对其进行域名解析;

该方法的的主要逻辑有如下代码所示:

Server.prototype.listen = function (...args) {  /*         处理入参,根据文档我们知道listen可以接收好几个参数,          假设我们这里是只传了端口号9297          */  var normalized = normalizeArgs(args);  //  normalized = [{port: 9297}, null];  var options = normalized[0];  var cb = normalized[1];  // 第一次listen的时候会创建,如果非空说明已经listen过  if (this._handle) {    throw new errors.Error("ERR_SERVER_ALREADY_LISTEN");  }  // listen成功后执行的回调  var hasCallback = cb !== null;  if (hasCallback) {    // listen成功的回调    this.once("listening", cb);  }  options = options._handle || options.handle || options;  // 第一种情况,传进来的是一个TCP服务器,而不是需要创建一个服务器  if (options instanceof TCP) {    this._handle = options;    this[async_id_symbol] = this._handle.getAsyncId();    listenIncluster(this, null, -1, -1, backlogFromArgs);    return this;  }  // 第二种,传进来一个对象,并且带了fd  if (typeof options.fd === "number" && options.fd >= 0) {    listenIncluster(this, null, null, null, backlogFromArgs, options.fd);    return this;  }  // 创建一个tcp服务器  var backlog;  if (typeof options.port === "number" || typeof options.port === "string") {    backlog = options.backlog || backlogFromArgs;    // 第三种 启动一个TCP服务器,传了host则先进行DNS解析    if (options.host) {      lookupAndListen(        this,        options.port | 0,        options.host,        backlog,        options.exclusive      );    } else {      listenIncluster(        this,        null,        options.port | 0,        4,        backlog,        undefined,        options.exclusive      );    }    return this;  }};

listenInCluster

在每种方式的最后丢回调用 listenIncluster 方法,该方法主要做的事情是区分 master 进程 和 worker 进程,采用不同的处理策略:

mastr 进程: 直接调用 server._listen 启动监听;worker 进程: 使用 cluster._getServer 处理传入的 server 对象,修改 server._handle 再调用了 server._listen 启动监听;

构建 UDP 服务

UDP 又称用户数据包协议,与 TCP 一样同属于网络层传输层。UDPTCP 最大的不同是 UDP 不是面向链接的。

创建 UDP 服务

创建 UDP 套接字十分简单,UDP 套接字一旦创建,既可以作为客户端发送数据,也可以作为服务端接收数据,下面的代码创建了一个 UDP 套接字,具体代码如下所示:

import dgram from "node:dgram";const server = dgram.createSocket("udp4");server.on("error", (err) => {  console.error(`server error:\n${err.stack}`);  server.close();});server.on("message", (msg, rinfo) => {  console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);});server.on("listening", () => {  const address = server.address();  console.log(`server listening ${address.address}:${address.port}`);});server.bind(3000);

该套接字将接收所有网课上 3000 端口上的消息,在绑定完成后,将触发 listening 事件,会终端执行,会输出 server listening 0.0.0.0:3000 字段。

接下来我们创建一个客户端和服务端进行对话,具体代码如下所示:

import dgram from "node:dgram";import { Buffer } from "node:buffer";const message = Buffer.from("你个叼毛");const client = dgram.createSocket("udp4");client.send(message, 0, message.length, 3000, "localhost", () => {  client.close();});

终端的最终输出结果如下图所示

image.png

UDP 广播

dgram 模块中,可以使用 socket 端口对象的 setBroadcast 方法来进行数据的广播:

socket.setBroadcast(flag);

flag: 当 flagtrue 时,UDP 服务器或者客户端可以利用其所用的 socket 端口对象的 send 方法中的地址修改为广播地址。

服务端的代码定义在 server.js 文件,具体代码如下所示:

import dgram from "dgram";const server = dgram.createSocket("udp4");server.on("message", function (msg, rinfo) {  console.log(    "server got: " + msg + " from " + rinfo.address + ":" + rinfo.port  );});server.on("listening", function () {  var address = server.address();  console.log("server listening " + address.address + ":" + address.port);});server.bind(3000);

客户端的代码定义在 server.js 文件,具体代码如下定义:

import dgram from "dgram";import { Buffer } from "buffer";const socket = dgram.createSocket("udp4");const params = process.argv.splice(2);socket.bind(function () {  socket.setBroadcast(true);});const message = Buffer.from(...params);socket.send(message, 0, message.length, 3000, "255.255.255.255", () => {  socket.close();});

具体运行效果如下图所示:

动画.gif

更多node相关知识,请访问:nodejs 教程!

以上就是一文浅析Node中的TCP和UDP的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/541414.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
苹果江郎才尽?前营销高管不看好苹果AR/VR头显。
上一篇 2025年11月9日 15:28:51
java框架安全架构设计应包含哪些安全审计模块?
下一篇 2025年11月9日 15:28:58

相关推荐

  • 为什么VSCode的语法高亮有时会失效?

    语法高亮失效通常由语言模式识别错误、扩展冲突或配置问题导致。1. 检查右下角语言模式并手动切换为正确类型,确保文件有正确扩展名;2. 禁用近期安装的扩展或以 code –disable-extensions 启动排查冲突;3. 切换至默认主题并检查 settings.json 是否覆盖颜…

    2026年9月21日
    500
  • VSCode的括号着色功能如何帮助你避免语法错误?

    VSCode括号着色功能通过彩色高亮匹配括号,帮助用户直观识别嵌套结构、提升代码可读性,并快速发现遗漏或多余括号,减少语法错误。 VSCode的括号着色功能通过视觉方式帮你快速识别代码中的匹配和嵌套结构,减少语法错误的发生。当你在编写代码时,成对出现的括号(如()、[]、{})会被高亮显示为相同或相…

    2026年9月21日
    000
  • 如何制作抖音点单小程序:全面指南与实用技巧

    引言: 随着移动互联网的飞速发展,抖音已不仅仅是短视频平台,更成为商家连接用户的重要入口。越来越多企业开始关注抖音点单小程序的搭建,以提升服务效率和用户体验。本文将为您系统讲解抖音点单小程序的制作流程,并分享实用技巧与真实案例,助您快速打造专属的小程序,实现流量变现与销售增长。 1. 明确核心需求与…

    2026年9月21日
    200
  • PHP播放HLS视频流的方法_PHP播放HLS视频流方法

    答案:PHP通过权限控制和文件代理实现HLS流安全分发,前端使用HTML5视频标签和hls.js播放。具体描述:HLS将视频切为.ts片段并用.m3u8索引,PHP后端可校验用户权限、防止盗链,动态输出.m3u8或.ts内容;前端通过video标签加载stream.php?id=1,结合hls.js…

    2026年9月20日
    000
  • VSCode有哪些必备的插件?

    EditorConfig for VS Code统一代码风格,2. Prettier自动格式化多语言代码,3. ESLint检查JS/TS错误并集成Prettier,4. GitLens增强Git可视化,5. Path Intellisense补全文件路径,6. 括号高亮提升嵌套识别,7. Auto…

    2026年9月20日
    1000
  • Via浏览器怎么让地址栏显示完整的网址链接_Via浏览器显示完整网址的设置方法

    1、打开Via浏览器设置,进入高级设置中的地址栏选项,开启“显示完整网址”功能;2、在外观设置中关闭简洁模式或极简地址栏,以恢复协议头和路径显示;3、高级用户可借助自定义脚本强制输出完整URL,通过工具箱添加执行脚本实现。 如果您在使用Via浏览器时发现地址栏默认只显示域名而隐藏了完整的网址链接,可…

    2026年9月20日
    000
  • ChatGPT代码会出错吗_AI编程中5个常见错误及解决方法

    AI编程中常见错误包括语法不匹配、逻辑遗漏、API误用、安全漏洞和集成困难,需通过版本明确、测试验证、文档核对、安全扫描和上下文补充等方式解决,结合人工审查与测试才能确保代码质量。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ ChatGP…

    2026年9月20日
    100
  • VSCode的扩展推荐是怎么工作的?

    VSCode的扩展推荐基于用户行为和项目环境智能生成,当你打开.py文件时会推荐Python相关工具,打开.ts、.vue等文件则触发对应语言插件;系统通过分析package.json、requirements.txt等依赖文件识别技术栈,推荐Docker、ESLint等匹配扩展;同时记录常用操作如…

    2026年9月20日
    000
  • 怎么在VSCode里运行HTML文件?

    使用Live Server扩展是VSCode运行HTML文件最简单的方法,安装后右键选择“Open with Live Server”即可在浏览器中自动打开并实时预览网页内容。 在VSCode里运行HTML文件,最简单的方法是借助浏览器打开。VSCode本身不直接运行HTML,但可以快速预览和调试。…

    2026年9月20日
    000
  • 百度AI如何提升企业运营效率_百度AI企业运营效率提升策略

    通过引入百度AI技术优化企业运营,1. 部署智能电话客服系统,利用语音识别与UNIT技术实现自动应答;2. 实施智能语音质检,将通话转文本并分析情绪与风险;3. 启用人脸识别考勤,提升安全性与效率;4. 构建OCR单据识别体系,实现信息自动录入,全面提升电销、客服与办公协同效率。 ☞☞☞AI 智能聊…

    2026年9月20日
    100
  • VSCode的侧边栏图标代表什么?

    资源管理器(文件夹图标)用于管理项目文件结构,支持新建、重命名、删除和拖拽操作;2. 搜索(放大镜图标)实现全局文本查找与替换,支持正则表达式及范围筛选;3. 源代码管理(分支图标)集成Git功能,可查看变更、提交代码并同步远程仓库;4. 运行和调试(虫子图标)支持断点调试、变量监控及多语言启动配置…

    2026年9月20日
    000
  • 如何为VSCode添加自定义主题?

    可通过安装扩展、手动添加JSON文件或使用Yeoman生成器创建主题。首先安装现成主题扩展并从颜色主题列表中选择应用;其次将自定义主题JSON文件放入用户themes目录后在命令面板中启用;最后可用yo code生成项目开发发布主题,需编辑配色文件并预览效果。 为 VSCode 添加自定义主题有几种…

    2026年9月20日
    000
  • Linux如何将进程放入后台运行

    将Linux进程放入后台运行主要有四种方法:使用&符号在启动时放入后台;通过Ctrl+Z暂停后用bg继续运行;结合nohup与&防止会话关闭导致终止;使用screen或tm…

    2026年9月20日
    000
  • edge浏览器无法打开本地HTML文件或显示空白怎么办_Edge浏览器打开本地HTML文件失败解决方法

    1、检查文件路径并选择Edge打开,确保路径为纯英文;2、在edge://flags中启用“Allow file access from files”;3、使用开发者工具排查资源加载错误;4、通过命令行启动Edge绕过安全限制;5、推荐使用npx live-server搭建本地服务器运行HTML文件…

    2026年9月20日
    000
  • 怎样在VSCode中为不同文件类型设置不同的缩进?

    在VSCode中可为不同文件类型设置缩进,通过settings.json按语言ID配置,如Python用4空格、HTML用2空格,保存后自动生效并覆盖全局设置。 在 VSCode 中为不同文件类型设置不同的缩进,可以通过配置语言特定的设置来实现。VSCode 支持按语言 ID 覆盖编辑器的缩进行为,…

    2026年9月20日
    100
  • Via浏览器怎么设置在新标签页打开链接_Via浏览器调整链接打开方式的方法

    Via浏览器可通过长按链接选择“在新标签页中打开”;2. 在设置中修改“标签页行为”为始终在新标签页打开;3. 通过手势设置自定义快捷操作;4. 高级用户可启用JavaScript脚本强制所有链接在新标签页打开。 如果您在浏览网页时希望链接能够在新标签页中打开,以方便多任务处理或保留当前页面,可以通…

    2026年9月20日
    200
  • VSCode的悬浮提示信息如何自定义?

    通过JSDoc或docstring添加注释可直接影响VSCode悬浮提示内容,如JavaScript/TypeScript中使用/* /格式、Python中使用三引号文档字符串,配合Pylance等扩展增强显示;安装语言支持扩展可提升提示丰富度;高级场景可通过开发自定义语言服务器,在textDocu…

    2026年9月20日
    500
  • VSCode怎么把代码整齐_VSCode代码格式化与自动排版功能设置教程

    VSCode代码格式化可显著提升代码可读性与质量,通过内置功能及Prettier、ESLint插件实现。安装Prettier和ESLint插件后,需在设置中配置默认格式化工具、保存时自动格式化(Format On Save)、粘贴时格式化(Format On Paste)及保存时修复ESLint问题…

    2026年9月20日
    100
  • 怎样在VSCode中重命名变量或文件?

    使用F2键可快速重命名变量或文件,VSCode会自动更新符号引用,支持多语言,重命名文件时需注意导入路径可能需手动调整。 在 VSCode 中重命名变量或文件非常方便,可以通过内置的重构功能快速完成,同时保持代码的一致性。 重命名变量(符号重命名) 当你想重命名代码中的变量、函数或类时,VSCode…

    2026年9月20日
    100
  • VSCode的扩展推荐是基于什么?

    答案是VSCode根据文件类型、项目配置、用户行为和社区流行度智能推荐扩展。打开.py文件时提示Python扩展,存在package.json则推荐JS工具,频繁格式化代码会推荐Prettier,安装ESLint后可能推荐Prettier,高评分和官方扩展优先展示。 VSCode的扩展推荐主要基于你…

    2026年9月20日
    000

发表回复

登录后才能评论
关注微信