listener tcp: Improve reconnect logic to work in after system sleeps and handle more errors

This commit is contained in:
Hylke Bons 2012-02-09 23:42:51 +01:00
parent f2a89c29df
commit 2c98cf1acd

View file

@ -29,7 +29,7 @@ namespace SparkleLib {
private Object socket_lock = new Object (); private Object socket_lock = new Object ();
private bool is_connected = false; private bool is_connected = false;
private bool is_connecting = false; private bool is_connecting = false;
private int receive_timeout = 180 * 1000; private DateTime last_ping = DateTime.Now;
public SparkleListenerTcp (Uri server, string folder_identifier) : public SparkleListenerTcp (Uri server, string folder_identifier) :
@ -70,9 +70,8 @@ namespace SparkleLib {
lock (this.socket_lock) { lock (this.socket_lock) {
this.socket = new Socket (AddressFamily.InterNetwork, this.socket = new Socket (AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp) { SocketType.Stream, ProtocolType.Tcp) {
ReceiveTimeout = 5 * 1000,
ReceiveTimeout = this.receive_timeout, SendTimeout = 5 * 1000
SendTimeout = 10 * 1000
}; };
// Try to connect to the server // Try to connect to the server
@ -99,6 +98,11 @@ namespace SparkleLib {
this.is_connected = false; this.is_connected = false;
this.is_connecting = false; this.is_connecting = false;
if (this.socket.Connected)
this.socket.Shutdown (SocketShutdown.Both);
this.socket.Dispose ();
OnDisconnected (e.Message); OnDisconnected (e.Message);
return; return;
} }
@ -106,81 +110,94 @@ namespace SparkleLib {
byte [] bytes = new byte [4096]; byte [] bytes = new byte [4096];
int bytes_read = 0; int bytes_read = 0;
DateTime last_ping = DateTime.Now;
// Wait for messages // Wait for messages
while (this.is_connected) { while (this.is_connected) {
try {
// This blocks the thread
bytes_read = this.socket.Receive (bytes);
// We've timed out, let's ping the server to // This blocks the thread
// see if the connection is still up int i = 0;
} catch (SocketException) { while (this.socket.Available < 1) {
Thread.Sleep (1000);
Console.WriteLine ("Waiting for available bytes... " + i);
i++;
try { try {
// Check when the last ping occured. If it's // We've timed out, let's ping the server to
// significantly longer than our regular interval the // see if the connection is still up
// system likely woke up from sleep and we want to if (i == 60) {
// simulate a disconnect SparkleHelpers.DebugInfo ("ListenerTcp",
int sleepiness = DateTime.Compare ( "Pinging " + Server);
last_ping.AddMilliseconds (this.receive_timeout * 1.25),
DateTime.Now
);
if (sleepiness <= 0) { byte [] ping_bytes = Encoding.UTF8.GetBytes ("ping\n");
// 10057 means "Socket is not connected" byte [] pong_bytes = new byte [4096];
// TODO: remove debug output
Console.WriteLine ("SLEEP OCCURED"); lock (this.socket_lock)
throw new SocketException (10057); this.socket.Send (ping_bytes);
if (this.socket.Receive (pong_bytes) < 1)
// 10057 means "Socket is not connected"
throw new SocketException (10057);
SparkleHelpers.DebugInfo ("ListenerTcp",
"Received pong from " + Server);
i = 0;
this.last_ping = DateTime.Now;
} else {
// Check when the last ping occured. If it's
// significantly longer than our regular interval the
// system likely woke up from sleep and we want to
// simulate a disconnect
int sleepiness = DateTime.Compare (
this.last_ping.AddMilliseconds (60 * 1000 * 1.2),
DateTime.Now
);
if (sleepiness <= 0) {
SparkleHelpers.DebugInfo ("ListenerTcp",
"System woke up from sleep");
// 10057 means "Socket is not connected"
throw new SocketException (10057);
}
} }
SparkleHelpers.DebugInfo ("ListenerTcp", "Pinging " + Server);
byte [] ping_bytes = Encoding.UTF8.GetBytes ("ping\n");
byte [] pong_bytes = new byte [4096];
lock (this.socket_lock)
this.socket.Send (ping_bytes);
this.socket.ReceiveTimeout = 10 * 1000;
if (this.socket.Receive (pong_bytes) < 1)
// 10057 means "Socket is not connected"
throw new SocketException (10057);
SparkleHelpers.DebugInfo ("ListenerTcp", "Received pong from " + Server);
this.socket.ReceiveTimeout = this.receive_timeout;
last_ping = DateTime.Now;
// The ping failed: disconnect completely // The ping failed: disconnect completely
} catch (SocketException) { } catch (SocketException) {
this.is_connected = false; this.is_connected = false;
this.is_connecting = false; this.is_connecting = false;
this.socket.ReceiveTimeout = this.receive_timeout;
if (this.socket.Connected)
this.socket.Shutdown (SocketShutdown.Both);
this.socket.Dispose ();
OnDisconnected ("Ping timeout"); OnDisconnected ("Ping timeout");
break; return;
} }
} }
if (this.socket.Available > 0)
lock (this.socket_lock)
bytes_read = this.socket.Receive (bytes);
// Parse the received message // Parse the received message
if (bytes_read > 0) { if (bytes_read > 0) {
string received = Encoding.UTF8.GetString (bytes); string received = Encoding.UTF8.GetString (bytes);
string line = received.Substring (0, received.IndexOf ("\n")); string line = received.Substring (0, received.IndexOf ("\n"));
if (!line.Contains ("!")) if (!line.Contains ("!"))
continue; continue;
string folder_identifier = line.Substring (0, line.IndexOf ("!")); string folder_identifier = line.Substring (0, line.IndexOf ("!"));
string message = CleanMessage (line.Substring (line.IndexOf ("!") + 1)); string message = CleanMessage (line.Substring (line.IndexOf ("!") + 1));
if (!folder_identifier.Equals ("debug") && if (!folder_identifier.Equals ("debug") &&
!String.IsNullOrEmpty (message)) { !String.IsNullOrEmpty (message)) {
// We have a message! // We have a message!
OnAnnouncement (new SparkleAnnouncement (folder_identifier, message)); OnAnnouncement (new SparkleAnnouncement (folder_identifier, message));
} }
@ -201,6 +218,8 @@ namespace SparkleLib {
lock (this.socket_lock) lock (this.socket_lock)
this.socket.Send (Encoding.UTF8.GetBytes (to_send)); this.socket.Send (Encoding.UTF8.GetBytes (to_send));
this.last_ping = DateTime.Now;
} catch (SocketException e) { } catch (SocketException e) {
this.is_connected = false; this.is_connected = false;
this.is_connecting = false; this.is_connecting = false;
@ -219,6 +238,8 @@ namespace SparkleLib {
lock (this.socket_lock) lock (this.socket_lock)
this.socket.Send (Encoding.UTF8.GetBytes (to_send)); this.socket.Send (Encoding.UTF8.GetBytes (to_send));
this.last_ping = DateTime.Now;
} catch (SocketException e) { } catch (SocketException e) {
this.is_connected = false; this.is_connected = false;
this.is_connecting = false; this.is_connecting = false;