On this page

UDP/数据报套接字

History
Source Code: lib/dgram.js

稳定性:2 - 稳定

node:dgram 模块提供了 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(41234);
// 输出:server listening 0.0.0.0:41234

类:dgram.Socket

History
  • 继承:{EventEmitter}

封装了数据报功能。

dgram.Socket 的新实例使用 dgram.createSocket() 创建。 不应使用 new 关键字来创建 dgram.Socket 实例。

事件:'close'

History

当套接字被 close() 关闭后,会发出 'close' 事件。 一旦触发,此套接字上将不再发出新的 'message' 事件。

事件:'connect'

History

当套接字因成功调用 connect() 而关联到远程地址后,会发出 'connect' 事件。

事件:'error'

History
Attributes
exception:<Error>

每当发生任何错误时,都会发出 'error' 事件。事件处理函数会被传入一个 Error 对象。

事件:'listening'

History

一旦 dgram.Socket 可寻址并能接收数据,就会发出 'listening' 事件。 这可以通过显式调用 socket.bind() 发生,或者在使用 socket.send() 首次发送数据时隐式发生。 在 dgram.Socket 监听之前,底层系统资源不存在,诸如 socket.address()socket.setTTL() 之类的调用将失败。

当套接字上有新的数据报可用时,会发出 'message' 事件。 事件处理函数会被传入两个参数:msgrinfo

  • msg {Buffer} 消息。
  • rinfo <Object> 远程地址信息。
    Attributes
    address:<string>
    发送者地址。
    family:<string>
    地址族( 'IPv4''IPv6' )。
    发送者端口。
    消息大小。

如果传入数据包的源地址是 IPv6 链路本地地址,则接口名称会被添加到 address 中。 例如,在 en0 接口上接收到的数据包的地址字段可能设置为 'fe80::2618:1234:ab11:3b9c%en0',其中 '%en0' 是作为区域 ID 后缀的接口名称。

M

socket.addMembership

History
socket.addMembership(multicastAddress, multicastInterface?): void
Attributes
multicastAddress:<string>
multicastInterface:<string>

告诉内核使用 IP_ADD_MEMBERSHIP 套接字选项加入给定 multicastAddressmulticastInterface 处的多播组。 如果未指定 multicastInterface 参数,操作系统将选择一个接口并将成员资格添加到该接口。 要将成员资格添加到每个可用接口,请多次调用 addMembership,每个接口一次。

当在未绑定的套接字上调用时,此方法将隐式绑定到随机端口,监听所有接口。

当在多个 cluster 工作进程之间共享 UDP 套接字时,socket.addMembership() 函数只能调用一次,否则将发生 EADDRINUSE 错误:

import cluster from 'node:cluster';
import dgram from 'node:dgram';

if (cluster.isPrimary) {
  cluster.fork(); // 正常工作。
  cluster.fork(); // 失败,抛出 EADDRINUSE。
} else {
  const s = dgram.createSocket('udp4');
  s.bind(1234, () => {
    s.addMembership('224.0.0.114');
  });
}
M

socket.addSourceSpecificMembership

History
socket.addSourceSpecificMembership(sourceAddress, groupAddress, multicastInterface?): void
Attributes
sourceAddress:<string>
groupAddress:<string>
multicastInterface:<string>

告诉内核使用 multicastInterfaceIP_ADD_SOURCE_MEMBERSHIP 套接字选项加入给定 sourceAddressgroupAddress 处的源特定多播通道。 如果未指定 multicastInterface 参数,操作系统将选择一个接口并将成员资格添加到该接口。 要将成员资格添加到每个可用接口,请多次调用 socket.addSourceSpecificMembership(),每个接口一次。

当在未绑定的套接字上调用时,此方法将隐式绑定到随机端口,监听所有接口。

M

socket.address

History
socket.address(): void

返回一个包含套接字地址信息的对象。 对于 UDP 套接字,此对象将包含 addressfamilyport 属性。

如果在未绑定的套接字上调用此方法,将抛出 EBADF

M

socket.bind

History
socket.bind(port?, address?, callback?): void
Attributes
address:<string>
callback:<Function>
无参数。绑定完成时调用。

对于 UDP 套接字,使 dgram.Socket 在指定的 port 和可选的 address 上监听数据报消息。 如果未指定 port 或为 0,操作系统将尝试绑定到随机端口。 如果未指定 address,操作系统将尝试监听所有地址。 绑定完成后,会发出 'listening' 事件并调用可选的 callback 函数。

同时指定 'listening' 事件监听器并向 socket.bind() 方法传递 callback 并无害处,但用处不大。

绑定的数据报套接字会保持 Node.js 进程运行以接收数据报消息。

如果绑定失败,将生成 'error' 事件。在极少数情况下(例如尝试使用已关闭的套接字进行绑定),可能会抛出 Error

监听端口 41234 的 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(41234);
// 输出:server listening 0.0.0.0:41234
M

socket.bind

History
socket.bind(options, callback?): void
Attributes
options:<Object>
必需。支持以下属性:
address:<string>
exclusive:<boolean>
callback:<Function>

对于 UDP 套接字,使 dgram.Socket 在作为第一个参数传入的 options 对象的属性中指定的 port 和可选 address 上监听数据报消息。 如果未指定 port 或为 0,操作系统将尝试绑定到随机端口。 如果未指定 address,操作系统将尝试监听所有地址。 绑定完成后,会发出 'listening' 事件并调用可选的 callback 函数。

options 对象可能包含一个 fd 属性。 当设置大于 0fd 时,它将包装具有给定文件描述符的现有套接字。 在这种情况下,portaddress 属性将被忽略。

同时指定 'listening' 事件监听器并向 socket.bind() 方法传递 callback 并无害处,但用处不大。

options 对象可能包含一个额外的 exclusive 属性,该属性在与 cluster 模块一起使用 dgram.Socket 对象时使用。 当 exclusive 设置为 false(默认值)时,集群工作进程将使用相同的底层套接字句柄,允许共享连接处理任务。 然而,当 exclusivetrue 时,句柄不共享,尝试共享端口会导致错误。 创建 dgram.Socket 时将 reusePort 选项设置为 true 会导致调用 socket.bind()exclusive 始终为 true

绑定的数据报套接字会保持 Node.js 进程运行以接收数据报消息。

如果绑定失败,将生成 'error' 事件。在极少数情况下(例如尝试使用已关闭的套接字进行绑定),可能会抛出 Error

下面展示了一个监听独占端口的套接字示例。

socket.bind({
  address: 'localhost',
  port: 8000,
  exclusive: true,
});
M

socket.close

History
socket.close(callback?): void
Attributes
callback:<Function>
当套接字已关闭时调用。

关闭底层套接字并停止监听其上的数据。 如果提供了回调,它将被添加为 'close' 事件的监听器。

M

socket[Symbol.asyncDispose]

History
socket[Symbol.asyncDispose](): void

调用 socket.close() 并返回一个 promise,当套接字关闭时该 promise 会被兑现。

M

socket.connect

History
socket.connect(port, address?, callback?): void
Attributes
address:<string>
callback:<Function>
当连接完成或出错时调用。

dgram.Socket 关联到远程地址和端口。 由此句柄发送的每条消息都会自动发送到该目的地。 此外,套接字将只接收来自该远程对等方的消息。 尝试在已连接的套接字上调用 connect() 将导致 ERR_SOCKET_DGRAM_IS_CONNECTED 异常。 如果未提供 address,默认将使用 '127.0.0.1'(对于 udp4 套接字)或 '::1'(对于 udp6 套接字)。 连接完成后,会发出 'connect' 事件并调用可选的 callback 函数。 如果失败,将调用 callback,如果失败,则发出 'error' 事件。

M

socket.disconnect

History
socket.disconnect(): void

一个同步函数,用于将已连接的 dgram.Socket 与其远程地址解除关联。 尝试在未绑定或已断开的套接字上调用 disconnect() 将导致 ERR_SOCKET_DGRAM_NOT_CONNECTED 异常。

M

socket.dropMembership

History
socket.dropMembership(multicastAddress, multicastInterface?): void
Attributes
multicastAddress:<string>
multicastInterface:<string>

指示内核使用 IP_DROP_MEMBERSHIP 套接字选项离开 multicastAddress 处的多播组。 当套接字关闭或进程终止时,内核会自动调用此方法,因此大多数应用程序永远没有理由调用此方法。

如果未指定 multicastInterface,操作系统将尝试在所有有效接口上删除成员资格。

M

socket.dropSourceSpecificMembership

History
socket.dropSourceSpecificMembership(sourceAddress, groupAddress, multicastInterface?): void
Attributes
sourceAddress:<string>
groupAddress:<string>
multicastInterface:<string>

指示内核使用 IP_DROP_SOURCE_MEMBERSHIP 套接字选项离开给定 sourceAddressgroupAddress 处的源特定多播通道。 当套接字关闭或进程终止时,内核会自动调用此方法,因此大多数应用程序永远没有理由调用此方法。

如果未指定 multicastInterface,操作系统将尝试在所有有效接口上删除成员资格。

M

socket.getRecvBufferSize

History
socket.getRecvBufferSize(): void
  • 返回:<number> SO_RCVBUF 套接字接收缓冲区大小(字节)。

如果在未绑定的套接字上调用此方法,将抛出 ERR_SOCKET_BUFFER_SIZE

M

socket.getSendBufferSize

History
socket.getSendBufferSize(): void
  • 返回:<number> SO_SNDBUF 套接字发送缓冲区大小(字节)。

如果在未绑定的套接字上调用此方法,将抛出 ERR_SOCKET_BUFFER_SIZE

M

socket.getSendQueueSize

History
socket.getSendQueueSize(): void
  • 返回:<number> 排队等待发送的字节数。
M

socket.getSendQueueCount

History
socket.getSendQueueCount(): void
  • 返回:<number> 当前队列中等待处理的发送请求数。
M

socket.ref

History
socket.ref(): void

默认情况下,绑定套接字会导致只要套接字打开,它就会阻止 Node.js 进程退出。 可以使用 socket.unref() 方法将套接字从保持 Node.js 进程活动的引用计数中排除。 socket.ref() 方法将套接字添加回引用计数并恢复默认行为。

多次调用 socket.ref() 不会产生额外效果。

socket.ref() 方法返回对套接字的引用,因此调用可以链式进行。

M

socket.remoteAddress

History
socket.remoteAddress(): void

返回一个包含远程端点的 addressfamilyport 的对象。 如果套接字未连接,此方法将抛出 ERR_SOCKET_DGRAM_NOT_CONNECTED 异常。

socket.send(msg, offset?, length?, port?, address?, callback?): void
Attributes
要发送的消息。
offset:<integer>
缓冲区中消息开始的偏移量。
length:<integer>
消息中的字节数。
目标端口。
address:<string>
目标主机名或 IP 地址。
callback:<Function>
当消息已发送时调用。

在套接字上广播数据报。 对于无连接套接字,必须指定目标 portaddress。 另一方面,已连接的套接字将使用其关联的远程端点,因此不得设置 portaddress 参数。

msg 参数包含要发送的消息。 根据其类型,可能适用不同的行为。 如果 msgBuffer、任何 TypedArrayDataView,则 offsetlength 分别指定 Buffer 内消息开始的偏移量和消息中的字节数。 如果 msgString,则它会自动转换为具有 'utf8' 编码的 Buffer。 对于包含多字节字符的消息,offsetlength 将相对于 [字节长度][] 而不是字符位置进行计算。 如果 msg 是数组,则不得指定 offsetlength

address 参数是一个字符串。 如果 address 的值是主机名,将使用 DNS 来解析主机的地址。 如果未提供 address 或其他为 nullish,默认将使用 '127.0.0.1'(对于 udp4 套接字)或 '::1'(对于 udp6 套接字)。

如果套接字之前未通过调用 bind 进行绑定,则套接字被分配一个随机端口号并绑定到“所有接口”地址(udp4 套接字为 '0.0.0.0'udp6 套接字为 '::0')。

可以指定一个可选的 callback 函数,作为报告 DNS 错误或确定何时可以安全地重用 buf 对象的一种方式。 DNS 查找会将发送时间延迟至少一个 Node.js 事件循环周期。

确定数据报已发送的唯一方法是使用 callback。 如果发生错误并给出了 callback,则错误将作为第一个参数传递给 callback。 如果未给出 callback,则错误会作为 'error' 事件在 socket 对象上发出。

Offset 和 length 是可选的,但如果使用了其中任何一个,则两者都必须设置。 仅当第一个参数是 BufferTypedArrayDataView 时才支持它们。

如果在未绑定的套接字上调用此方法,将抛出 ERR_SOCKET_BAD_PORT

localhost 上的端口发送 UDP 数据包的示例;

import dgram from 'node:dgram';
import { Buffer } from 'node:buffer';

const message = Buffer.from('Some bytes');
const client = dgram.createSocket('udp4');
client.send(message, 41234, 'localhost', (err) => {
  client.close();
});

127.0.0.1 上的端口发送由多个缓冲区组成的 UDP 数据包的示例;

import dgram from 'node:dgram';
import { Buffer } from 'node:buffer';

const buf1 = Buffer.from('Some ');
const buf2 = Buffer.from('bytes');
const client = dgram.createSocket('udp4');
client.send([buf1, buf2], 41234, (err) => {
  client.close();
});

发送多个缓冲区可能更快或更慢,具体取决于应用程序和操作系统。 运行基准测试以确定具体情况下的最佳策略。 然而,一般来说,发送多个缓冲区更快。

使用连接到 localhost 上端口的套接字发送 UDP 数据包的示例:

import dgram from 'node:dgram';
import { Buffer } from 'node:buffer';

const message = Buffer.from('Some bytes');
const client = dgram.createSocket('udp4');
client.connect(41234, 'localhost', (err) => {
  client.send(message, (err) => {
    client.close();
  });
});

IPv4/v6 数据报的最大大小取决于 MTU(最大传输单元)和 Payload Length 字段大小。

  • Payload Length 字段宽 16 位,这意味着正常负载不能超过 64K 八位字节,包括互联网头部和数据(65,507 字节 = 65,535 − 8 字节 UDP 头部 − 20 字节 IP 头部);这对于环回接口通常是正确的,但如此长的数据报消息对于大多数主机和网络来说是不切实际的。

  • MTU 是给定链路层技术可以支持的数据报消息的最大大小。 对于任何链路,IPv4 强制要求最小 MTU 为 68 八位字节,而 IPv4 的推荐 MTU 为 576(通常推荐作为拨号类型应用程序的 MTU),无论它们是完整到达还是分片到达。

    对于 IPv6,最小 MTU 为 1280 八位字节。 但是,强制性的最小分片重组缓冲区大小为 1500 八位字节。 68 八位字节的值非常小,因为大多数当前的链路层技术(如以太网)的最小 MTU 为 1500。

不可能预先知道数据包可能经过的每个链路的 MTU。 发送大于接收者 MTU 的数据报将不起作用,因为数据包将被静默丢弃,而不会通知源数据未到达其预期接收者。

M

socket.setBroadcast

History
socket.setBroadcast(flag): void
Attributes

设置或清除 SO_BROADCAST 套接字选项。 当设置为 true 时,UDP 数据包可以发送到本地接口的广播地址。

如果在未绑定的套接字上调用此方法,将抛出 EBADF

M

socket.setMulticastInterface

History
socket.setMulticastInterface(multicastInterface): void
Attributes
multicastInterface:<string>

本节中对 scope 的所有引用均指 [IPv6 区域索引][],其由 RFC 4007 定义。在字符串形式中,带有范围索引的 IP 写为 'IP%scope',其中 scope 是接口名称或接口编号。

将套接字的默认传出多播接口设置为所选接口或返回系统接口选择。 multicastInterface 必须是来自套接字族的 IP 的有效字符串表示。

对于 IPv4 套接字,这应该是为所需物理接口配置的 IP。 所有在套接字上发送到多播的数据包都将在此调用最近一次成功使用所确定的接口上发送。

对于 IPv6 套接字,multicastInterface 应包含一个范围以指示接口,如下例所示。 在 IPv6 中,单个 send 调用也可以在地址中使用显式范围,因此只有发送到未指定显式范围的多播地址的数据包才会受此调用最近一次成功使用的影响。

如果在未绑定的套接字上调用此方法,将抛出 EBADF

在大多数系统上,其中范围格式使用接口名称:

const socket = dgram.createSocket('udp6');

socket.bind(1234, () => {
  socket.setMulticastInterface('::%eth1');
});

在 Windows 上,其中范围格式使用接口编号:

const socket = dgram.createSocket('udp6');

socket.bind(1234, () => {
  socket.setMulticastInterface('::%2');
});

所有系统使用所需物理接口上的主机 IP:

const socket = dgram.createSocket('udp4');

socket.bind(1234, () => {
  socket.setMulticastInterface('10.0.0.2');
});

对尚未准备好发送或不再打开的套接字的调用可能会抛出 Not running Error

如果 multicastInterface 无法解析为 IP,则抛出 EINVAL System Error

在 IPv4 上,如果 multicastInterface 是有效地址但不匹配任何接口,或者如果地址不匹配族,则抛出 System Error,例如 EADDRNOTAVAILEPROTONOSUP

在 IPv6 上,大多数指定或省略范围的错误将导致套接字继续使用(或返回到)系统的默认接口选择。

套接字地址族的 ANY 地址(IPv4 '0.0.0.0' 或 IPv6 '::')可用于将套接字默认传出接口的控制权返回给系统,用于未来的多播数据包。

M

socket.setMulticastLoopback

History
socket.setMulticastLoopback(flag): void
Attributes

设置或清除 IP_MULTICAST_LOOP 套接字选项。 当设置为 true 时,多播数据包也将在本地接口上接收。

如果在未绑定的套接字上调用此方法,将抛出 EBADF

M

socket.setMulticastTTL

History
socket.setMulticastTTL(ttl): void
Attributes

设置 IP_MULTICAST_TTL 套接字选项。 虽然 TTL 通常代表“生存时间”,但在此上下文中,它指定数据包允许通过的 IP 跳数,特别是对于多播流量。 每个转发数据包的路由器或网关都会递减 TTL。 如果路由器将 TTL 递减到 0,则不会转发它。

ttl 参数可能在 0 到 255 之间。 大多数系统上的默认值为 1

如果在未绑定的套接字上调用此方法,将抛出 EBADF

M

socket.setRecvBufferSize

History
socket.setRecvBufferSize(size): void
Attributes

设置 SO_RCVBUF 套接字选项。 设置最大套接字接收缓冲区(字节)。

如果在未绑定的套接字上调用此方法,将抛出 ERR_SOCKET_BUFFER_SIZE

M

socket.setSendBufferSize

History
socket.setSendBufferSize(size): void
Attributes

设置 SO_SNDBUF 套接字选项。 设置最大套接字发送缓冲区(字节)。

如果在未绑定的套接字上调用此方法,将抛出 ERR_SOCKET_BUFFER_SIZE

M

socket.setTTL

History
socket.setTTL(ttl): void
Attributes

设置 IP_TTL 套接字选项。 虽然 TTL 通常代表“生存时间”,但在此上下文中,它指定数据包允许通过的 IP 跳数。 每个转发数据包的路由器或网关都会递减 TTL。 如果路由器将 TTL 递减到 0,则不会转发它。 更改 TTL 值通常用于网络探测或多播时。

ttl 参数可能在 1 到 255 之间。 大多数系统上的默认值为 64。

如果在未绑定的套接字上调用此方法,将抛出 EBADF

M

socket.unref

History
socket.unref(): void

默认情况下,绑定套接字会导致只要套接字打开,它就会阻止 Node.js 进程退出。 可以使用 socket.unref() 方法将套接字从保持 Node.js 进程活动的引用计数中排除,允许进程即使套接字仍在监听也能退出。

多次调用 socket.unref() 不会产生额外效果。

socket.unref() 方法返回对套接字的引用,因此调用可以链式进行。

dgram.createSocket(options, callback?): void
Attributes
options:<Object>
可用选项包括:
套接字族。必须是  'udp4''udp6' 。必填。
reuseAddr:<boolean>
当为  true 时, socket.bind() 将复用地址,即使另一个进程已经在该地址上绑定了套接字,但只有一个套接字可以接收数据。 默认值: false
reusePort:<boolean>
当为  true 时, socket.bind() 将复用端口,即使另一个进程已经在该端口上绑定了套接字。传入的数据报将分发给监听套接字。该选项仅在某些平台上可用,例如 Linux 3.9+、DragonFlyBSD 3.6+、FreeBSD 12.0+、Solaris 11.4 和 AIX 7.2.5+。在不支持的平台上,当绑定套接字时此选项会抛出错误。 默认值: false
ipv6Only:<boolean>
将  ipv6Only 设置为 true 将禁用双栈支持,即,绑定到地址 :: 不会导致 0.0.0.0 被绑定。 默认值: false
recvBufferSize:<number>
设置  SO_RCVBUF 套接字值。
sendBufferSize:<number>
设置  SO_SNDBUF 套接字值。
lookup:<Function>
自定义查找函数。 默认值: dns.lookup()
一个可用于关闭套接字的 AbortSignal。
receiveBlockList:<net.BlockList>
receiveBlockList 可用于丢弃发往特定 IP 地址、IP 范围或 IP 子网的入站数据报。如果服务器位于反向代理、NAT 等后面,则此功能不起作用,因为针对阻止列表检查的地址是代理的地址,或 NAT 指定的地址。
sendBlockList:<net.BlockList>
sendBlockList 可用于禁用对特定 IP 地址、IP 范围或 IP 子网的出站访问。
callback:<Function>
作为  'message' 事件的监听器附加。可选。
返回: <dgram.Socket>

创建一个 dgram.Socket 对象。一旦套接字被创建,调用 socket.bind() 将指示套接字开始监听数据报消息。当 addressport 未传递给 socket.bind() 时,该方法会将套接字绑定到随机端口的“所有接口”地址(它对 udp4udp6 套接字都能正确处理)。绑定的地址和端口可以使用 socket.address().addresssocket.address().port 检索。

如果启用了 signal 选项,在相应的 AbortController 上调用 .abort() 类似于在套接字上调用 .close()

const controller = new AbortController();
const { signal } = controller;
const server = dgram.createSocket({ type: 'udp4', signal });
server.on('message', (msg, rinfo) => {
  console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
// 稍后,当你想要关闭服务器时。
controller.abort();
M

dgram.createSocket

History
dgram.createSocket(type, callback?): void
Attributes
要么是  'udp4' 要么是 'udp6'
callback:<Function>
作为  'message' 事件的监听器附加。
返回: <dgram.Socket>

创建指定 typedgram.Socket 对象。

一旦套接字被创建,调用 socket.bind() 将指示套接字开始监听数据报消息。当 addressport 未传递给 socket.bind() 时,该方法会将套接字绑定到随机端口的“所有接口”地址(它对 udp4udp6 套接字都能正确处理)。绑定的地址和端口可以使用 socket.address().addresssocket.address().port 检索。