我开发了一个使用websocket连接的小型数据库系统。 有时连接可能突然断开,而不是因为电缆或电力问题,原因现在我不清楚。
我添加了心跳信号,每3秒检查一次连接,在服务器端,如果连接突然突然变慢,我给出5秒的连接超时。
问题: 客户端意识到连接丢失后,它将尝试重新连接。有时它可以工作,但有时会反复失败-也许是因为打开的连接???怎么来的?我试图确保重新连接之前连接已完全关闭。我做错了吗?!
JS源(部分):
EF.WS=new EF_WebSocketInterface();
function EF_WebSocketInterface() {
var _=this,QS={_:0},QR={},ito,hb;
window.onunload=function() {
if(!_||!_.con) return;
// send termination command to the server
}
_.Init=function(p,f) { Run(f); }
function Run(f) {
if(_.con&&_.con.readyState!==3) return setTimeout(Run,1,f);
if(!hb) hb=setInterval(_.SMInt,3000,'-',''); // Heartbeat
}
try {
_.con=new WebSocket('wss://'+document.domain+':'+/* port */+'/?C='+ /* credentials been checked as OK */+'&L=EN-US');
_.con.onerror=function(e) { console.log(e); }
_.con.onopen=function() { _.con.tmt=Date.now(); }
_.con.onclose=function() { setTimeout(Run,1); } // immediate reconnection!
_.con.onmessage=function(e) {
var V=new FileReader(),C,I;
_.con.tmt=Date.now(); // Any incomming messages from the server including the heartbeat are updating the time stamp!
V.addEventListener("loadend", function() {
V=V.result.replace(/\n/g,'').replace(/\r/g,'');
if(V=='-') return; // ignore server heartbeat for processing.
/* Message (command) processing */
});
V.readAsText(e.data);
}
}
catch(e){ console.log(e); if(_.con) _.con.onclose(); }
}
/* This is the official function to send commands.
This will register the message in special queue for a case connection lost, to resend them without repeated user interaction.
Parameters:
p = Command code
c = Parameters content for command
f = function callback to be done on message receive (optional)
x = reserved for internal use
*/
_.SM=function(p,c,f,x) {
var d=""+(QS._++); while(d.length<5) d="0"+d; QS[d]=[(x?"":EF.ON([p,c])),Date.now(),f]; _.SMInt((x?x:"A"),d);
}
/* This is internal command sender*/
_.SMInt=function(b,c) {
/* Command preparation and message ID management */
// Preparation for sending the message
if(_.con&&_.con.readyState==1)
// If pass more than 5 seconds, the connection probably closed.
if(_.con.tmt+5000<Date.now()) { _.con.close(); }
else {
// Sending of heartbeat to the server, also checking if has got any pending messages in queue since last failed session.
if(b=='-') {
if(ito) return; ito=1; var n=Object.keys(QS);
for(var i=0 ; i<n.length ; i++) if(n[i]!=='_' && QS[n[i]][1]+5000<Date.now()) _.SMInt("C",n[i]);
ito=0;
}
try { _.con.send(new Blob([m],{type:'text/html',charset:'utf-8'}),{ binary:true }); }
catch(e) { }
}
}
_.Get=function(p,m,r,c) { _.SM(m,r,function(e) { _.xQ(p,EF.Data,e); c(); });
}
}
https://www.crazygao.com/ef4中可用的完整JS代码
服务器端PERL(部分):
Net::WebSocket::Server->new(
listen => $ssl,
silence_max=> 14400,
tick_period => 1,
on_tick => sub {
my @l=keys %CON; # This is the main connections DB, each with its own properties
my $i; my $j; my @m;
$_TPC->[2]++; if($_TPC->[2]==5) { $_TPC->[2]=0; }
if($_TPC->[2]==0) {
for($i=0 ; $i<@l ; $i++) {
# Resend pending messages in queue
# If connection passed 10 seconds without any message, it is clearly lost: &CX is closing the connection and deleting the session from %CON.
if($CON{$l[$i]}{"C"}) {
if($CON{$l[$i]}{"T"}+10<time()) { &CX($l[$i],($CON{$l[$i]}{"T"}+300<time())?1:0); }
else { &SMInt($l[$i],'-',''); }
}
elsif($CON{$l[$i]} && $CON{$l[$i]}{"T"}+14400<time()) { &XX($l[$i]); }
}
},
on_connect => sub {
my($serv,$conn)=@_; my $cid;
$conn->on(
handshake => sub {
my($conn,$handshake)=@_;
my $nsn=$handshake->req->resource_name;
my $ns=substr($nsn,4,index($nsn,'&L=')-4);
$cid=($ns eq "")?int(rand(1000000000)):$ns;
if(!defined $CON{$cid}) { $CON{$cid}={"QS"=>{"_"=>0},"QR"=>{},"T"=>time(),"L"=>substr($nsn,index($nsn,'&L=')+3)}; }
elsif(defined $CON{$cid}{"C"}) { $CON{$cid}{"C"}->disconnect(); }
$CON{$cid}{"C"}=$conn;
},
binary => sub {
# for any incomming messages - update the active time stamp.
$CON{$cid}{"T"}=time(); my($c,$msg)=@_;
# ignore client side heartbeat here
if($msg eq '-') { return; }
# Process the message (command)
},
disconnect => sub {} # Nothing special to be done.
);
}
)->start;
sub CX {
$CON{$_[0]}{"C"}->disconnect();
delete $CON{$_[0]}{"C"}; if($_[1]==1 && $CON{$_[0]}{'U'} eq "") { &XX($_[0]); }
}
sub XX { delete $CON{$_[0]}; }
注意:
1.除非客户端将刷新浏览器,否则不会丢失作为JS变量存储在客户端的凭据,但这不是这种情况。
2.在此类“连接断开”上检查的readystate变量仍显示1-但消息未转发到服务器。
3.在反复出现连接故障时,如果刷新浏览器并重新连接,它将可以正常工作(奇怪...)。
4.在PERL中,如果有帮助,我正在使用Net :: Websocket :: Server和IO :: Socket :: SSL。
断开连接的PERL命令是否可能需要更长的时间才能关闭? 对于突然断开连接或为什么我无法重新连接的任何想法,将不胜感激。