码迷,mamicode.com
首页 > Web开发 > 详细

[Nodejs]初探nodejs学习笔记- 如何使用nodejs搭建简单的UDP聊天功能

时间:2015-10-17 22:19:20      阅读:345      评论:0      收藏:0      [点我收藏+]

标签:

何为UDP(User Datagram Protocol)?

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

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

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

????????? ????? 防工具盗链抓取【如果显示此文字,代表来自第三方转发】 freddon所有  ??? ???????????

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

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

       Fred、Lenka只能互相发消息。

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

????????? ????? 防工具盗链抓取【如果显示此文字,代表来自第三方转发】 freddon所有  ??? ???????????

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 < userPool.pool.length; 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 < msgTask.tasks.length; 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):

技术分享

技术分享

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

技术分享

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

技术分享

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

server:

技术分享

nick:

技术分享

fred:

技术分享

lenka:

技术分享

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

Lenka:

技术分享

fred:

技术分享

nick:

技术分享



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

????????? ????? 防工具盗链抓取【如果显示此文字,代表来自第三方转发】 freddon所有  ??? ???????????


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





[Nodejs]初探nodejs学习笔记- 如何使用nodejs搭建简单的UDP聊天功能

标签:

原文地址:http://my.oschina.net/freddon/blog/518328

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!