node.js 实现udp传输_[Nodejs]初探nodejs学习笔记- 如何使用nodejs搭建简单的UDP聊天功能...

本文介绍了如何使用Node.js实现UDP(User Datagram Protocol)传输,创建一个简单的聊天功能。通过Node.js的dgram模块,创建UDP服务器和客户端,实现用户间的消息传递。尽管UDP不保证消息的可靠性和顺序,但其低延迟和高效使其适用于此类应用。示例代码展示了Fred、Lenka和Nick三个用户之间的聊天交互,包括在线状态管理和离线消息缓存。
摘要由CSDN通过智能技术生成

何为UDP(User Datagram Protocol)?

从baidu摘过来一段:UDP,用户数据报协议,与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。

UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。

好了,接下来简要说明一下我实现的效果:

有Fred、Lenka、Nick三个人,均处于同一聊天室中:

即:Nick发的消息,Fred、Lenka均能收到;

Fred、Lenka只能互相发消息。

OK,就这么简单。为了说明问题,就不过度封装代码,以说明为主。

Server端:

server.js

var udp = require('dgram');

var server = udp.createSocket('udp4');

/**

* 用于存储人员之间的离线消息任务

* @type {{tasks: Array}}

*/

var msgTask = {

//config: {},

tasks: []

};

/**

* 存醋当前在线的用户

* @type {{online: Array, pool: Array}}

*/

var userPool = {

online: [],

pool: []

};

/**

* 加入某个用户的在线状态

* @param name

* @param rinfo

*/

var pushIntoPool = function (name, rinfo) {

var index = userPool.online.indexOf(name);

if (index >= 0) {

userPool.online.splice(index, 1);

userPool.pool.splice(index, 1);

}

userPool.online.push(name);

userPool.pool.push({name: name, ip: rinfo.address, port: rinfo.port});

};

/**

* 移除在线状态

* @param name

*/

var pullFromPool = function (name) {

var index = userPool.online.indexOf(name);

if (index >= 0) {

userPool.online.splice(index, 1);

userPool.pool.splice(index, 1);

return;

}

};

/**

* 加入离线消息任务

* @param msg

*/

var addInTask = function (msg) {

msgTask.tasks.push({msg: msg, expireTime: 7 * 24 * 3600 + new Date().getTime()});

};

/**

* 发送消息

* @param m

* @param rinfo

*/

var sendMsg = function (m, rinfo) {

process.nextTick(function () {

if (m.to){

//获取对方的服务地址\端口

var index = userPool.online.indexOf(m.to.name);

if (index >= 0) {

//在线

var config = userPool.pool[index];

var msg = JSON.stringify(m);

server.send(msg, 0, Buffer.byteLength(msg, encoding = 'utf8'), config.port, config.ip, function (err, bytes) {

if (err) {

//发送失败

//缓存数据

addInTask(m);

}

});

} else {

if (rinfo) {

//离线

var content = JSON.stringify({content: m.to.name + '不在线'});

server.send(content, 0, Buffer.byteLength(content, encoding = 'utf8'), rinfo.port, rinfo.address, function (err, bytes) {

if (err) {

//发送失败

}

});

}

//不在线

pullFromPool(m.to.name);

//缓存数据

addInTask(m);

}

} else {

//群聊

for (var i = 0; i 

var to_cfg = userPool.pool[i];

if (to_cfg.name == m.from.name) {

continue;

} else {

var msg = JSON.stringify(m);

server.send(msg, 0, Buffer.byteLength(msg, encoding = 'utf8'), to_cfg.port, to_cfg.ip, function (err, bytes) {

if (err) {

//发送失败

}

});

}

}

}

});

};

/**

* 后台轮询任务

*/

var backgroundTask = function () {

for (var i = 0; i 

var m = msgTask.tasks.splice(i, 1)[0];

sendMsg(m.msg);

}

beginTask();

};

var tid;

var beginTask = function () {

clearTimeout(tid);

tid = setTimeout(backgroundTask, 1000);

};

server.on('message', function (msg, rinfo) {

//注意msg为Buffer对象

var m = JSON.parse(msg.toString());

pushIntoPool(m.from.name, rinfo);

if (m.action == 'online') {

console.log('当前聊天室在线人数%d::%s', userPool.online.length,userPool.online.join(","));

return;

}

//发送消息

sendMsg(m, rinfo);

}).bind(8124, function () {

console.log('服务端启动成功');

//当服务启动后,开启后台消息轮询服务

beginTask();

});

Client端:

Fred.js 用户fred

var udp=require('dgram');

var mm=require('./msgmodel');

var client=udp.createSocket('udp4');

var from={

name:'Fred',

host:client.address,

port:client.remotePort,

content:''

};

var msg=new mm.FMsg(from);

process.stdin.resume();

process.stdin.on('data',function(data){

msg.setAction('chat');

msg.setContent(data.toString('utf8'));

//设置只能发送给Lenka

msg.setTo({

name:'Lenka'

});

msg.udpSendMsg(client,function(err,bytes){

if(err){

//发送失败

}

});

});

client.on('message',function(data){

var data=JSON.parse(data.toString());

if(!data.from){

console.log(data.content);

}else{

if(!data.to){

console.log("[%s]:%s",data.from.name,data.content);

}else{

console.log("[%s@%s]:%s",data.from.name,data.to.name,data.content);

}

}

});

//默认连接后上线操作

msg.udpSendMsg(client,function(err,bytes){

if(err==0){

console.log("Fred上线!");

}

});

Lenka.js 用户Lenka

var udp=require('dgram');

var mm=require('./msgmodel');

var client=udp.createSocket('udp4');

var from={

name:'Lenka',

host:client.address,

port:client.remotePort,

content:''

};

var msg=new mm.FMsg(from);

process.stdin.resume();

process.stdin.on('data',function(data){

msg.setAction('chat');

msg.setContent(data.toString('utf8'));

//设置只能发送给Fred

msg.setTo({

name:'Fred'

});

msg.udpSendMsg(client,function(err,bytes){

if(err){

//发送失败

}

});

});

client.on('message',function(data){

var data=JSON.parse(data.toString());

if(!data.from){

console.log(data.content);

}else{

if(!data.to){

console.log("[%s]:%s",data.from.name,data.content);

}else{

console.log("[%s@%s]:%s",data.from.name,data.to.name,data.content);

}

}

});

//默认连接后上线操作

msg.udpSendMsg(client,function(err,bytes){

if(err==0){

console.log("Lenka上线!");

}

});

Nick.js 用户Nick

var udp=require('dgram');

var mm=require('./msgmodel');

var client=udp.createSocket('udp4');

var from={

name:'Nick',

host:client.address,

port:client.remotePort,

content:''

};

var msg=new mm.FMsg(from);

process.stdin.resume();

process.stdin.on('data',function(data){

msg.setAction('chat');

msg.setContent(data.toString('utf8'));

//不设置发送给谁,默认发送给所有人

msg.udpSendMsg(client,function(err,bytes){

if(err){

//发送失败

}

});

});

client.on('message',function(datas){

var data=JSON.parse(data.toString());

if(!data.from){

console.log(data.content);

}else{

if(!data.to){

console.log("[%s]:%s",data.from.name,data.content);

}else{

console.log("[%s@%s]:%s",data.from.name,data.to.name,data.content);

}

}

});

//默认连接后上线操作

msg.udpSendMsg(client,function(err,bytes){

if(err==0){

console.log("Nick上线!");

}

});

消息类msgmodel.js

var host = '127.0.0.1';//需要连接到服务器提供udp连接的ip

var port = 8124;//需要连接到服务器提供udp连接的端口

var ACTIONS=['online','chat','request','stranger','del','offline'];

function FMsg(from, to, content) {

this.from = from;

this.to = to;

this.content = content;

this.action = 'online';

this.setAction = function (action) {

this.action = action;

};

this.setTo = function (to) {

this.to = to;

};

this.setContent = function (content) {

this.content = content;

};

this.getMsg = function () {

var msg = {

from: this.from,

to: this.to,

content: this.content,

action:this.action

};

return JSON.stringify(msg);

};

this.udpSendMsg = function (client, callback) {

var data = this.getMsg();

client.send(data, 0, Buffer.byteLength(data,encoding='utf8'), port, host, callback);

};

}

exports.FMsg = FMsg;

接下来分别使用nodejs启动服务端和客户端。

启动服务端后,只启动Fred、Lenka中的一个(比如说启动了Fred):

3bdb271b42e0a9b2e82a4cea55039cc1.png

b9b7d08ba097608ce3c9b3d779813ef2.png

在Fred控制台进行如下输入:(Lenka不在线,所以该消息未发送成功)

41ba12a69b2bae818ae193d48e5199e1.png

然后启动Lenka,(Lenka收到了离线消息)Lenka的控制台为:

f1cb8216b3fc20faa35746d54b005495.png

启动Nick,Nick说话,然后观察其他两个客户端,

server:

31ba0e6a6f759aeb2df11a34291d72ca.png

nick:

55be6dff93340b99834c8b1e5a606d81.png

fred:

b0cf87ac14e4f829dfc1f736648eda06.png

lenka:

0496d558d5603047ba2425c62a97897e.png

Lenka、Fred说话,观察Nick控制台:

Lenka:

11b717cde91ee3f86c1b965e97c91250.png

fred:

58b8ef32ef88385bea1b70b687d4acfe.png

nick:

adf54cad557036615bc90b585ef4048c.png

好啦,相当简单的一个点对点和聊天室的功能就这样搭好了。

转载请注明:http://my.oschina.net/freddon/blog/518328

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值