龙空技术网

js版udp通讯

头条路人饼 108

前言:

当前小伙伴们对“js通信”可能比较关注,小伙伴们都需要分析一些“js通信”的相关文章。那么小编在网上汇集了一些关于“js通信””的相关文章,希望大家能喜欢,咱们一起来了解一下吧!

js版udp通讯,

这两天想做一个打车程序。采用android +vue+koa+mongodb,

通讯方面用的udp协议。

众所周知udp的优点是简单,速度快。但不可靠。

曾经用过websocket,实际应用发现,也有不可靠的情况,既然都不可靠,而且传输的数据也不多,索性还是用udp协议。

这个udp程序只适应于少量数据。

具体代码如下:(欢迎大家指正问题)

Array.prototype.remove=function(o) {

for(var i=0;i<this.length;i++) if (this[i]==o) {

this.splice(i,1);

break;

}

return this;

}

var UDP=function(_opt) {

let ROOT=this;

this.error={

Not_found_module:10001,

Not_found_cmd:10002,

overTime:10010,

sendFaild:10011,

}

this.info={

droped:0,//丢了多少包

speedTx:0,//每秒发送多少数据

speedRx:0,//每秒接收多少数据

}

let opt=_opt||{};

let debug=opt.debug || {

enabled:true,//是否开启调试

show:true,//是否开始显示

cmds:[],//只显示某个命令的调试信息

excepts:[],//显示除了某个命令的调试信息

}

debug.cmd=function(cmd) {

if (this.cmd.length) this.show=this.cmd.indexOf(cmd)>=0;

if (this.except.length) deubg.show=this.except.indexOf(cmd)<0;

}

debug.show=function() {

if (!this.enabled||!this.show) return;

console.log(new Date(),...arguments);

}

let users={};

let REQ=1;

let modules={};

let user={};

let isServer=!opt.server;//如果定义了server,作为客户端,否则为服务端

let server=opt.server;

let serverport=opt.serverport;

let localport=opt.port;

let sendTime=new Date().getTime();//作为客户端,定时发送心跳包

//如果用nodejs使用下面代码===================

let dgram=require('dgram');

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

socket.bind(localport,()=>{

debug.show("bind ", socket.address());

});

socket.on('listening',()=>{

debug.show("udp listening in ",opt.port);

if (!isServer) {//如果是客户端,则连接到服务器

debug.show("connect to server",server,serverport);

this.connect(server,serverport,opt.connected);//连接服务器

}

});

socket.on('error',(err)=>{

debug.show('udp error',err);

});

socket.on('close',()=>{

debug.show('udp closed.');

});

socket.on('message',async (data,rinfo)=>{

try{

let msg=JSON.parse(data.toString());

debug.show("收到数据",rinfo,msg);

compleData(msg,rinfo);

}catch(e){debug.show("收到非法数据",data.toString())};

});

function socketSend(data,port,adr,cb) {

socket.send(data,port,adr,cb);

}

//========================================================

//如果用android需要重写上面代码

//调用 android的代码

//=======================================================

async function compleData(msg,rinfo) {

let userid=msg.userid;

if (isServer) {//如果是服务器,处理下面信息

if ((!userid || !users[userid]) && msg.syscmd!='login') {//如果没有这个用户,而且不是登陆信息

socketSend(JSON.stringify({syscmd:'relogin'}),rinfo.port,rinfo.address);

return;

}

if (msg.syscmd=='login') {//如果是登陆信息

if (!userid || !users[userid]){//如果之前没有这个用户,则创建一个userid

userid=Date.now().toString(16).substr(4)+Math.random().toString(36).substr(3);

}

user=users[userid]={

id:userid,

rinfo:rinfo,

loginTime:new Date(),

sends:[],

}

send(user,{req:msg.req,syscmd:'logined',body:{result:0,content:{userid:userid}}});

if (typeof(opt.connected)=='function') opt.connected(user);//通知用户登陆回调函数,

return;

} else{

user=users[userid];

user.rinfo=rinfo;//防止用户网格变化

}

user.updateTime=new Date().getTime();

if (msg.syscmd=='logout') {//如果用户要退出

send(user,{req:msg.req,syscmd:'logout'});

if (typeof(opt.disconnected)=='function') opt.disconnected(user);//通知用户退出回调函数,

delete users[userid];//删除这个用户

return;

}

if (msg.syscmd=='heart') return;

} else {//如果我是客户端,处理下面信息

if (msg.syscmd=='logined') {//收到服务器的登陆成功信息

user.logined=true;

user.id=msg.body.content.userid;

user.loginTime=new Date();

user.sends.some((send)=>{

if (send.req==msg.req) {

user.sends.remove(send);

send.callback(msg.body);

return true;

}

});

return;

}

if (msg.syscmd=='relogin') {//服务器要求重新登陆

user.logined=false;

ROOT.connect();

return;

}

user.rinfo=rinfo;

}

total.recv(JSON.stringify(msg).length);//统计接收数据量

//其他的都是用户命令了.

if (msg.syscmd=='recive') {//如果是系统回复的收到命令

if (!user.sends.some((send)=>{

if (send.req==msg.req) {

user.sends.remove(send);

if (send.body) send.callback(msg.body);

if (isServer) server_reSend(); else client_reSend();

return true;

}

})) msg.syscmd='send'; //如果sends没有这命令,设置成send后续判断modules里能否处理。

}

if (msg.syscmd=='send') {//收到用户数据

if (!modules[msg.module]) {

send(user,{req:msg.req,syscmd:'recive',module:msg.module,cmd:msg.cmd,body:{result:ROOT.error.Not_found_module,content:{message:'没有找到模块'}}});

return;

}

let module=isServer?new modules[msg.module](user,msg.body):new modules[msg.module](msg.body);

if (!module[msg.cmd]) {//如果没有命令

send(user,{req:msg.req,syscmd:'recive',rinfo:rinfo,module:msg.module,cmd:msg.cmd,body:{result:ROOT.error.Not_found_cmd,content:{message:'没有找到命令'}}});

return;

}

let ret;

ret=await module[msg.cmd]();

if (msg.repeat) {//如果需要回复

if (ret) {//如果有回复内容

send(user,{req:msg.req,syscmd:'recive',module:msg.module,cmd:msg.cmd,body:ret});

} else {//如果没有回复内容

send(user,{req:msg.req,syscmd:'recive',module:msg.module,cmd:msg.cmd});

}

return;

}//如果不需要回复,则不处理了

}

};

function server_reSend() {//处理重复发送

for(let id in users) {

let sends=users[id].sends;

if (sends.length) {//如果没有要发送的数据,则先退出

let _send=sends[0];// 先处理第一个

let now=new Date();

if (_send.count>(opt.overcount||5)) {

sends.shift();

ROOT.info.droped++;

_send.callback({result:ROOT.error.overTime,content:{message:"发送超时"}});

} else {

debug.show("超时发送数据");

//if (now-_send.sendTime>(opt.overtime||5000)) {//5秒超时就重新发

_send.sendTime=now;

send(user[id],_send,true);

_send.count++;

//}

}

}

}

}

function client_reSend() {//处理重复发送

let sends=user.sends;

if (sends.length) {//如果没有要发送的数据,则先退出

let _send=sends[0];// 先处理第一个

let now=new Date();

if (_send.count>(opt.overcount||5)) {

sends.shift();

ROOT.info.droped++;

_send.callback({result:ROOT.error.overTime,content:{message:"发送超时"}});

} else {

debug.show("超时发送数据");

//if (now-_send.sendTime>(opt.overtime||5000)) {//5秒超时就重新发

_send.sendTime=now;

send(user,_send,true);

_send.count++;

//}

}

}

}

setInterval(()=>{

//检测在线用户

let now=new Date().getTime();

if (isServer) {

server_reSend();

for(let id in users) {

let u=users[id];

if (now-u.updateTime>(opt.userOverTime||20000)) {

if (typeof(opt.disconnected)=='function') opt.disconnected(u);//通知用户退出回调函数,

delete users[id];

}

}

}else {

client_reSend();

if (now-sendTime>(opt.heartTime||10000)) {//发送心跳包

send(user,{syscmd:'heart'},true);

}

}

},opt.overtime||5000);

this.use=function(name,module) {

modules[name]=module;//加载模块

}

let total={

time:new Date().getTime(),

tx:0,

rx:0,

send(len){

this.tx+=len;

this.overtime();

},

recv(len){

this.rx+=len;

this.overtime();

},

overtime() {

if (new Date().getTime()-this.time>1000) {

ROOT.info.speedTx=this.tx;

ROOT.info.speedRx=this.rx;

this.tx=0;

this.rx=0;

this.time=new Date().getTime();

}

}

}

function send(_user,value,unpush) {

if (value.repeat && !unpush) _user.sends.push(value);

value.userid=_user.id;

value.callback=value.callback || function(){};

let str=JSON.stringify(value);

debug.show("发送数据到" + _user.rinfo.address +":"+_user.rinfo.port);

debug.show(str);

sendTime=new Date().getTime();

socketSend(str,_user.rinfo.port,_user.rinfo.address,(err,bytes)=>{

if (err) {

value.callback({result:ROOT.error.sendFaild,content:{message:err}});

} else {

total.send(bytes);//计算发送量

}

});

}

this.send=function (_user,module,cmd,body,callback){

if (!isServer) {

callback=body;

body=cmd;

cmd=module;

module=_user;

_user=user;

}

let value={

module:module,

syscmd:'send',

cmd:cmd,

body:{content:body},

req:REQ++,

count:0,

sendTime:new Date(),

callback:callback,//回调函数

repeat:typeof(callback)=='function',//如果有回调,就需要回复。

}

send(_user,value);

return this;

}

this.connect=function(ip,port,callback) {//连接服务器

isServer=false;

server=ip||server;

serverport=port||serverport;

user={rinfo:{port:serverport,address:server},sends:[]};

send(user,{

syscmd:'login',

repeat:true,

count:0,

callback:callback,

time:new Date(),

req:REQ++

});

}

}

//==================测试代码==============================

let udp;

function sendLoop() {

udp.send("user","login",{body:'body'},(msg)=>{

console.log(msg.result?msg.content.message:'登录成功');

});

}

let argv=process.argv;

if (!argv[2]) {//如果是服务器

console.log("建立服务器");

udp=new UDP({port:10010,debug:{enabled:false},connected:(user)=>{

setTimeout(()=>{

udp.send(user,'user','logined',{id:3},(msg)=>{

console.log("logined result=",msg.result);

});

},1000);

console.log("用户登陆",user);

},disconnected:(user)=>{

console.log("用户退出",user);

}});

udp.use('user',require('./udp_user.js'));

} else {//如果是客户端

console.log("建立客户端");

udp=new UDP({server:"127.0.0.1",serverport:10010,port:10011,debug:{enabled:false},connected:(msg)=>{

if (msg.result) {

console.log(msg.content.message);

} else {

sendLoop();

}

}});

udp.use('user',function(_msg) {

let msg=_msg;

this.logined=()=>{

console.log("msg =",msg);

return {result:9,message:'999999'};

}

});

}

//====================================

//dos下用 node udp.js 启动udp服务器

//node udp.js asdf 启动udp客户端

标签: #js通信