使用dgram模塊實(shí)現(xiàn)基于UDP的數(shù)據(jù)通信

我們知道,TCP是一種基于連接的協(xié)議,在進(jìn)行通信前,首先要求客戶端與服務(wù)器端建立一條用于通信的連接。而UDP是一種面向非連接的協(xié)議,在進(jìn)行通信前,不要求首先建立客戶端與服務(wù)器端的連接,可以直接把數(shù)據(jù)包發(fā)送給對(duì)方?;谶@個(gè)原因,UPD也是一種不可靠的協(xié)議,但是其傳輸速度比TCP更快,因此更適用于實(shí)時(shí)通信的場(chǎng)合。
在Node.js中,提供了dgram模塊,用于創(chuàng)建UPD服務(wù)器與客戶端,以及實(shí)現(xiàn)UDP服務(wù)器與客戶端之間的通信。


一、 創(chuàng)建UDP服務(wù)器與客戶端

在dgram模塊中,可以使用crateSocket方法創(chuàng)建一個(gè)用于實(shí)現(xiàn)UDP通信的socket端口對(duì)象。

const socket = dgram.createSocket(type, [callback]);

type:用于指定進(jìn)行UDP通信時(shí)使用的協(xié)議類型,可指定值為“upd4”或“upd6”。
callback:可選,用于指定當(dāng)從該端口接收到數(shù)據(jù)時(shí)調(diào)用的回調(diào)函數(shù)。該回調(diào)函數(shù)使用方法如下:

function (msg, rinfo) {
}

該回調(diào)函數(shù)中可以使用兩個(gè)參數(shù):
msg:參數(shù)值為一個(gè)Buffer對(duì)象,其中存放了接收到的數(shù)據(jù)。
rinfo:參數(shù)值也是一個(gè)對(duì)象,該對(duì)象具有如下屬性:

  • address:屬性值為發(fā)送者所使用的地址,如127.0.0.1。
  • family:屬性值為一個(gè)標(biāo)識(shí)了發(fā)送者所使用的地址是IPv4地址還是IPv6地址的字符串,例如“IPv4”。
  • port:屬性值為發(fā)送者所使用的socket端口號(hào),例如49436。
  • size:屬性值為發(fā)送者所發(fā)送的數(shù)據(jù)的字節(jié)數(shù)。

當(dāng)從socket端口中接收到數(shù)據(jù)時(shí),觸發(fā)該socket對(duì)象的message事件,可以不在createSocket方法中使用callback參數(shù)。

socket.on('message', function(msg, rinfo) {
})

在創(chuàng)建了socket端口對(duì)象后,可以使用該socket端口對(duì)象的bind方法來指定該socket端口對(duì)象所監(jiān)聽的地址與端口號(hào)。當(dāng)創(chuàng)建UDP服務(wù)器時(shí),必須使用該方法,這樣UDP客戶端才能知道向哪個(gè)地址發(fā)送數(shù)據(jù)。

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

port:參數(shù)值為一個(gè)整數(shù),用于指定該socket端口對(duì)象所監(jiān)聽的端口號(hào)。
address:可選,參數(shù)值為一個(gè)字符串,用于指定該socket端口對(duì)象所監(jiān)聽的地址,該地址可以為一個(gè)IP地址,也可以為一個(gè)主機(jī)名。如果不指定address參數(shù),該socket端口對(duì)象將監(jiān)聽來自于所有地址的數(shù)據(jù)。
callback:可選,當(dāng)指定了bind方法之后,該socket端口對(duì)象將立即監(jiān)聽來自于指定地址和端口號(hào)的數(shù)據(jù),可使用callback參數(shù)值指定開始監(jiān)聽時(shí)所要調(diào)用的回調(diào)函數(shù),也可以對(duì)socket端口對(duì)象的listening事件進(jìn)行監(jiān)聽,該回調(diào)函數(shù)不使用任何參數(shù)。

socket.on('listening', function() {
}

當(dāng)創(chuàng)建了一個(gè)socket端口對(duì)象之后可以利用該socket端口對(duì)象的send方法向外發(fā)送數(shù)據(jù)。

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

buf: 代表了一個(gè)緩存區(qū)的buffer對(duì)象,該緩存區(qū)中存放了需要發(fā)送的數(shù)據(jù)。
offset:參數(shù)值為一個(gè)整數(shù),用于指定從緩存區(qū)的第幾個(gè)字節(jié)處開始取出要發(fā)送的數(shù)據(jù)。
length:參數(shù)值為一個(gè)整數(shù),用于指定需要發(fā)送的字節(jié)數(shù)。
prot:參數(shù)為一個(gè)整數(shù),用于指定接受數(shù)據(jù)的socket端口對(duì)象所使用的端口號(hào)。
address:參數(shù)值為一個(gè)字符串,用于指定接收數(shù)據(jù)的socket端口對(duì)象所屬地址。
callback:可選,指定當(dāng)數(shù)據(jù)發(fā)送完畢時(shí)所需調(diào)用的回調(diào)函數(shù)。

function(err, bytes) {
}

err:發(fā)送數(shù)據(jù)失敗時(shí)觸發(fā)的錯(cuò)誤對(duì)象,在發(fā)送數(shù)據(jù)時(shí)產(chǎn)生的失敗通常是由于DNS解析錯(cuò)誤引起。
bytes:參數(shù)值為發(fā)送數(shù)據(jù)的字節(jié)數(shù)

如果在發(fā)送數(shù)據(jù)前還沒有使用該socket端口對(duì)象的bind方法來指定其端口號(hào)及地址,操作系統(tǒng)將為其分配一個(gè)隨機(jī)端口號(hào)并指定其可以接收來自于任何地址的數(shù)據(jù)。
可以使用socket端口對(duì)象的address方法獲取該socket端口對(duì)象相關(guān)的地址信息。

let address = socket.address()

該方法返回一個(gè)對(duì)象,其中具有如下屬性:

  • port:屬性值為該socket端口對(duì)象的端口號(hào)。
  • address:屬性值為該socket端口對(duì)象所屬地址。
  • family:屬性值為一個(gè)標(biāo)識(shí)了該socket端口對(duì)象所屬地址是IPv4地址還是IPv6地址的字符串。

創(chuàng)建簡單UDP服務(wù)器

const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on("message", function (msg, rinfo) {
    console.log('已接收到客戶端發(fā)送的數(shù)據(jù):' + msg);
    console.log("客戶端地址信息為:", rinfo);
    let buf = new Buffer('確認(rèn)信息為:' + msg);
    server.send(buf, 0, buf.length, rinfo.port, rinfo.address);
});

server.on("listening", function () {
    let address = server.address();
    console.log("服務(wù)器開始監(jiān)聽。地址信息為", address);
});
server.bind(41234, 'localhost');

創(chuàng)建簡單UDP客戶端

const dgram = require('dgram');
let message = new Buffer('你好。');
const client = dgram.createSocket('udp4');
client.send(message, 0, message.length, 41234, "localhost", function (err, bytes) {
    if (err) {
        console.log('發(fā)送數(shù)據(jù)失敗');
    } else {
        console.log("已發(fā)送字節(jié)數(shù)據(jù)。", bytes);
    }
});
client.on('message', function (msg, rinfo) {
    console.log('已接收到服務(wù)器端發(fā)送的數(shù)據(jù)', msg);
    console.log('服務(wù)器地址為', rinfo.address);
    console.log('服務(wù)器所用端口號(hào)為', rinfo.port);
});

未完待續(xù)。。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容