PageRenderTime 19ms CodeModel.GetById 2ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/SparkleLib/SparkleListenerTcp.cs

http://github.com/hbons/SparkleShare
C# | 259 lines | 163 code | 66 blank | 30 comment | 21 complexity | 11772c06b6ceefae4bf0a4bef6699ebd MD5 | raw file
  1//   SparkleShare, a collaboration and sharing tool.
  2//   Copyright (C) 2010  Hylke Bons <hylkebons@gmail.com>
  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
 17
 18using System;
 19using System.Net.Sockets;
 20using System.Text;
 21using System.Threading;
 22
 23namespace SparkleLib {
 24
 25    public class SparkleListenerTcp : SparkleListenerBase {
 26
 27        private Socket socket;
 28        private Thread thread;
 29        private bool is_connected  = false;
 30        private bool is_connecting = false;
 31        private DateTime last_ping = DateTime.Now;
 32
 33
 34        public SparkleListenerTcp (Uri server, string folder_identifier) : base (server, folder_identifier)
 35        {
 36        }
 37
 38
 39        public override bool IsConnected {
 40            get {
 41                return this.is_connected;
 42            }
 43        }
 44
 45
 46        public override bool IsConnecting {
 47            get {
 48                return this.is_connecting;
 49            }
 50        }
 51
 52
 53        // Starts a new thread and listens to the channel
 54        public override void Connect ()
 55        {
 56            this.is_connecting = true;
 57
 58            this.thread = new Thread (() => {
 59                int port = Server.Port;
 60
 61                if (port < 0)
 62                    port = 443;
 63
 64                try {
 65                    this.socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) {
 66                        ReceiveTimeout = 5 * 1000,
 67                        SendTimeout    = 5 * 1000
 68                    };
 69
 70                    // Try to connect to the server
 71                    this.socket.Connect (Server.Host, port);
 72
 73                    this.is_connecting = false;
 74                    this.is_connected  = true;
 75
 76                    OnConnected ();
 77
 78                } catch (Exception e) {
 79                    this.is_connected  = false;
 80                    this.is_connecting = false;
 81
 82                    if (this.socket != null)
 83                        this.socket.Close ();
 84
 85                    OnDisconnected (SparkleLib.DisconnectReason.TimeOut, e.Message);
 86                    return;
 87                }
 88
 89
 90                byte [] bytes  = new byte [4096];
 91                int bytes_read = 0;
 92                this.last_ping = DateTime.Now;
 93
 94                // Wait for messages
 95                while (this.is_connected) {
 96                    try {
 97                        int i = 0;
 98                        int timeout = 300;
 99                        DisconnectReason reason = DisconnectReason.TimeOut;
100
101                        // This blocks the thread
102                        while (this.socket.Available < 1) {
103                            try {
104                                // We've timed out, let's ping the server to
105                                // see if the connection is still up
106                                if (i == timeout) {
107                                    SparkleLogger.LogInfo ("ListenerTcp", "Pinging " + Server);
108
109                                    byte [] ping_bytes = Encoding.UTF8.GetBytes ("ping\n");
110                                    byte [] pong_bytes = new byte [4096];
111
112                                    this.socket.Send (ping_bytes);
113
114                                    if (this.socket.Receive (pong_bytes) < 1)
115                                        // 10057 means "Socket is not connected"
116                                        throw new SocketException (10057);
117
118                                    SparkleLogger.LogInfo ("ListenerTcp", "Received pong from " + Server);
119
120                                    i = 0;
121                                    this.last_ping = DateTime.Now;
122
123                                } else {
124
125                                    // Check when the last ping occured. If it's
126                                    // significantly longer than our regular interval the
127                                    // system likely woke up from sleep and we want to
128                                    // simulate a disconnect
129                                    int sleepiness = DateTime.Compare (
130                                        this.last_ping.AddMilliseconds (timeout * 1000 * 1.2),
131                                        DateTime.Now
132                                    );
133
134                                    if (sleepiness <= 0) {
135                                        SparkleLogger.LogInfo ("ListenerTcp", "System woke up from sleep");
136                                        reason = DisconnectReason.SystemSleep;
137
138                                        // 10057 means "Socket is not connected"
139                                        throw new SocketException (10057);
140                                    }
141                                }
142
143                            // The ping failed: disconnect completely
144                            } catch (SocketException e) {
145                                Disconnect(reason, "Ping timeout: " + e.Message);
146                                return;
147                            }
148
149                            Thread.Sleep (1000);
150                            i++;
151                        }
152
153                    } catch (Exception) {
154                        return;
155                    }
156
157                    try {
158                        if (this.socket.Available > 0)
159                            bytes_read = this.socket.Receive (bytes);
160
161                        // Parse the received message
162                        if (bytes_read > 0) {
163                            string received = Encoding.UTF8.GetString (bytes);
164                            string line     = received.Substring (0, received.IndexOf ("\n"));
165
166                            if (!line.Contains ("!"))
167                                continue;
168
169                            string folder_identifier = line.Substring (0, line.IndexOf ("!"));
170                            string message           = CleanMessage (line.Substring (line.IndexOf ("!") + 1));
171
172                            // We have a message!
173                            if (!folder_identifier.Equals ("debug") && !string.IsNullOrEmpty (message))
174                                OnAnnouncement (new SparkleAnnouncement (folder_identifier, message));
175                        }
176
177                    } catch (SocketException e) {
178                        Disconnect (DisconnectReason.TimeOut, "Timeout during receiving: " + e.Message);
179                        return;
180                    }
181                }
182            });
183
184            this.thread.Start ();
185        }
186
187
188        private void Disconnect (DisconnectReason reason, string message)
189        {
190            this.is_connected  = false;
191            this.is_connecting = false;
192
193            if (this.socket != null) {
194                this.socket.Close ();
195                this.socket = null;
196            }
197
198            OnDisconnected (reason, message);
199        }
200
201
202        protected override void AlsoListenToInternal (string folder_identifier)
203        {
204            string to_send = "subscribe " + folder_identifier + "\n";
205
206            try {
207                this.socket.Send (Encoding.UTF8.GetBytes (to_send));
208                this.last_ping = DateTime.Now;
209
210            } catch (Exception e) {
211                this.is_connected  = false;
212                this.is_connecting = false;
213
214                OnDisconnected (DisconnectReason.TimeOut, e.Message);
215            }
216        }
217
218
219        protected override void AnnounceInternal (SparkleAnnouncement announcement)
220        {
221            string to_send = "announce " + announcement.FolderIdentifier + " " + announcement.Message + "\n";
222
223            try {
224                if (this.socket != null)
225                    this.socket.Send (Encoding.UTF8.GetBytes (to_send));
226
227                this.last_ping = DateTime.Now;
228
229            } catch (Exception e) {
230                this.is_connected  = false;
231                this.is_connecting = false;
232
233                OnDisconnected (DisconnectReason.TimeOut, e.Message);
234            }
235        }
236
237
238        public override void Dispose ()
239        {
240            if (this.socket != null) {
241                this.socket.Close ();
242                this.socket = null;
243            }
244
245            this.thread.Abort ();
246            this.thread.Join ();
247
248            base.Dispose ();
249        }
250
251
252        private string CleanMessage (string message)
253        {
254            message = message.Replace ("\n", "");
255            message = message.Replace ("\0", "");
256            return message.Trim ();
257        }
258    }
259}