PageRenderTime 60ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/Sparkles/TcpListener.cs

https://github.com/hbons/SparkleShare
C# | 259 lines | 163 code | 66 blank | 30 comment | 21 complexity | dfb82488e138dd18e3bf2a2b3387a53d MD5 | raw file
  1. // SparkleShare, a collaboration and sharing tool.
  2. // Copyright (C) 2010 Hylke Bons <hi@planetpeanut.uk>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as
  6. // published by the Free Software Foundation, either version 3 of the
  7. // License, or (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. using System;
  17. using System.Net.Sockets;
  18. using System.Text;
  19. using System.Threading;
  20. namespace Sparkles {
  21. public class TcpListener : BaseListener {
  22. private Socket socket;
  23. private Thread thread;
  24. private bool is_connected = false;
  25. private bool is_connecting = false;
  26. private DateTime last_ping = DateTime.Now;
  27. public TcpListener (Uri server, string folder_identifier) : base (server, folder_identifier)
  28. {
  29. }
  30. public override bool IsConnected {
  31. get {
  32. return this.is_connected;
  33. }
  34. }
  35. public override bool IsConnecting {
  36. get {
  37. return this.is_connecting;
  38. }
  39. }
  40. // Starts a new thread and listens to the channel
  41. public override void Connect ()
  42. {
  43. this.is_connecting = true;
  44. this.thread = new Thread (() => {
  45. int port = Server.Port;
  46. if (port < 0)
  47. port = 443;
  48. try {
  49. this.socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) {
  50. ReceiveTimeout = 5 * 1000,
  51. SendTimeout = 5 * 1000
  52. };
  53. // Try to connect to the server
  54. this.socket.Connect (Server.Host, port);
  55. this.is_connecting = false;
  56. this.is_connected = true;
  57. OnConnected ();
  58. } catch (Exception e) {
  59. this.is_connected = false;
  60. this.is_connecting = false;
  61. if (this.socket != null)
  62. this.socket.Close ();
  63. OnDisconnected (Sparkles.DisconnectReason.TimeOut, e.Message);
  64. return;
  65. }
  66. byte [] bytes = new byte [4096];
  67. int bytes_read = 0;
  68. this.last_ping = DateTime.Now;
  69. // Wait for messages
  70. while (this.is_connected) {
  71. try {
  72. int i = 0;
  73. int timeout = 300;
  74. DisconnectReason reason = DisconnectReason.TimeOut;
  75. // This blocks the thread
  76. while (this.socket.Available < 1) {
  77. try {
  78. // We've timed out, let's ping the server to
  79. // see if the connection is still up
  80. if (i == timeout) {
  81. Logger.LogInfo ("ListenerTcp", "Pinging " + Server);
  82. byte [] ping_bytes = Encoding.UTF8.GetBytes ("ping\n");
  83. byte [] pong_bytes = new byte [4096];
  84. this.socket.Send (ping_bytes);
  85. if (this.socket.Receive (pong_bytes) < 1)
  86. // 10057 means "Socket is not connected"
  87. throw new SocketException (10057);
  88. Logger.LogInfo ("ListenerTcp", "Received pong from " + Server);
  89. i = 0;
  90. this.last_ping = DateTime.Now;
  91. } else {
  92. // Check when the last ping occured. If it's
  93. // significantly longer than our regular interval the
  94. // system likely woke up from sleep and we want to
  95. // simulate a disconnect
  96. int sleepiness = DateTime.Compare (
  97. this.last_ping.AddMilliseconds (timeout * 1000 * 1.2),
  98. DateTime.Now
  99. );
  100. if (sleepiness <= 0) {
  101. Logger.LogInfo ("ListenerTcp", "System woke up from sleep");
  102. reason = DisconnectReason.SystemSleep;
  103. // 10057 means "Socket is not connected"
  104. throw new SocketException (10057);
  105. }
  106. }
  107. // The ping failed: disconnect completely
  108. } catch (SocketException e) {
  109. Disconnect(reason, "Ping timeout: " + e.Message);
  110. return;
  111. }
  112. Thread.Sleep (1000);
  113. i++;
  114. }
  115. } catch (Exception) {
  116. return;
  117. }
  118. try {
  119. if (this.socket.Available > 0)
  120. bytes_read = this.socket.Receive (bytes);
  121. // Parse the received message
  122. if (bytes_read > 0) {
  123. string received = Encoding.UTF8.GetString (bytes);
  124. string line = received.Substring (0, received.IndexOf ("\n"));
  125. if (!line.Contains ("!"))
  126. continue;
  127. string folder_identifier = line.Substring (0, line.IndexOf ("!"));
  128. string message = CleanMessage (line.Substring (line.IndexOf ("!") + 1));
  129. // We have a message!
  130. if (!folder_identifier.Equals ("debug") && !string.IsNullOrEmpty (message))
  131. OnAnnouncement (new Announcement (folder_identifier, message));
  132. }
  133. } catch (SocketException e) {
  134. Disconnect (DisconnectReason.TimeOut, "Timeout during receiving: " + e.Message);
  135. return;
  136. }
  137. }
  138. });
  139. this.thread.Start ();
  140. }
  141. private void Disconnect (DisconnectReason reason, string message)
  142. {
  143. this.is_connected = false;
  144. this.is_connecting = false;
  145. if (this.socket != null) {
  146. this.socket.Close ();
  147. this.socket = null;
  148. }
  149. OnDisconnected (reason, message);
  150. }
  151. protected override void AlsoListenToInternal (string folder_identifier)
  152. {
  153. string to_send = "subscribe " + folder_identifier + "\n";
  154. try {
  155. this.socket.Send (Encoding.UTF8.GetBytes (to_send));
  156. this.last_ping = DateTime.Now;
  157. } catch (Exception e) {
  158. this.is_connected = false;
  159. this.is_connecting = false;
  160. OnDisconnected (DisconnectReason.TimeOut, e.Message);
  161. }
  162. }
  163. protected override void AnnounceInternal (Announcement announcement)
  164. {
  165. string to_send = "announce " + announcement.FolderIdentifier + " " + announcement.Message + "\n";
  166. try {
  167. if (this.socket != null)
  168. this.socket.Send (Encoding.UTF8.GetBytes (to_send));
  169. this.last_ping = DateTime.Now;
  170. } catch (Exception e) {
  171. this.is_connected = false;
  172. this.is_connecting = false;
  173. OnDisconnected (DisconnectReason.TimeOut, e.Message);
  174. }
  175. }
  176. public override void Dispose ()
  177. {
  178. if (this.socket != null) {
  179. this.socket.Close ();
  180. this.socket = null;
  181. }
  182. this.thread.Abort ();
  183. this.thread.Join ();
  184. base.Dispose ();
  185. }
  186. private string CleanMessage (string message)
  187. {
  188. message = message.Replace ("\n", "");
  189. message = message.Replace ("\0", "");
  190. return message.Trim ();
  191. }
  192. }
  193. }