ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 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,可选