/** ** ============================== ** O O O OOOO ** O O O O O O ** O O O O O O ** OOOO OOOO O OOO OOOO ** O O O O O O O ** O O O O O O O ** OOOO OOOO O O OOOO ** ============================== ** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de ** ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED ** BY THE AUTHOR(S). ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, ** MODIFIED, OR OTHERWISE USED IN A CONTEXT ** OUTSIDE OF THE SOFTWARE SYSTEM. ** ** $AUTHORS: Stefan Bosse ** $INITIAL: (C) 2006-2017 bLAB ** $CREATED: 28-3-15 by sbosse. ** $VERSION: 1.1.11 ** ** $INFO: ** * ================================ * DOS: UDP P2P connection module. * ================================ * ** ** $ENDOFINFO */ "use strict"; var log = 0; var Io = Require('com/io'); var Net = Require('dos/network'); var Buf = Require('dos/buf'); var Rpc = Require('dos/rpc'); var util = Require('util'); var Sch = Require('dos/scheduler'); var Comp = Require('com/compat'); var Perv = Comp.pervasives; var Hashtbl = Comp.hashtbl; var String = Comp.string; var Rand = Comp.random; var Array = Comp.array; var trace = Io.tracing; var div = Perv.div; var Status = Net.Status; var Conn = Require('dos/connutils'); if (global && global.TARGET && global.TARGET!= 'browser') { /* Virtual Link Connection (VLC) using connectionless UDP Messages ** ** Internet Server-capable Appl. only ** UDP Frames: Header,Data1,Data2,... * * RPC Frame Format: * * +-----------------------------------+ * MessageType (int16) * Transaction ID (int16) * Hostport (port) * Offset (int32) | * Size (int16) | Operation (int16) * Data (Buffer) | Sendport (port) * | Hop (int16) * | Hop_max (int16) * | Header? (header) * | NumDataFrags? (int16) * +-----------------------------------+ * * Unicast Mode: * Here Remote * | | * Node 1 ------ VLC -------------- Node 2 * rcv_ip snd_ip * rcv_ipport snd_ipport * src_ipport * * Multicast Mode * Node 2 * Node 1 -------- VLC ------------ Node 3 * Node 4 ... * * Ping/Link Frame Format: * * +-----------------------------------+ * MessageType (int16) * Conneciton Port (port) * Receiver IP Port (int32) * +-----------------------------------+ * +-----------------------------------+ * * * 1. A listener on address rcv_ip:rcv_ipport must be started (receiver) * 2. Unicast Mode: Another node can connect to this receiver and will be the other side * of the virtual link (snd_ip:snd_ipport), that can be used by the router * to forward RPCIO messages. * 3. Unicast Mode: Ping remote side periodically to test virtual link. No remote endpoint = (undefined:undefined), * 4. Multicast Mode: Any node can connect to the VLC (receive), and any other host can be contacted (send) */ var VLC_MAXLIVE=4; var VCMessageType = { VCMPING:1, VCMPONG:2, VCMLINK:3, VCMUNLINK:4, VCMRPCHEAD:6, VCMRPCDATA:7, print:function(op) { switch (op) { case VCMessageType.VCMPING: return "VCMPING"; case VCMessageType.VCMPONG: return "VCMPONG"; case VCMessageType.VCMLINK: return "VCMLINK"; case VCMessageType.VCMUNLINK: return "VCMUNLINK"; case VCMessageType.VCMRPCHEAD: return "VCMRPCHEAD"; case VCMessageType.VCMRPCDATA: return "VCMRPCDATA"; default: return "Connection.VCMessageType?"; } } }; var VCState = { VCS_NOTINIT:1, // Not initialized conenction VCS_INIT:2, // Server started, but not confirmed VCS_READY:3, // Server intiialized and confirmed (other end point not connected) VCS_NEGOTIATE:4, // Server intiialized, in negotiation state (other end point not connected) VCS_CONNECTED:5, // Other side connected VCS_NOTCONNECTED:6, // Other side not connected print:function(op) { switch (op) { case VCState.VCS_NOTINIT: return "VCS_NOTINIT"; case VCState.VCS_INIT: return "VCS_INIT"; case VCState.VCS_READY: return "VCS_READY"; case VCState.VCS_NEGOTIATE: return "VCS_NEGOTIATE"; case VCState.VCS_CONNECTED: return "VCS_CONNECTED"; case VCState.VCS_NOTCONNECTED: return "VCS_NOTCONNECTED"; default: return "Connection.VCState?"; } } }; var dgram = Require('dgram'); // type options = {conn_port,rcv_ip,rcv_ipport,snd_ip,snd_ipport,router,verbose} var udpConnection = function (options) { var self=this; this.host_port = undefined; // Host server port (== host node port), returned by ALIVE request this.conn_port = options.conn_port||Net.uniqport(); // Connection Link Port (this side) this._status = Net.Status.STD_OK; /* ** 1. Receive Connection Port (Local service point) ** 2. Send Connection Port establishing a connection to another host (Remote endpoint) */ this.rcv_ip = options.rcv_ip; // URL this.rcv_ipport = options.rcv_ipport; // IP port this.rcv_sock = dgram.createSocket("udp4"); /* ** The other communication endpoint ** 1. Public receiver IP port we are sending to * 2. Private sender IP port we are receiving from */ this.snd_ip = options.snd_ip; // -> URL this.snd_ipport = options.snd_ipport; // -> IP port this.src_ipport = undefined; // (IP port) -> this.snd_port = undefined; // Connection Link Port (other side) this.snd_sock = dgram.createSocket("udp4"); this.verbose = options.verbose||0; this.dlimit = 512; // MTU limited maximal data payload of a fragment! this.multicast=false; this.router=options.router; this.rpccon=Rpc.RpcConn( // Communication port self.conn_port, /* ** Rpcio Send/Forward Operation */ function(rpcio,callback) { // Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Send '+rpcio.tid); self.send(rpcio,callback); }, /* ** Rpcio Alive Operation */ function() { return self.state==VCState.VCS_CONNECTED; } ); /* ** Next ping time.. */ this.live=0; this.state=VCState.VCS_NOTINIT; this.timer=undefined; this.mode = Conn.Mode.TWOCHANN; this.callback=undefined; }; udpConnection.prototype.debug = function (v) {log=v}; udpConnection.prototype.init = function () { if (this.router) this.router.add_conn(this.rpccon); }; /** Negotiate a virtual communication link (peer-to-peer). * * @param snd_ip * @param snd_ipport * @param callback * * +------------+ * VCMessageType (int16) * Connection Port (port) * Receiver IP Port (int32) * +------------+ * */ udpConnection.prototype.link=function(snd_ip,snd_ipport,callback) { var self = this; var buf = Buf.Buffer(); var sock = this.snd_sock; this.rpccon.stats.op_link++; if (self.state==VCState.VCS_CONNECTED) return; Buf.buf_put_int16(buf, VCMessageType.VCMLINK); Buf.buf_put_port(buf,this.conn_port); if (snd_ip!=undefined) this.snd_ip=snd_ip; if (snd_ipport!=undefined) this.snd_ipport=snd_ipport; if (snd_ip==undefined) snd_ip=this.snd_ip; if (snd_ipport==undefined) snd_ipport=this.snd_ipport; Buf.buf_put_int32(buf, self.rcv_ipport); Io.log(((log+self.verbose)<2)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.link: to '+snd_ip + ':' + snd_ipport)); sock.send(buf.data,0,Buf.length(buf),snd_ipport,snd_ip,function (err) { if (err) { sock.close(); if (callback) callback(Status.STD_IOERR); } else { if (callback) callback(Status.STD_OK); } }); }; /** * * @param snd_ip * @param snd_ipport * @param callback * * +------------+ * VCMessageType (int16) * Connection Port (port) * Receiver IP Port (int32) * +------------+ */ udpConnection.prototype.ping=function(snd_ip,snd_ipport,callback) { var self = this; var buf = Buf.Buffer(); var sock = this.snd_sock; this.rpccon.stats.op_ping++; Buf.buf_put_int16(buf, VCMessageType.VCMPING); Buf.buf_put_port(buf,this.conn_port); if (snd_ip==undefined) snd_ip=this.snd_ip; if (snd_ipport==undefined) snd_ipport=this.snd_ipport; Buf.buf_put_int32(buf, self.rcv_ipport); Io.log(((log+self.verbose)<2)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.ping: to '+snd_ip + ':' + snd_ipport)); sock.send(buf.data,0,Buf.length(buf),snd_ipport,snd_ip,function (err) { if (err) { sock.close(); if (callback) callback(Status.STD_IOERR); } else { if (callback) callback(Status.STD_OK); } }); }; /** * * @param snd_ip * @param snd_ipport * @param callback * +------------+ * VCMessageType (int16) * Connection Port (port) * Receiver IP Port (int32) * +------------+ */ udpConnection.prototype.pong=function(snd_ip,snd_ipport,callback) { var self = this; var buf = Buf.Buffer(); var sock = this.snd_sock; this.rpccon.stats.op_pong++; Buf.buf_put_int16(buf, VCMessageType.VCMPONG); Buf.buf_put_port(buf,this.conn_port); if (snd_ip==undefined) snd_ip=this.snd_ip; if (snd_ipport==undefined) snd_ipport=this.snd_ipport; Buf.buf_put_int32(buf, self.rcv_ipport); Io.log(((log+self.verbose)<2)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.pong: to '+snd_ip + ':' + snd_ipport)); sock.send(buf.data,0,Buf.length(buf),snd_ipport,snd_ip,function (err) { if (err) { sock.close(); if (callback) callback(Status.STD_IOERR); } else { if (callback) callback(Status.STD_OK); } }); }; /** Install a receiver. * * * @param callback * @param [rcv_ip] * @param [rcv_ipport] */ udpConnection.prototype.receive=function(callback,rcv_ip,rcv_ipport) { var self = this; if (rcv_ip==undefined) rcv_ip=this.rcv_ip; if (rcv_ipport==undefined) rcv_ipport=this.rcv_ipport; // RPCIO fragmentation cache // TODO: garabage collection var cache = Hashtbl.create(); var buf = Buf.Buffer(); var sock = this.rcv_sock; sock.on('listening', function () { var address = sock.address(); if (self.verbose>=0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] UDP receiver listening on ' + address.address + ":" + address.port); }); sock.on('message', function (message, remote) { var rpcio,dfrags,dlist,msgtyp,port,ipport,discard; Io.log((log<2)||('Receive: '+Perv.mtime())); self.rpccon.stats.op_receive++; Buf.buf_init(buf); Buf.buf_of_str(buf,message); if (message.length >= 2) { msgtyp=Buf.buf_get_int16(buf); discard=false; Io.log(((log+self.verbose)<2)||(msgtyp==VCMessageType.VCMPING||msgtyp==VCMessageType.VCMPONG)|| ('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.receive('+VCState.print(self.state)+'): Receiving Message from '+remote.address + ':' + remote.port +' [' + message.length+'] '+VCMessageType.print(msgtyp))); switch (msgtyp) { case VCMessageType.VCMLINK: port = Buf.buf_get_port(buf); ipport = Buf.buf_get_int32(buf); if (!self.multicast && self.snd_ip==undefined && self.snd_ipport==undefined) { if (self.timer) clearTimeout(self.timer); self.snd_ip=remote.address; self.snd_ipport=ipport; self.snd_port=port; self.src_ipport=remote.port; if (self.verbose>0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Linked with ' + remote.address + ":" + ipport+', '+ Net.Print.port(port)); self.live=VLC_MAXLIVE; self.link(self.snd_ip,self.snd_ipport); self.state=VCState.VCS_CONNECTED; self.watchdog(true); } else if (!self.multicast && self.snd_ip && Conn.ipequal(self.snd_ip,remote.address) && self.snd_ipport==ipport && self.state==VCState.VCS_READY) { if (self.timer) clearTimeout(self.timer); self.snd_port=port; self.src_ipport=remote.port; self.live = VLC_MAXLIVE; self.link(self.snd_ip,self.snd_ipport); self.state=VCState.VCS_CONNECTED; if (self.verbose>0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Linked with preferred ' + remote.address + ":" + ipport+', '+ Net.Print.port(port)); self.watchdog(true); } else if (!self.multicast && self.state==VCState.VCS_CONNECTED && Net.Equal.port(port,self.snd_port)) { if (self.verbose>0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Refresh link with ' + remote.address + ":" + ipport+', '+ Net.Print.port(port)); self.src_ipport=remote.port; self.live = VLC_MAXLIVE; self.state=VCState.VCS_CONNECTED; // self.link(self.snd_ip,self.snd_ipport); self.watchdog(true); } else if (!self.multicast && self.state==VCState.VCS_CONNECTED && !Net.Equal.port(port,self.snd_port) && Conn.ipequal(self.snd_ip,remote.address) && self.snd_ipport==ipport) { self.state=VCState.VCS_READY; /* ** Reincarnation of node? */ self.snd_port=port; self.src_ipport=remote.port; if (self.verbose>0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Relinked with ' + remote.address + ":" + ipport+', '+ Net.Print.port(port)); self.live=VLC_MAXLIVE; self.link(self.snd_ip,self.snd_ipport); self.state=VCState.VCS_CONNECTED; self.watchdog(true); } break; case VCMessageType.VCMUNLINK: port = Buf.buf_get_port(buf); ipport = Buf.buf_get_int32(buf); if (!self.multicast && self.state==VCState.VCS_CONNECTED) { if (self.verbose>0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Unlink from ' + remote.address + ":" + ipport+', '+ Net.Print.port(port)); if (self.timer) clearTimeout(self.timer); self.timer=undefined; self.state=VCState.VCS_READY; } break; case VCMessageType.VCMPING: port = Buf.buf_get_port(buf); ipport = Buf.buf_get_int32(buf); if (self.state==VCState.VCS_INIT && Net.Equal.port(port,self.conn_port)) { /* ** Self ping message received: this connection end point is confirmed to be unique! */ if (self.timer) clearTimeout(self.timer); self.timer=undefined; self.state=VCState.VCS_READY; if (self.verbose>0) Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Ready.'); if (self.callback) { var f = self.callback; self.callback=undefined; f(Status.STD_OK); } } else { // Send back a PONG message with this connection port self.pong(remote.address,ipport); } break; case VCMessageType.VCMPONG: port = Buf.buf_get_port(buf); ipport = Buf.buf_get_int32(buf); if (self.state==VCState.VCS_CONNECTED && Net.Equal.port(self.snd_port,port)) { self.live = VLC_MAXLIVE; } break; case VCMessageType.VCMRPCHEAD: case VCMessageType.VCMRPCDATA: if (!self.multicast && self.src_ipport!=remote.port) discard=true; break; } } if (!discard && (msgtyp==VCMessageType.VCMRPCHEAD||msgtyp==VCMessageType.VCMRPCDATA) && message.length >= 12) { var tid = Buf.buf_get_int16(buf); var hostport = Buf.buf_get_port(buf); Io.log(((log+self.verbose)<2)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.receive: TID='+tid+' for HOSTPORT='+Net.Print.port(hostport)+' '+VCMessageType.print(msgtyp))); if (msgtyp==VCMessageType.VCMRPCHEAD) { // header frame rpcio = self.router.pkt_get(); rpcio.connport=self.conn_port; rpcio.tid=tid; rpcio.hostport=hostport; // FROM rpcio.operation = Buf.buf_get_int16(buf); rpcio.sendport = Buf.buf_get_port(buf); // TO rpcio.hop = Buf.buf_get_int16(buf); rpcio.hop_max = Buf.buf_get_int16(buf); switch (rpcio.operation) { case Rpc.Operation.LOOKUP: case Rpc.Operation.IAMHERE: rpcio.header=Buf.buf_get_hdr(buf); dfrags=0; break; default: rpcio.header=Buf.buf_get_hdr(buf); dfrags = Buf.buf_get_int16(buf); } rpcio.data=new Buffer(''); if (dfrags>0) { dlist = Array.range(0, dfrags - 1); Hashtbl.add(cache, rpcio.hostport + rpcio.tid, [rpcio, dlist, 1000]); rpcio=undefined; } else { if (self.router) self.router.route(rpcio); } // Io.inspect(cache) } else { // data frame var off = Buf.buf_get_int32(buf); var size = Buf.buf_get_int16(buf); var thisnum = off/self.dlimit; var handler = Hashtbl.find(cache,hostport+tid); if (handler!=undefined) { rpcio=handler[0]; Io.log(((log+self.verbose)<3)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.receive: adding data num='+ thisnum+' off='+off+' size='+size+' '+handler[1])); Buf.buf_get_buf(buf,rpcio,off,size); handler[1]=Array.filter(handler[1],function(num) {return (num!=thisnum)}); if (Array.empty(handler[1])) { Io.log(((log+self.verbose)<3)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.receive: finalize '+remote.address + ':' + remote.port)); // Io.out(rpcio.data.toString()); // Deliver rpcio.connport=self.conn_port; self.router.route(rpcio); Hashtbl.remove(cache,hostport+tid); } rpcio=undefined; } } } // console.log(); }); sock.bind(rcv_ipport, rcv_ip, function () { // self.snd_sock.bind(self.rcv_ipport, self.rcv_ip); self.state=VCState.VCS_INIT; if (callback) callback(Status.STD_OK); }); } /** Send a RPCIO to a remote node * * @param rpcio * @param {function((Status.STD_OK|*),rpcio)} [callback] * @param [snd_ip] * @param [snd_ipport] * * +------------+ * VCMessageType (int16) * RPCIO Transaction ID (int16) * RPCIO Host Port (port) * RPCIO Operation (int16) * RPCIO Send Port (port) * RPCIO Hop (int16) * RPCIO HopMax (int16) * ... * +------------+ */ udpConnection.prototype.send=function(rpcio,callback,snd_ip,snd_ipport) { var self = this; var buf = Buf.Buffer(); var sock = this.snd_sock; this.rpccon.stats.op_send++; var size = Buf.length(rpcio); var frags = div((size+self.dlimit-1),self.dlimit); if (snd_ip==undefined) snd_ip=this.snd_ip; if (snd_ipport==undefined) snd_ipport=this.snd_ipport; Buf.buf_put_int16(buf, VCMessageType.VCMRPCHEAD); Buf.buf_put_int16(buf,rpcio.tid); // Transaction Message number Buf.buf_put_port(buf, rpcio.hostport); Buf.buf_put_int16(buf,rpcio.operation); if (rpcio.sendport) Buf.buf_put_port(buf,rpcio.sendport); else Buf.buf_put_port(buf,Net.nilport); Buf.buf_put_int16(buf,rpcio.hop); Buf.buf_put_int16(buf,rpcio.hop_max); switch (rpcio.operation) { case Rpc.Operation.LOOKUP: case Rpc.Operation.IAMHERE: Buf.buf_put_hdr(buf,rpcio.header); break; default: Buf.buf_put_hdr(buf,rpcio.header); Buf.buf_put_int16(buf,frags); } Io.log(((log+self.verbose)<2)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.send: to '+self.snd_ip + ':' + self.snd_ipport +' TID='+rpcio.tid+ ' SENDPORT='+ Net.Print.port(rpcio.sendport)+ ' [' + size +']')); Io.log(((log+self.verbose)<3)||('Send VCMRPCHEAD Start: '+Perv.mtime())); sock.send(buf.data,0,Buf.length(buf),snd_ipport,snd_ip,function (err) { Io.log(((log+self.verbose)<3)||('Send VCMRPCHEAD Done: '+Perv.mtime())); if (err) { sock.close(); callback(Status.STD_IOERR,rpcio); } else { if (size >0) { var dsend = function (n, off) { var fsize,more; if (frags == 1) fsize = size; else if (n < frags) fsize = self.dlimit; else fsize = size - off; if (n==frags) more=0; else more=1; Buf.buf_init(buf); Buf.buf_put_int16(buf, VCMessageType.VCMRPCDATA); Buf.buf_put_int16(buf, rpcio.tid); // Transaction Message number Buf.buf_put_port(buf,rpcio.hostport); Buf.buf_put_int32(buf, off); // Data fragment offset Buf.buf_put_int16(buf, fsize); // Data fragment size if (rpcio.data==undefined) { Io.out('[PVDP] Invalid RPCIO in VCMRPCDATA send (#:'+n+' off='+off+') found: '+Rpc.Print.rpcio(rpcio)); } Buf.buf_put_buf(buf, rpcio, off, fsize); Io.log(((log+self.verbose)<3)||('Send VCMRPCDATA Start #'+n+': '+Perv.mtime())); sock.send(buf.data, 0, Buf.length(buf), self.snd_ipport, self.snd_ip, function (err) { Io.log(((log+self.verbose)<3)||('Send VCMRPCDATA Done #'+n+': '+Perv.mtime())); if (err) { sock.close(); if (callback) callback(Status.STD_IOERR,rpcio); } else if (n < frags) dsend(n + 1, off + fsize); else { if (callback) callback(Status.STD_OK,rpcio); } }); }; dsend(1,0); } else { if (callback) callback(Status.STD_OK,rpcio); } } }) }; udpConnection.prototype.start = function (callback) { var self=this; var interval = 1000; // Start Listener (asynchronous) this.callback=callback; this.receive(function () { /* * Test that this server UDP IP-port is not used already by another connection port. We must receive * our own PING message. Otherwise another connection port listening * on the same IP port. */ self.ping(self.rcv_ip,self.rcv_ipport); self.timer=setTimeout(function () { // No PING received, shut down... Io.out('[PVDP '+Net.Print.port(self.conn_port)+'] Not received my PING on ' + self.rcv_ip + ":" + self.rcv_ipport+ ', propably IP-port used by another connection port. Shutting down...'); self.state=VCState.VCS_NOTINIT; self.rcv_sock.close(); self.snd_sock.close(); },1000); }); // Install a garabage collector Sch.AddTimer(interval, 'VLC Garbage Collector '+Net.Print.port(this.conn_port), function (context) { }); }; udpConnection.prototype.status = function () { return this._status; } udpConnection.prototype.stop = function (callback) { var self=this; var buf = Buf.Buffer(); if (this.timer) clearTimeout(self.timer); switch (self.state) { case VCState.VCS_CONNECTED: if (self.verbose>=1) Io.out('[PVDP ' + Net.Print.port(self.conn_port) + '] Unlinking ' + self.snd_ip + ":" + self.snd_ipport); self.unlink(undefined,undefined,callback); self.state = VCState.VCS_NOTCONNECTED; self.snd_ip = undefined; self.snd_ipport = undefined; break; } }; /** Unlink operation * */ udpConnection.prototype.unlink=function(snd_ip,snd_ipport,callback) { var self = this; var buf = Buf.Buffer(); var sock = this.snd_sock; this.rpccon.stats.op_unlink++; if (self.state!=VCState.VCS_CONNECTED) return; Buf.buf_put_int16(buf, VCMessageType.VCMUNLINK); Buf.buf_put_port(buf,this.conn_port); if (snd_ip!=undefined) this.snd_ip=snd_ip; if (snd_ipport!=undefined) this.snd_ipport=snd_ipport; if (snd_ip==undefined) snd_ip=this.snd_ip; if (snd_ipport==undefined) snd_ipport=this.snd_ipport; Buf.buf_put_int32(buf, self.rcv_ipport); Io.log(((log+self.verbose)<2)||('[PVDP '+Net.Print.port(self.conn_port)+'] udpConnection.unlink: to '+snd_ip + ':' + snd_ipport)); sock.send(buf.data,0,Buf.length(buf),snd_ipport,snd_ip,function (err) { if (err) { sock.close(); if (callback) callback(Status.STD_IOERR); } else { if (callback) callback(Status.STD_OK); } }); }; /** Install a watchdog timer. * * 1. If link state is VCS_READY, retry link request. * 2. If link state is VCS_CONNECTED, check link end point. * * @param run */ udpConnection.prototype.watchdog = function(run) { var self=this; if (self.timer) clearTimeout(self.timer); if (run) self.timer=setTimeout(function () { self.timer = undefined; switch (self.state) { case VCState.VCS_CONNECTED: if (self.live == 0) { // No PING received, disconnect... if (self.verbose>=1) Io.out('[PVDP ' + Net.Print.port(self.conn_port) + '] Endpoint ' + self.snd_ip + ":" + self.snd_ipport + ' not responding, propably dead. Unlinking...'); self.state = VCState.VCS_NOTCONNECTED; self.snd_ip = undefined; self.snd_ipport = undefined; } else { self.live--; self.watchdog(true); self.ping(); } break; case VCState.VCS_READY: Io.log(((log+self.verbose)<1)||('[PVDP ' + Net.Print.port(self.conn_port) + '] Retrying Link to ' + self.snd_ip + ":" + self.snd_ipport)); self.link(); self.watchdog(true); break; } },1000); }; } module.exports = { /** Virtual Connection Linkc (VLC) using connectionless UDP * * @param conn_port * @param [rcv_ip] * @param [rcv_ipport] * @param [snd_ip] * @param [snd_ipport] * @param [router] * @returns {udpConnection} * @constructor */ /** * type options = {conn_port,rcv_ip,rcv_ipport,snd_ip,snd_ipport,router,verbose} */ Connection: function(options) { var obj = new udpConnection(options); Object.preventExtensions(obj); return obj; } };