乐趣区

关于node.js:Node-js-开发入门-UDP-编程小白也能轻松学会

UDP 协定
UDP 协定(无连贯传输协定)是运行在运输层之上,可能为调用它的应用程序提供一种无需建设连贯就能够间接发送数据包的网络传输协定;它次要有以下两个特点:

无连贯:

不同于 TCP 在数据传输之前须要通过三次握手来建设连贯,UDP 无需做任何筹备即可进行数据传输操作,因而 UDP 的数据传输不存在因连贯而导致的时延;
TCP 须要在客户端与服务端之间保护连贯状态,以便实现 TCP 的牢靠数据传输服务,但因为 UDP 是无连贯的,因而 UDP 无需保护客户端与服务端之间的连贯状态。
应用层领有相对控制权:

UDP 会把应用层的数据包间接交给网络层,同样的在接管到网络层的数据包后间接交给应用层,UDP 不会对数据包做任何的操作;
TCP 个别会通过拥塞管制来避免过多数据进入网络,从而导致网络阻塞,但 UDP 并不理睬这些问题,因而可能最大限度的独霸网络带宽。
出于上述两个起因,应用 UDP 的利用领有对数据包、带宽的相对控制权。不过须要留神的是,即便 UDP 也应用了测验机制,也不意味着它对数据有效性进行任何保障,UDP 的测验仅用来查看数据包是否呈现了过错,即便呈现了过错,它还是会将数据包交给应用层,除了给予应用层一些敌对的正告外,它不会采取或提供任何补救措施。

绝对于 TCP,UDP 是一个绝对比较简单的运输层网络传输协定,也因为它的简便性,它领有 TCP 无法比拟的效率劣势,所以对于 DNS、语音视频流等对效率要求较高、对准确性要求较低的利用个别多应用 UDP 协定。

UDP 广(多)播
咱们常见的 UDP 服务是一对一的单播服务,接下来咱们将探讨一对多的 UDP 服务:

播送:播送与单播的次要区别是指标 IP 地址的不同,单播的指标 IP 地址是具体的主机地址,而播送的指标 IP 地址是所属局域子网中的播送地址,即位于该局域子网下的所有主机均能收到一份数据正本;
多播:也称为组播,是将网络中属于同一业务类型的主机进行逻辑上的分组,信息收发仅产生在同一分组中,不在该分组的的主机无奈收发对应的数据。
因为播送简直会占用所属局域子网的所有带宽,且只能在局域网中应用,因而基于播送的利用绝对于多播来说,数量非常少,而多播的一些长处使得它十分实用于生产者 / 消费者模式下的网络应用:

同一分组下的主机共享同一通道,这大大节俭了服务器带宽;
因为多播协定由数据消费者来确定是否进行数据的转发,所以对于生产者的服务端来说,其所需的带宽是固定的,与作为消费者的客户端的数量无关;
多播不仅能够在局域网中应用,也可在广域网中应用。
最初须要留神的是,无论是播送还是多播,它们仅仅进行数据的转发,而不关怀且无奈保障接收端可能正确地接管到数据,其个性完全符合 UDP 协定,因而播送、多播罕用于 UDP 协定。

利用
先装置 dgram 模块

服务端项代码如下:

/**
 * UDP 服务端
 */
// 载入 udp 模块
const dgram = require("dgram");
// 创立服务器
const server = dgram.createSocket("udp4");
 
server.on("message",(msg,rinfo)=>{
    // 将接管到的音讯返回客户端
    var strmsg = "你好,UDP 客户端,音讯曾经收到!";
    server.send(strmsg,rinfo.port,rinfo.address);
    console.log("服务器接管到来自"+rinfo.address+":"+rinfo.port+"的音讯:"+msg.toString());
});
 
server.on("listening",()=>{let adress = server.address();
    console.log("服务器监听:",adress.adress+":"+adress.port);
});
 
server.on("error",(err)=>{console.err("服务器异样谬误:"+err.message);
});
 
server.bind(8234,"127.0.0.1");

客户端项代码如下:

/**
 * UDP 客户端
 */
const dgram = require("dgram");
 
const client = dgram.createSocket("udp4");
 
client.on("message",(msg,rinfo)=>{console.log("接管来自:"+rinfo.address+":"+rinfo.port+"的音讯:"+msg.toString());
});
 
client.on("error",(err)=>{console.error("客户端谬误:"+err.message);
});
 
client.on("close",()=>{console.log("socket 已敞开");
});
 
client.send("我是 UDP 客户端!",8234,"127.0.0.1",(err)=>{if(err) client.close();});

运行后果如下:

【服务端】

【客户端】

上例实现了一个简略的 UDP 服务,次要波及了 dgram.createSocket、dgram.Socket 的应用,上面咱们对其进行一一介绍。

dgram.createSocket
dgram.createSocket 次要用于创立 dgram.Socket 实例;有以下两种签名:

dgram.createSocket(type[, callback]):

type:套接字类型,可用值为 udp4、udp6;其中 udp4 指运行在 IPv4 下,udp6 指运行在 IPv6 下;
callback:该回调会追加到 dgram.Socket 的 message 事件监听队列中,当接管到另一端发送的数据时触发。
dgram.createSocket(options[, callback]):

options:属性设置,相干属性如下:

type:Socket 类型,可用值为 udp4、udp6;其中 udp4 指运行在 IPv4 下,udp6 指运行在 IPv6 下;

reuseAddr:如果一个 socket 绑定了 0.0.0.0:41234,另一个 socket 绑定了 127.0.0.1:41234:

如果 reuseAddr 为 false,将抛出 EADDRINUSE 异样,这是因为 0.0.0.0 代表任何一个 IP 地址,其余的 IP 地址(比方上文中的 127.0.0.1)均会被零碎认为已占用;
如果 reuseAddr 为 true,此时 0.0.0.0:41234 和 127.0.0.1:41234 代表的是齐全不同的地址端口对,所以这两个 socket 均能绑定胜利。
该属性的默认值为 false。

ipv6Only:是否禁用双协定栈;默认值为 false;

recvBufferSize:设置套接字 SO_RCVBUF 的值,即设置接收缓冲区大小;

sendBufferSize:设置套接字 SO_SNDBUF 的值,即设置发送缓冲区大小;

lookup:自定义 DNS 查问逻辑,默认调用 dns.lookup();

signal:应用指定的 AbortSignal 来敞开套接字。

callback:该回调会追加到 dgram.Socket 的 message 事件监听队列中,当接管到另一端发送的数据时触发。

dgram.Socket
dgram.Socket 次要作为服务端与客户端之间通信的桥梁。

罕用办法
bind:绑定 IP 地址和端口号,这样通信另一端可通过指定的 IP 地址和端口号向该 socket 发送数据报信息,该办法有以下两种签名:

socket.bind(port[, callback]):

port:要绑定的端口号,如果该参数的值为 0 或未指定,零碎将随机调配段口号;
address:要绑定的 IP 地址,默认值为 0.0.0.0;
callback:绑定实现后的回调函数。
socket.bind(options[, callback]):

options:属性设置,相干属性如下:

port:要绑定的端口号,如果该参数的值为 0 或未指定,零碎将随机调配段口号;
address:要绑定的 IP 地址,默认值为 0.0.0.0;
exclusive:在 cluster 中是否容许共享服务监听句柄;默认值为 false;
fd:已存在 socket 相干文件描述符,如指定则应用该 socket,否则将创立一个新的 socket。
callback:绑定实现后的回调函数。

close:敞开套接字并进行监听来自通信另一端的数据报,该办法会触发 close 事件;

connect:与通信另一端的地址与端口号建设关联,相干参数如下:

port:通信另一端端口号;
address:通信另一端地址,默认值 udp4 下为 127.0.0.1,udp6 下为 ::1;
callback:连贯胜利后触发 connect 事件以及指定的 callback,连贯失败仅触发指定的 callback。
前文咱们说 UDP 是无连贯的协定,因而这里的 connect 并不是在通信单方之间建设真正的连贯,而只是用来设置通信另一端的地址和端口号;连贯建设后,socket.send() 调用无需指定 port 和 address 参数,并且仅能收到连贯指定的通信另一端的数据报。

disconnect:与通信另一端的地址与端口号勾销关联;

send:发送数据报给指定的通信另一端,相干参数如下:

msg:要发送的数据报;
offset:数据报第一个字节在缓冲区的偏移量;
length:数据报的字节大小;
port:通信另一端端口号,如果以后 socket 未连贯,则须要指定该参数,否则将应用连贯时指定的端口号,而无需指定该参数;
address:通信另一端地址,如果以后 socket 未连贯,则须要指定该参数,否则将应用连贯时指定的地址,而无需指定该参数;
callback:数据报发送胜利后的回调函数。
setBroadcast:设置套接字选项 SO_BROADCAST 的值,用来管制是否容许发送播送数据,其参数 flag 为 boolean 类型;

setMulticastInterface:设置多播接口,其参数 multicastInterface 为 string 类型;其值在 IPv4 和 IPv6 的要求如下:

在 IPv4 下,值为具体的 IP 地址,比方上面的例子:

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

socket.bind(1234, () => {socket.setMulticastInterface('10.0.0.2');
});
复制代码

在 IPv6 下,值应该蕴含一个作用域,比方上面的例子:

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

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

addMembership:在指定的接口上将指定的地址退出到一个不限源的多播组中(外部应用了套接字选项 IP_ADD_MEMBERSHIP),相干参数如下:

multicastAddress:多播地址;
multicastInterface:多播接口,如未指定,操作系统将自行抉择一个接口。
dropMembership:在指定的接口上将指定的地址从不限源的多播组中移除(外部应用了套接字选项 IP_DROP_MEMBERSHIP),相干参数如下:

multicastAddress:多播地址;
multicastInterface:多播接口,如未指定,将会从首个匹配的多播组中将指定的地址移除。
addSourceSpecificMembership:在指定的接口上退出一个特定于源的多播组(外部应用了套接字选项 IP_ADD_SOURCE_MEMBERSHIP),相干参数如下:

sourceAddress:源地址;
groupAddress:多播组地址;
multicastInterface:多播接口,如未指定,操作系统将自行抉择一个接口。
dropSourceSpecificMembership:在指定的接口上移除特定于源的多播组(外部应用了套接字选项 IP_DROP_SOURCE_MEMBERSHIP),相干参数如下:

sourceAddress:源地址;
groupAddress:多播组地址;
multicastInterface:多播接口,如未指定,将会解除首个匹配的特定于源的多播组之间的成员关系。
setMulticastLoopback:设置套接字选项 IP_MULTICAST_LOOP 的值,用来控制数据是否能够回送到本地的回环接口(默认状况下,当本机发送多播数据到某个网络接口时,在 IP 层,数据会回送到本地的回环接口),其参数为 flag 为 boolean 类型。

相干事件
listening:当 socket 已筹备好,能够接收数据时触发;该事件可通过 socket.bind() 显示触发,也可通过 socket.send() 隐式触发;须要留神的是,套接字相干的系统资源在该事件触发之前将不可用;

connect:通过调用 socket.connect() 与近程端胜利建设起连贯后触发;

message:当 socket 接管到新的数据报时触发;回调函数的参数如下:

msg:数据报信息,类型为 Buffer;

rinfo:数据报发送端信息,相干属性如下:

address:数据报发送端的 IP 地址;
family:数据报发送端 IP 地址协定版本,值为 IPv4 或 IPv6;
port:数据报发送端的端口号;
size:数据报大小。
error:产生异样时触发;

close:通过调用 socket.close() 胜利敞开 socket 后触发;一旦触发了该事件,将不会再触发 message 事件。

总结
本文咱们首先对 UDP 协定进行介绍,它是一种无连贯的、非牢靠的运输层传输协定,该协定罕用于 DNS、NFS、多媒体流等畛域;在理解了相干协定的根本运行原理后,咱们接着介绍了 Node.js 中 dgram 模块的应用;心愿可能通过这种从原理到实际的形式让大家真正把握 Node.js 网络编程。

源码附件曾经打包好上传到百度云了,大家自行下载即可~

链接: https://pan.baidu.com/s/14G-b…

提取码: yu27
百度云链接不稳固,随时可能会生效,大家放松保留哈。

如果百度云链接生效了的话,请留言通知我,我看到后会及时更新~

GIT 我的项目举荐:蕴含多端免受权可商用

附件地址:https://gitee.com/ZhongBangKeJi

退出移动版