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.createSocket() 建立 dgram.Socket 的新執行個體。不得使用 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 時,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])#

告訴核心加入特定來源的多播頻道,在給定的 sourceAddressgroupAddress,使用 multicastInterfaceIP_ADD_SOURCE_MEMBERSHIP socket 選項。如果未指定 multicastInterface 參數,作業系統會選擇一個介面並加入成員資格。若要加入每個可用介面的成員資格,請多次呼叫 socket.addSourceSpecificMembership(),每個介面呼叫一次。

在未繫結的套接字上呼叫此方法時,此方法會隱式繫結到隨機埠,並在所有介面上監聽。

socket.address()#

傳回包含 socket 位址資訊的物件。對於 UDP socket,此物件將包含 addressfamilyport 屬性。

如果在未繫結的 socket 上呼叫此方法,將擲回 EBADF

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

對於 UDP socket,會導致 dgram.Socket 在指定的 port 和選用的 address 上監聽資料報訊息。如果未指定 portport0,作業系統會嘗試繫結至隨機 port。如果未指定 address,作業系統會嘗試在所有位址上監聽。繫結完成後,會發出 'listening' 事件,並呼叫選用的 callback 函式。

指定 'listening' 事件監聽器和傳遞 callbacksocket.bind() 方法並無害處,但用處不大。

已繫結的資料報 socket 會讓 Node.js 程序持續執行以接收資料報訊息。

如果繫結失敗,會產生 'error' 事件。在罕見情況下(例如嘗試使用已關閉的 socket 繫結),可能會擲回 Error

監聽 port 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 在作為第一個參數傳遞的 options 物件的屬性傳遞的命名 port 和選用 address 上偵聽資料報訊息。如果未指定 port 或為 0,作業系統會嘗試繫結至隨機埠。如果未指定 address,作業系統會嘗試在所有位址上偵聽。繫結完成後,會發出 'listening' 事件,並呼叫選用的 callback 函式。

options 物件可能包含 fd 屬性。當 fd 大於 0 時,它會封裝具有給定檔案描述子的現有套接字。在此情況下,會忽略 portaddress 的屬性。

指定 'listening' 事件監聽器和傳遞 callbacksocket.bind() 方法並無害處,但用處不大。

options 物件可能包含額外的 exclusive 屬性,當使用 dgram.Socket 物件與 cluster 模組時會使用。當 exclusive 設為 false (預設值) 時,叢集工作執行緒會使用相同的基礎套接字控制代碼,允許共用連線處理職責。但是,當 exclusivetrue 時,不會共用控制代碼,且嘗試共用埠會導致錯誤。

已繫結的資料報 socket 會讓 Node.js 程序持續執行以接收資料報訊息。

如果繫結失敗,會產生 'error' 事件。在罕見情況下(例如嘗試使用已關閉的 socket 繫結),可能會擲回 Error

以下顯示在獨佔埠上偵聽的範例套接字。

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

socket.close([callback])#

  • callback <Function> 在套接字已關閉時呼叫。

關閉基礎套接字,並停止偵聽其上的資料。如果提供 callback,則會將其新增為 'close' 事件的監聽器。

socket[Symbol.asyncDispose]()#

穩定性:1 - 實驗性

呼叫 socket.close() 並傳回一個承諾,在 socket 關閉時履行。

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

dgram.Socket 關聯到遠端位址和埠。此處理常式傳送的每則訊息都會自動傳送到該目的地。此外,socket 只會接收來自該遠端對等方的訊息。嘗試對已連線的 socket 呼叫 connect() 將導致 ERR_SOCKET_DGRAM_IS_CONNECTED 例外。如果未提供 address,預設會使用 '127.0.0.1'(對於 udp4 sockets)或 '::1'(對於 udp6 sockets)。連線完成後,會發出 'connect' 事件,並呼叫選用的 callback 函式。如果發生錯誤,會呼叫 callback,或在呼叫失敗時,發出 'error' 事件。

socket.disconnect()#

一個同步函式,用於將已連線的 dgram.Socket 與其遠端位址取消關聯。嘗試對未繫結或已中斷連線的 socket 呼叫 disconnect() 將導致 ERR_SOCKET_DGRAM_NOT_CONNECTED 例外。

socket.dropMembership(multicastAddress[, multicastInterface])#

指示核心使用 IP_DROP_MEMBERSHIP socket 選項在 multicastAddress 離開多播群組。當 socket 關閉或程序終止時,核心會自動呼叫此方法,因此大多數應用程式永遠不需要呼叫它。

如果未指定 multicastInterface,作業系統會嘗試放棄所有有效介面的成員資格。

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

指示核心使用 IP_DROP_SOURCE_MEMBERSHIP socket 選項在指定的 sourceAddressgroupAddress 離開來源特定多播頻道。當 socket 關閉或程序終止時,核心會自動呼叫此方法,因此大多數應用程式永遠不需要呼叫它。

如果未指定 multicastInterface,作業系統會嘗試放棄所有有效介面的成員資格。

socket.getRecvBufferSize()#

  • 傳回:<number> 以位元組為單位的 SO_RCVBUF socket 接收緩衝區大小。

如果在未繫結的 socket 上呼叫此方法,此方法會擲回 ERR_SOCKET_BUFFER_SIZE

socket.getSendBufferSize()#

  • 傳回:<number> 以位元組為單位的 SO_SNDBUF socket 傳送緩衝區大小。

如果在未繫結的 socket 上呼叫此方法,此方法會擲回 ERR_SOCKET_BUFFER_SIZE

socket.getSendQueueSize()#

  • 傳回:<number> 排隊準備傳送的位元組數目。

socket.getSendQueueCount()#

  • 傳回:<number> 目前在排隊等候處理的傳送要求數目。

socket.ref()#

預設情況下,繫結一個 socket 會導致它在 socket 開啟時阻擋 Node.js 程序退出。socket.unref() 方法可用於將 socket 排除在讓 Node.js 程序保持 active 的參考計數之外。socket.ref() 方法將 socket 加回參考計數並還原預設行為。

多次呼叫 socket.ref() 沒有額外效果。

socket.ref() 方法傳回 socket 的參考,因此可以串連呼叫。

socket.remoteAddress()#

傳回包含遠端端點的 addressfamilyport 的物件。如果 socket 未連線,此方法會擲回 ERR_SOCKET_DGRAM_NOT_CONNECTED 例外。

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

在 Socket 上廣播資料報。對於無連線的 Socket,必須指定目的 portaddress。另一方面,已連線的 Socket 會使用其關聯的遠端端點,因此不得設定 portaddress 參數。

msg 參數包含要傳送的訊息。根據其類型,可能會套用不同的行為。如果 msgBuffer、任何 TypedArrayDataView,則 offsetlength 分別指定訊息在 Buffer 中開始的位置的偏移量和訊息中的位元組數。如果 msg字串,則會自動轉換為編碼為 'utf8'Buffer。對於包含多位元組字元的訊息,offsetlength 將根據 位元組長度 計算,而不是字元位置。如果 msg 是陣列,則不得指定 offsetlength

address 參數為字串。如果 address 的值是主機名稱,將會使用 DNS 解析主機的地址。如果未提供 address 或為 null,預設會使用 '127.0.0.1'(對於 udp4 socket)或 '::1'(對於 udp6 socket)。

如果 socket 尚未透過呼叫 bind 進行繫結,socket 會被指定一個隨機埠號,並繫結到「所有介面」地址(對於 udp4 socket 為 '0.0.0.0',對於 udp6 socket 為 '::0')。

可以指定一個選用的 callback 函式,作為回報 DNS 錯誤或判斷何時可以重複使用 buf 物件的方法。DNS 查詢會延遲至少一個 Node.js 事件迴圈的滴答時間才能傳送。

確定資料包已傳送的唯一方法是使用 callback。如果發生錯誤且提供了 callback,錯誤會作為第一個參數傳遞給 callback。如果未提供 callback,錯誤會在 socket 物件上發出 'error' 事件。

偏移量和長度是選用的,但如果使用其中一個,必須同時設定兩個。僅當第一個參數是 BufferTypedArrayDataView 時才支援它們。

如果在未繫結的 socket 上呼叫此方法,此方法會擲出 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 上的埠的 socket 傳送 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 socket 選項。設為 true 時,UDP 封包可以傳送至本機介面的廣播位址。

如果在未繫結的 socket 上呼叫此方法,將擲回 EBADF

socket.setMulticastInterface(multicastInterface)#

本節中所有範圍的參照都指的是 IPv6 區域索引,其定義於 RFC 4007。在字串形式中,帶有範圍索引的 IP 寫為 'IP%範圍',其中範圍是介面名稱或介面編號。

將 socket 的預設傳出多播介面設定為所選介面或回復為系統介面選取。multicastInterface 必須是來自 socket 家族的 IP 的有效字串表示形式。

對於 IPv4 socket,這應該是為所需的實體介面設定的 IP。傳送至 socket 上的多播的所有封包都將在介面上傳送,而介面是由最近一次成功使用此呼叫所決定的。

對於 IPv6 socket,multicastInterface 應包含一個範圍以指示介面,如下面的範例所示。在 IPv6 中,個別 send 呼叫也可以在位址中使用明確範圍,因此只有傳送至多播位址而未指定明確範圍的封包會受到最近一次成功使用此呼叫的影響。

如果在未繫結的 socket 上呼叫此方法,將擲回 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');
}); 
呼叫結果#

針對未準備好傳送或已不再開啟的 socket 進行呼叫可能會擲回一個未執行 Error

如果無法將 multicastInterface 解析成 IP,則會擲回一個EINVAL System Error

在 IPv4 上,如果 multicastInterface 是有效的位址,但與任何介面不符,或者如果位址與系列不符,則會擲回 System Error,例如 EADDRNOTAVAILEPROTONOSUP

在 IPv6 上,指定或省略範圍時發生的多數錯誤,都會導致 socket 繼續使用(或返回)系統的預設介面選項。

socket 的位址系列的 ANY 位址(IPv4 '0.0.0.0' 或 IPv6 '::')可用於將 socket 預設傳出介面的控制權,傳回給系統以供將來的多播封包使用。

socket.setMulticastLoopback(flag)#

設定或清除 IP_MULTICAST_LOOP socket 選項。設定為 true 時,多播封包也會在本地端介面上接收。

如果在未繫結的 socket 上呼叫此方法,將擲回 EBADF

socket.setMulticastTTL(ttl)#

設定 IP_MULTICAST_TTL socket 選項。雖然 TTL 通常代表「存活時間」,但在這個脈絡中,它指定封包允許傳遞的 IP 跳躍次數,特別是針對多播流量。每個轉送封包的路由器或閘道器都會遞減 TTL。如果 TTL 被路由器遞減至 0,則不會轉送。

ttl 參數可以介於 0 到 255 之間。大多數系統的預設值為 1

如果在未繫結的 socket 上呼叫此方法,將擲回 EBADF

socket.setRecvBufferSize(size)#

設定 SO_RCVBUF socket 選項。設定最大 socket 接收緩衝區(以位元組為單位)。

如果在未繫結的 socket 上呼叫此方法,此方法會擲回 ERR_SOCKET_BUFFER_SIZE

socket.setSendBufferSize(size)#

設定 SO_SNDBUF socket 選項。設定最大 socket 傳送緩衝區(以位元組為單位)。

如果在未繫結的 socket 上呼叫此方法,此方法會擲回 ERR_SOCKET_BUFFER_SIZE

socket.setTTL(ttl)#

設定 IP_TTL socket 選項。雖然 TTL 通常代表「存活時間」,但在這個脈絡中,它指定封包允許通過的 IP 跳躍次數。每一個轉送封包的路由器或閘道器都會遞減 TTL。如果 TTL 被路由器遞減到 0,它就不會被轉送。變更 TTL 值通常用於網路探測或多播。

ttl 參數介於 1 到 255 之間。大多數系統的預設值為 64。

如果在未繫結的 socket 上呼叫此方法,將擲回 EBADF

socket.unref()#

預設情況下,繫結 socket 會導致它在 socket 開啟時阻止 Node.js 程序退出。socket.unref() 方法可用於將 socket 排除在讓 Node.js 程序保持 active 的參考計數之外,允許程序退出,即使 socket 仍在偵聽。

多次呼叫 socket.unref() 沒有額外效果。

socket.unref() 方法傳回 socket 的參考,因此可以串連呼叫。

node:dgram 模組函數#

dgram.createSocket(options[, callback])#

  • options <Object> 可用的選項為
    • type <string> Socket 的類型。必須是 'udp4''udp6'。必要。
    • reuseAddr <boolean>true 時,socket.bind() 會重複使用位址,即使另一個程序已在此位址上繫結 Socket。預設值: false
    • ipv6Only <boolean>ipv6Only 設為 true 會停用雙堆疊支援,亦即繫結至位址 :: 也不會繫結 0.0.0.0預設值: false
    • recvBufferSize <number> 設定 SO_RCVBUF Socket 值。
    • sendBufferSize <number> 設定 SO_SNDBUF Socket 值。
    • lookup <Function> 自訂查詢函式。預設值: dns.lookup()
    • signal <AbortSignal> 可用於關閉 Socket 的 AbortSignal。
  • callback <Function> 附加為 'message' 事件的監聽器。選用。
  • 傳回:<dgram.Socket>

建立一個 dgram.Socket 物件。一旦建立了 socket,呼叫 socket.bind() 將指示 socket 開始監聽資料報訊息。當 addressport 未傳遞給 socket.bind() 方法時,該方法會將 socket 繫結到隨機埠上的「所有介面」地址(它對 udp4udp6 socket 都做正確的事)。繫結的地址和埠可以使用 socket.address().addresssocket.address().port 擷取。

如果啟用 signal 選項,則對應的 AbortController 上呼叫 .abort() 類似於對 socket 呼叫 .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,呼叫 socket.bind() 將指示 socket 開始監聽資料報訊息。當 addressport 未傳遞給 socket.bind() 方法時,該方法會將 socket 繫結到隨機埠上的「所有介面」地址(它對 udp4udp6 socket 都做正確的事)。繫結的地址和埠可以使用 socket.address().addresssocket.address().port 擷取。