前言:
当前小伙伴们对“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通信