使用dgram模块实现基于UDP的数据通信

TCP是一种基于连接的协议,在进行通信前,首先要求客户端与服务器端建立一条用于通信的连接。而UDP是一种面向非连接的协议,在进行通信前,不要求首先建立客户端与服务器端之间的连接,可以直接把数据包发送给对方。基于这个原因,UPD也是一种不可靠的协议,但是其传输速度比TCP更快,因此更适用于实时通信的场合。

在Node.js中,提供了dgram模块,用于创建UPD服务器与客户端,以及实现UDP服务器与客户端之间的通信。

创建UDP服务器与客户端

在dgram模块中,可以使用createSocket方法创建一个用于实现UDP通信的socket端口对象

var socket=dgram.createSocket(type,[callback])
  • type:用于指定进行UDP通信时使用的协议类型,可指定值为“upd4”或“upd6”。
  • callback:用于指定当从该端口接收到数据时调用的回调函数,在该回调函数中,可以使用两个参数。msg参数值为一个Buffer对象,其中存放了接收到的数据。rinfo参数值也为一个对象,该对象所具有的属性及属性值如下:

    • address:属性值为发送者所使用的地址,例如127.0.0.1。
    • family:属性值为一个标识了发送者所使用的地址是IPv4地址还是IPv6地址的字符串,例如“IPv4”。
    • port:属性值为发送者所使用的socket端口号,例如49436。
    • size:属性值为发送者所发送的数据的字节数。

    监听收到消息

当从该socket端口中接收到数据时,触发该socket端口对象的message事件,可以不在createSocket方法中使用callback参数,而是通过对socket端口对象的message事件进行监听,并且指定事件回调函数的方法来指定当从该socket端口接收到数据时所需执行的处理

socket.on('message',function (msg,rinfo){
    //回调函数代码略
});

bind方法来指定该socket端口对象所监听的地址与端口号。

在创建了socket端口对象之后,可以使用该socket端口对象的bind方法来指定该socket端口对象所监听的地址与端口号。当创建UDP服务器时,必须使用该方法,这样UDP客户端才能知道向哪个地址发送数据。

socket.bind(port,[address],[callback])
  • port:参数值为一个整数,用于指定该socket端口对象所监听的端口号,例如41234。
  • address:参数值为一个字符串,用于指定该socket端口对象所监听的地址,该地址可以为一个IP地址,也可以为一个主机名。如果不指定address参数,该socket端口对象将监听来自于所有地址的数据。
  • callback:指定开始监听时所要调用的回调函数,该回调函数中不使用任何参数。

发送数据

当创建了一个socket端口对象之后,可以利用该socket端口对象的send方法向外发送数据。

socket.send(buf,offset,length,port,address,[callback])
  • buf:一个代表了缓存区的Buffer对象,该缓存区中存放了需要发送的数据。
  • offset:值为一个整数,用于指定从缓存区中的第几个字节处开始取出要发送的数据。
  • length:用于指定需要发送数据的字节数。
  • port:用于指定接收数据的socket端口对象所使用的端口号。
  • address:用于指定接收数据的socket端口对象所属地址。
  • callback:用于指定当数据发送完毕时所需调用的回调函数,在该回调函数中,可以使用两个参数,其中err参数值为发送数据失败时触发的错误对象,在发送数据时产生的失败通常是由DNS解析错误引起的。例如,在send方法中指定一个不存在的主机名通常会导致DNS解析错误,从而导致发送数据失败。bytes参数值为发送数据的字节数

获取该socket端口对象相关的地址信息。

var address=socket.address()

该方法返回一个对象,其中具有如下所示的属性。

  • port:属性值为该socket端口对象的端口号,例如41234。
  • address:属性值为该socket端口对象所属地址,例如127.0.0.1。
  • family:属性值为一个标识了该socket端口对象所属地址是IPv4地址还是IPv6地址的字符串,例如“IPv4”。

创建简单UDP服务器

var dgram = require("dgram");
var server = dgram.createSocket("udp4");
server.on("message", function (msg, rinfo) {
  console.log('已接收客户端发送的数据:'+msg);
  console.log("客户端地址信息为 % j",rinfo);
  var buf = new Buffer("确认信息:"+msg);
  server.send(buf, 0, buf.length, rinfo.port, rinfo.address);
});
server.on("listening", function () {
  var address = server.address();
  console.log("服务器开始监听。地址信息为 % j",address);
});
server.bind(41234, 'localhost');

创建简单UDP客户端

var dgram = require('dgram');
var message = new Buffer("你1好。");
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 41234,
  "localhost", function (err, bytes) {
    if (err) console.log('发送数据失败。');
    else console.log("已发送 % d字节数据。",bytes);
});
client.on("message", function (msg, rinfo) {
    console.log("已接收服务器端发送的数据:% s",msg);
    console.log("服务器地址为 % s。",rinfo.address);
    console.log("服务器所用端口为 % s。",rinfo.port);
});

关闭服务

socket.close()

setTTL方法

在Node.js中,可以使用socket端口对象的setTTL方法来设置从该端口发送出去的数据包在被路由器废弃之前,该数据包可以经过的路由器的最大数目。

socket.setTTL(ttl)

实现广播与组播

在dgram模块中,可以很轻松地实现数据的广播与组播。

广播

setBroadcast()

在dgram模块中,可以使用socket端口对象的setBroadcast方法来进行数据的广播

socket.setBroadcast(flag)
  • flag:个布尔类型的参数。当参数值为true时,UD服务器或客户端可以利用其所用的socket端口对象的send方法来进行数据的广播。在广播数据的时候,需要将socket端口对象的send方法中的地址修改为广播地址,例如“192.168.1.255”。

使用setBroadcast方法广播消息

var dgram = require("dgram");
var server = dgram.createSocket("udp4");
server.on("message", function (msg) {
    var buf=new Buffer("已接收客户端发送的数据:"+msg);
    server.setBroadcast(true);
    //在本例中,客户端需将端口号指定为41235
    server.send(buf, 0, buf.length,41235, "192.168.1.255");
});
server.bind(41234,'192.168.1.100');

客户端

var dgram = require('dgram');
var client = dgram.createSocket("udp4");
client.bind(41235,'192.168.1.102');
var buf = new Buffer("你好。");
client.send(buf,0,buf.length,41234,'192.168.1.100');
client.on("message", function (msg,rinfo) {
console.log("已接收服务器端发送的数据:%s",msg);
});

组播

所谓数据的组播,是指将网络中同一业务类型主机进行逻辑上的分组,从某个socket端口上发送的数据只被该组中的其他主机所接收,不被组外的任何主机接收。因此,在实现组播时,我们并不直接将数据发送给目标地址,而是将数据发送到组播地址,操作系统将把该数据组播给组内其他所有成员

在网络中,使用D类地址作为组播地址。所谓D类地址,是指224.0.0.0~239.255.255.255的IP地址,这些地址被划分为以下三类:

  • 局部组播地址:224.0.0.0~224.0.0.255,这是为路由协议和其他用途保留的地址。
  • 预留组播地址:224.0.1.0~238.255.255.255,可用于全球范围(如Internet)或网络协议。
  • 管理权限组播地址:239.0.0.0~239.255.255.255,可供组织内部使用,类似于私有IP地址,不能用于Internet,可限制组播范围。

在Node.js中,要实现数据的组播,首先要使用socket端口对象的addMembership方法将该socket端口对象加入到组播组中。

socket.addMembership(multicastAddress, [multicastInterface])
  • multicastAddress:参数值为一个字符串,用于指定socket端口需要加入的组播组地址;
  • multicastInterface:参数值为一个字符串,用于指定socket端口需要加入的网络接口IP地址,如果省略该参数,socket端口将被加入到所有有效的网络接口中。

在socket端口被加入到组播组中后,可以使用该socket端口对象的dropMembership方法退出组播组。

socket.dropMembership(multicastAddress, [multicastInterface])

参数与创建组播一致

setMulticastTTL方法

setMulticastTTL方法来设置在进行数据组播时从该端口发送出去的数据包在被路由器废弃之前,该数据包可以经过的路由器的最大数目。

socket.setMulticastTTL(ttl)

setMulticastLoopback方法

setMulticastLoopback方法来设置是否允许组播数据回送,如果允许的话,被组播的数据将被主机的本地接口所接收。

socket.setMulticastLoopback(flag)

用于发送组播数据的UDP服务器

var dgram = require('dgram');
var server = dgram.createSocket('udp4');
server.on('listening', function () {
    server.setMulticastTTL(128);
    server.addMembership('230.185.192.108');
});
setInterval(broadCast,1000);
function broadCast(){
    var buf=new Buffer((new Date()).toLocaleString());
    server.send(buf,0,buf.length, 8088, "230.185.192.108");
}

用于接收组播数据的UDP客户端

var PORT = 8088;
var HOST = '192.168.1.102';
var dgram = require('dgram');
var client = dgram.createSocket('udp4');
client.on('listening', function () {
    client.addMembership('230.185.192.108');
});
client.on('message', function (message, remote) {
    console.log(message.toString());
});
client.bind(PORT, HOST)