# UDP / 數據報套接字
~~~
穩定度: 3 - 穩定
~~~
數據報套接字通過 `require('dgram')` 提供。
重要提醒:`dgram.Socket#bind()` 的行為在 v0.10 中已改變,并且現在它總是異步的。如果您的代碼看起來像這樣:
~~~
var s = dgram.createSocket('udp4');
s.bind(1234);
s.addMembership('224.0.0.114');
~~~
您需要將它改成這樣:
~~~
var s = dgram.createSocket('udp4');
s.bind(1234, function() {
s.addMembership('224.0.0.114');
});
~~~
### dgram.createSocket(type, [callback])
- `type` String 可以是 'udp4' 或 'udp6'
- `callback` Function 可選,會被作為 `message` 事件的監聽器。
- 返回:Socket 對象
創建一個指定類型的數據報 Socket。有效類型包括 `udp4` 和 `udp6`。
接受一個可選的回調,會被添加為 `message` 事件的監聽器。
如果您想接收數據報則可調用 `socket.bind`。`socket.bind()` 會綁定到“所有網絡接口”地址的一個隨機端口(`udp4` 和 `udp6` 皆是如此)。然后您可以通過 `socket.address().address` 和 `socket.address().port` 來取得地址和端口。
### 類: dgram.Socket
dgram Socket 類封裝了數據報功能,可以通過 `dgram.createSocket(type, [callback])` 創建。
### 事件: 'message'
- `msg` Buffer 對象,消息
- `rinfo` Object,遠程地址信息
當套接字中有新的數據報時發生。`msg` 是一個 `Buffer`,`rinfo` 是一個包含了發送者地址信息的對象:
~~~
socket.on('message', function(msg, rinfo) {
console.log('收到 %d 字節,來自 %s:%d\n',
msg.length, rinfo.address, rinfo.port);
});
~~~
### 事件: 'listening'
當一個套接字開始監聽數據報時產生。它會在 UDP 套接字被創建時發生。
### 事件: 'close'
當一個套接字被 `close()` 關閉時產生。之后這個套接字上不會再有 `message` 事件發生。
### 事件: 'error'
- `exception` Error 對象
當發生錯誤時產生。
### socket.send(buf, offset, length, port, address, [callback])
- `buf` Buffer 對象,要發送的消息
- `offset` Integer,Buffer 中消息起始偏移值。
- `length` Integer,消息的字節數。
- `port` Integer,目標端口
- `address` String,目標 IP
- `callback` Function,可選,當消息被投遞后的回調。
對于 UDP 套接字,必須指定目標端口和 IP 地址。`address` 參數可以是一個字符串,它會被 DNS 解析。可選地可以指定一個回調以用于發現任何 DNS 錯誤或當 `buf` 可被重用。請注意 DNS 查詢會將發送的時間推遲到至少下一個事件循環。確認發送完畢的唯一已知方法是使用回調。
如果套接字之前并未被調用 `bind` 綁定,則它會被分配一個隨機端口并綁定到“所有網絡接口”地址(`udp4` 套接字是 0.0.0.0;`udp6` 套接字是 ::0)。
向 `localhost` 隨機端口發送 UDP 報文的例子:
~~~
var dgram = require('dgram');
var message = new Buffer("Some bytes");
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 41234, "localhost", function(err) {
client.close();
});
~~~
**關于 UDP 數據報大小的注意事項**
一個 `IPv4/v6` 數據報的最大大小取決與 `MTU`(*最大傳輸單位*)和 `Payload Length` 字段大小。
- `Payload Length` 字段寬 `16 bits`,意味著正常負載包括網絡頭和數據不能大于 64K(65,507 字節 = 65,535 ? 8 字節 UDP 頭 ? 20 字節 IP 頭);這對環回接口通常是真的,但如此大的數據報對大多數主機和網絡來說是不切實際的。
- `MTU` 是一個給定的數據鏈路層技術能為數據報提供支持的最大大小。對于任何連接,`IPv4` 允許最小 `68` 字節的 `MTU`,而 IPv4 所推薦的 `MTU` 為 `576`(通常作為撥號類應用的推薦 `MTU`),無論它們是完整接收還是分片。
對于 `IPv6`,最小的 `MTU` 為 `1280` 字節,但所允許的最小碎片重組緩沖大小為 `1500` 字節。 `68` 的值是非常小的,因為現在大多數數據鏈路層技術有都具有 `1500` 的最小 `MTU`(比如以太網)。
請注意我們不可能提前得知一個報文可能經過的每一個連接 MTU,因此通常情況下不能發送一個大于(接收者的)`MTU` 的數據報(報文會被悄悄地丟掉,而不會將數據沒有到達它意圖的接收者的消息告知來源)。
### socket.bind(port, [address], [callback])
- `port` Integer
- `address` String,可選
- `callback` 沒有參數的 Function,可選,當綁定完成時被調用。
對于 UDP 套接字,在一個具名端口 `port` 和可選的地址 `address` 上監聽數據報。如果 `address` 未指定,則操作系統會嘗試監聽所有地址。當綁定完成后,一個 "listening" 事件會發生,并且回調 `callback`(如果指定)會被調用。同時指定 "listening" 事件監聽器和 `callback` 并不會產生副作用,但也沒什么用。
一個綁定了的數據報套接字會保持 node 進程運行來接收數據報。
如果綁定失敗,則一個 "error" 事件會被產生。在極少情況下(比如綁定一個已關閉的套接字),該方法會拋出一個 `Error`。
一個監聽端口 41234 的 UDP 服務器的例子:
~~~
server.bind(41234);
// 服務器正在監聽 0.0.0.0:41234
~~~
### socket.close()
關閉底層套接字并停止監聽數據。
### socket.address()
返回一個包含了套接字地址信息的對象。對于 UDP 套接字,該對象會包含地址 `address`、地址族 `family` 和端口號 `port`。
### socket.setBroadcast(flag)
- `flag` Boolean
設置或清除 `SO_BROADCAST` 套接字選項。當該選項被設置,則 UDP 報文可能被發送到一個本地接口的廣播地址。
### socket.setTTL(ttl)
- `ttl` Integer
設置 `IP_TTL` 套接字選項。TTL 表示“Time to Live”(生存時間),但在此上下文中它指的是報文允許通過的 IP 躍點數。各個轉發報文的路由器或網關都會遞減 TTL。如果 TTL 被一個路由器遞減到 0,則它將不會被轉發。改變 TTL 值通常被用于網絡探測器或多播。
`setTTL()` 的參數為介于 1 至 255 的躍點數。在大多數系統上缺省值為 64。
### socket.setMulticastTTL(ttl)
- `ttl` Integer
設置 `IP_MULTICAST_TTL` 套接字選項。TTL 表示“Time to Live”(生存時間),但在此上下文中它指的是報文允許通過的 IP 躍點數,特別是組播流量。各個轉發報文的路由器或網關都會遞減 TTL。如果 TTL 被一個路由器遞減到 0,則它將不會被轉發。
`setMulticastTTL()` 的參數為介于 1 至 255 的躍點數。在大多數系統上缺省值為 1。
### socket.setMulticastLoopback(flag)
- `flag` Boolean
設置或清除 `IP_MULTICAST_LOOP` 套接字選項。當該選項被設置時,組播報文也會被本地接口收到。
### socket.addMembership(multicastAddress, [multicastInterface])
- `multicastAddress` String
- `multicastInterface` String,可選
以 `IP_ADD_MEMBERSHIP` 套接字選項告訴內核加入一個組播分組。
如果未指定 `multicastInterface`,則操作系統會嘗試向所有有效接口添加關系。
### socket.dropMembership(multicastAddress, [multicastInterface])
- `multicastAddress` String
- `multicastInterface` String,可選