Node.js v18.18.2 文档


目录

UDP/数据报套接字#

稳定性:2 - 稳定

源代码: lib/dgram.js

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);
// Prints: server listening 0.0.0.0:41234const dgram = require('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);
// Prints: server listening 0.0.0.0:41234

类:dgram.Socket#

封装数据报功能。

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

事件:'close'#

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

事件:'connect'#

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

事件:'error'#

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

事件:'listening'#

一旦 dgram.Socket可寻址并可以接收数据,就会发出 'listening'事件。这种情况要么通过socket.bind()显式发生,要么在第一次使用socket.send()发送数据时隐式发生。在dgram.Socket监听之前,底层系统资源并不存在,并且socket.address()socket.setTTL() 等调用将会失败。

事件:'message'#

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

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

socket.addMembership(multicastAddress[, multicastInterface])#

告诉内核使用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(); // Works ok.
  cluster.fork(); // Fails with EADDRINUSE.
} else {
  const s = dgram.createSocket('udp4');
  s.bind(1234, () => {
    s.addMembership('224.0.0.114');
  });
}const cluster = require('node:cluster');
const dgram = require('node:dgram');

if (cluster.isPrimary) {
  cluster.fork(); // Works ok.
  cluster.fork(); // Fails with EADDRINUSE.
} else {
  const s = dgram.createSocket('udp4');
  s.bind(1234, () => {
    s.addMembership('224.0.0.114');
  });
}

socket.addSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface])#

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

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

socket.address()#

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

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

socket.bind([port][, address][, callback])#

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

指定'listening'事件侦听器并将callback传递 给socket.bind()方法没有什么害处,但不是很有用。

绑定数据报套接字使 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);
// Prints: server listening 0.0.0.0:41234const dgram = require('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);
// Prints: server listening 0.0.0.0:41234

socket.bind(options[, callback])#

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

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

指定'listening'事件侦听器并将callback传递 给socket.bind()方法没有害处,但不是很有用。

options对象可能包含一个附加的exclusive属性,该属性在将dgram.Socket对象与cluster模块一起使用时使用。当 exclusive设置为false(默认值)时,集群工作线程将使用相同的底层套接字句柄,允许共享连接处理职责。但是,当exclusivetrue时,句柄不会共享,并且尝试端口共享会导致错误。

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

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

下面显示了侦听独占端口的套接字示例。

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

socket.close([callback])#

  • callback <Function>当套接字关闭时调用。

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

socket[Symbol.asyncDispose]()#

稳定性:1 - 实验性

调用socket.close()并返回一个在套接字关闭时履行的 Promise 。

socket.connect(port[, address][, callback])#

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

socket.disconnect()#

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

socket.dropMembership(multicastAddress[, multicastInterface])#

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

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

socket.dropSourceSpecificMembership(sourceAddress, groupAddress[, multicastInterface])#

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

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

socket.getRecvBufferSize()#

  • 返回:<number> SO_RCVBUF套接字接收缓冲区大小(以字节为单位)。

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

socket.getSendBufferSize()#

  • 返回:<number> SO_SNDBUF套接字发送缓冲区大小(以字节为单位)。

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

socket.getSendQueueSize()#

  • 返回:<number>排队等待发送的字节数。

socket.getSendQueueCount()#

  • 返回:<number>当前在队列中等待处理的发送请求数。

socket.ref()#

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

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

socket.ref()方法返回对套接字的引用,以便可以链接调用。

socket.remoteAddress()#

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

socket.send(msg[, offset, length][, port][, address][, callback])#

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

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

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

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

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

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

偏移量和长度是可选的,但如果使用其中任何一个,则必须设置两者。仅当第一个参数为BufferTypedArrayDataView时才支持它们。

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

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

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();
});const dgram = require('node:dgram');
const { Buffer } = require('node:buffer');

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

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

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();
});const dgram = require('node:dgram');
const { Buffer } = require('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();
  });
});const dgram = require('node:dgram');
const { Buffer } = require('node:buffer');

const message = Buffer.from('Some bytes');
const client = dgram.createSocket('udp4');
client.connect(41234, 'localhost', (err) => {
  client.send(message, (err) => {
    client.close();
  });
});
关于 UDP 数据报大小的注意事项#

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的数据报将不起作用,因为数据包将被静默丢弃,而不会通知源数据未到达其预期接收者。

socket.setBroadcast(flag)#

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

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

socket.setMulticastInterface(multicastInterface)#

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

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

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

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

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

示例:IPv6 组播出接口#

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

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

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

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

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

socket.bind(1234, () => {
  socket.setMulticastInterface('::%2');
}); 
示例:IPv4 组播出接口#

所有系统都使用所需物理接口上主机的 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 '::')可用于将套接字默认传出接口的控制权返回给系统,以用于将来的多播数据包。

socket.setMulticastLoopback(flag)#

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

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

socket.setMulticastTTL(ttl)#

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

ttl参数可以介于 0 和 255 之间。大多数系统上的默认值为1

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

socket.setRecvBufferSize(size)#

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

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

socket.setSendBufferSize(size)#

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

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

socket.setTTL(ttl)#

设置IP_TTL套接字选项。虽然 TTL 通常代表“生存时间”,但在这种情况下,它指定允许数据包通过的 IP 跳数。每个转发数据包的路由器或网关都会递减 TTL。如果 TTL 被路由器减为 0,则不会转发。更改 TTL 值通常是在网络探测或多播时进行的。

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

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

socket.unref()#

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

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

socket.unref()方法返回对套接字的引用,以便可以链接调用。

node:dgram模块功能#

dgram.createSocket(options[, callback])#

  • options <对象>可用选项有:
    • type <string>套接字系列。必须是'udp4''udp6'。必需的。
    • reuseAddr <boolean>true socket.bind()将重用该地址时,即使另一个进程已经在其上绑定了套接字。 默认值: false
    • ipv6Only <boolean>将ipv6Only设置为true将禁用双堆栈支持,即绑定到地址::不会使 0.0.0.0受到约束。默认值: false
    • recvBufferSize <number>设置SO_RCVBUF套接字值。
    • sendBufferSize <number>设置SO_SNDBUF套接字值。
    • lookup <Function>自定义查找函数。默认值: dns.lookup()
    • signal <AbortSignal>可用于关闭套接字的 AbortSignal。
  • 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}`);
});
// Later, when you want to close the server.
controller.abort(); 

dgram.createSocket(type[, callback])#

创建指定typedgram.Socket对象。

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

NodeJS中文文档为Read dev Docs平台提供托管,中文NodeJS文档均由英文版NodeJS文档翻译,版权属于nodejs.org