Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

TCPConnection.cs

/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2008 Philippe Durand <draekz@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Collections.Generic;

using Anculus.Core;
using Galaxium.Core;

namespace Galaxium.Protocol
{
      public class TCPConnection : AbstractConnection
      {
            protected Socket _socket;
            protected bool _sending;
      
            public Socket Socket
            {
                  get { return _socket; }
            }
            
            public override bool IsConnected
            {
                  get
                  {
                        lock (_lock)
                        {
                              if (_socket != null)
                              {
                                    // Socket.Connected doesn't tell us if the socket is actually connected...
                                    // http://msdn2.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx
                                    
                                    bool blocking = _socket.Blocking;
                                    try
                                    {
                                          _socket.Blocking = false;
                                          _socket.Send (new byte[0], 0, 0);
                                          return true;
                                    }
                                    catch (SocketException ex)
                                    {
                                          // 10035 == WSAEWOULDBLOCK
                                          if (ex.NativeErrorCode.Equals(10035))
                                                return true;
                                    } finally {
                                          _socket.Blocking = blocking;
                                    }
                              }

                              return false;
                        }
                  }
            }
            
            // Return true if we were connected at the last operation
            public bool WasConnected
            {
                  get
                  {
                        lock (_lock) {
                              if (_socket != null)
                                    return _socket.Connected;
                              return false;
                        }
                  }
            }
            
            public override bool IsSending
            {
                  get { return _sending; }
            }
            
            public TCPConnection (ISession session, IConnectionInfo connectionInfo) : base (session, connectionInfo)
            {
                  BufferInitialize ();
            }
            
            public override void Connect ()
            {
                  Log.Debug ("Connect to {0}:{1}", _connectionInfo.HostName, _connectionInfo.Port);
                  
                  _intendedDisconnect = false;
                  
                  try
                  {
                        lock (_lock)
                        {
                              IConfigurationSection section = Configuration.Proxy.Section;
                              
                              if (section.GetBool (Configuration.Proxy.UseProxy.Name, Configuration.Proxy.UseProxy.Default))
                              {
                                    string proxyHost = string.Empty;
                                    int proxyPort = 0;
                                    string proxyUser = string.Empty;
                                    string proxyPass = string.Empty;
                                    
                                    if (section.GetBool (Configuration.Proxy.UseSame.Name, Configuration.Proxy.UseSame.Default))
                                    {
                                          proxyHost = section.GetString (Configuration.Proxy.HttpHost.Name, Configuration.Proxy.HttpHost.Default);
                                          proxyPort = section.GetInt (Configuration.Proxy.HttpPort.Name, Configuration.Proxy.HttpPort.Default);
                                          proxyUser = section.GetString (Configuration.Proxy.HttpUsername.Name, Configuration.Proxy.HttpUsername.Default);
                                          proxyPass = section.GetString (Configuration.Proxy.HttpPassword.Name, Configuration.Proxy.HttpPassword.Default);
                                          
                                          try
                                          {
                                                Log.Debug ("Connecting using TUNNEL");
                                                _socket = TunnelSocket.ConnectToHttpsProxy (proxyHost, ushort.Parse(proxyPort.ToString()), _connectionInfo.HostName, ushort.Parse(_connectionInfo.Port.ToString()), string.Empty, string.Empty);
                                          }
                                          catch (ProxyConnectionException ex)
                                          {
                                                ThreadUtility.SyncDispatch (new VoidDelegate (delegate {
                                                      OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_CONNECT", ex.Message));
                                                }));
                                                
                                                return;
                                          }
                                    }
                                    else
                                    {
                                          proxyHost = section.GetString (Configuration.Proxy.SocksHost.Name, Configuration.Proxy.SocksHost.Default);
                                          proxyPort = section.GetInt (Configuration.Proxy.SocksPort.Name, Configuration.Proxy.SocksPort.Default);
                                          proxyUser = section.GetString (Configuration.Proxy.SocksUsername.Name, Configuration.Proxy.SocksUsername.Default);
                                          proxyPass = section.GetString (Configuration.Proxy.SocksPassword.Name, Configuration.Proxy.SocksPassword.Default);
                                          
                                          try
                                          {
                                                _socket = SocksSocket.ConnectToSocks5Proxy (proxyHost, ushort.Parse(proxyPort.ToString()), _connectionInfo.HostName, ushort.Parse(_connectionInfo.Port.ToString()), string.Empty, string.Empty);
                                          }
                                          catch (ProxyConnectionException ex)
                                          {
                                                ThreadUtility.SyncDispatch (new VoidDelegate (delegate {
                                                      OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_CONNECT", ex.Message));
                                                }));
                                                
                                                return;
                                          }
                                    }
                                    
                                    Log.Debug ("Using proxy {0}:{1}", proxyHost, proxyPort);
                              }
                              else
                              {
                                    _socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                                    
                                    _socket.Blocking = true;
                                    _socket.BeginConnect (GetAddress (_connectionInfo.HostName, _connectionInfo.Port), new AsyncCallback (EndConnectCallback), _socket);
                                    
                                    return;
                              }
                              
                              ThreadUtility.SyncDispatch (new VoidDelegate (delegate {
                                    OnEstablished (new ConnectionEventArgs (this));
                                    OnAfterConnect (new ConnectionEventArgs (this));
                              }));
                              
                              BeginDataReceive ();
                        }
                  }
                  catch (SocketException ex)
                  {
                        Log.Error (ex, "Unable to connect to server ["+ConnectionInfo.HostName+"].");
                        
                        ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                        {
                              OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_CONNECT", "Unable to connect to server."));
                        }));
                  }
            }
            
            protected override void Dispose (bool disposing)
            {
                  if (!_disposed)
                  {
                        if (disposing)
                        {
                              if (WasConnected)
                              {
                                    Log.Debug("Disconnecting during disposing of abstract connection.");
                                    Disconnect ();
                              }
                              
                              _socket = null;
                              
                              BufferUnload ();
                        }
                  }
                  
                  _disposed = true;
            }
            
            public override void Reconnect ()
            {
                  Disconnect ();
                  Connect ();
            }

            public override void Disconnect ()
            {
                  _intendedDisconnect = true;
                  _sending = false;
                  
                  if (WasConnected)
                  {
                        lock (_lock)
                        {
                              if (_socket != null && _socket.Connected)
                              {
                                    try
                                    {
                                          _socket.Shutdown (SocketShutdown.Both);
                                          _socket.Close ();
                                          _socket = null;
                                    }
                                    catch (SocketException e)
                                    {
                                          Log.Warn ("Unable to cleanly shutdown TCP socket: "+e.Message);
                                    }
                              }
                        }
                        
                        ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                        {
                              OnClosed (new ConnectionEventArgs (this));
                        }));
                  }
            }
            
            protected virtual void EndConnectCallback (IAsyncResult asyncResult)
            {
                  try
                  {
                        if (asyncResult.IsCompleted)
                        {
                              lock (_lock)
                              {
                                    Socket socket = asyncResult.AsyncState as Socket;
                                    
                                    if (IsConnected)
                                    {
                                          socket.EndConnect (asyncResult);
                                          socket.Blocking = false;
                                    }
                                    else
                                    {
                                          ThreadUtility.SyncDispatch (new VoidDelegate (delegate {
                                                OnErrorOccurred (new ConnectionErrorEventArgs (this, "SocketError", "Unable to connect socket."));
                                          }));
                                          
                                          return;
                                    }
                              }
                              
                              ThreadUtility.SyncDispatch (new VoidDelegate (delegate {
                                    OnEstablished (new ConnectionEventArgs (this));
                                    OnAfterConnect (new ConnectionEventArgs (this));
                              }));
                                                          
                              BeginDataReceive ();
                        }
                        else
                              throw new ApplicationException ();
                  }
                  catch (Exception ex)
                  {
                        Log.Error (ex, "Unable to connect to server ["+ConnectionInfo.HostName+"].");
                        
                        ThreadUtility.SyncDispatch (new VoidDelegate (delegate {
                              OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_CONNECT", "Unable to connect to server."));
                        }));
                        
                        Disconnect ();
                  }
            }

            protected virtual void BeginDataReceive ()
            {
                  byte[] buffer = GetBuffer ();
                  
                  lock (_lock)
                  {
                        if (_socket != null && _socket.Connected)
                              _socket.BeginReceive (buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback (EndReceiveCallback), new SocketStore (_socket, buffer));
                  }
            }

            protected virtual byte[] GetBuffer ()
            {
                  return BufferUtility.Allocate (BufferSize.Protocol);
            }

            protected virtual void FreeBuffer (ref byte[] buffer)
            {
                  BufferUtility.Free (BufferSize.Protocol, ref buffer);
            }

            protected virtual void BufferInitialize ()
            {
                  BufferUtility.IncreaseCapacity (BufferSize.Protocol);
            }

            protected virtual void BufferUnload ()
            {
                  BufferUtility.DecreaseCapacity (BufferSize.Protocol);
            }
            
            public override void Send (byte[] data)
            {
                  ThrowUtility.ThrowIfNull ("data", data);
                  
                  try
                  {
                        lock (_lock)
                        {
                              if (_socket != null && _socket.Connected)
                              {
                                    _sending = true;
                                    _socket.BeginSend (data, 0, data.Length, SocketFlags.None, new AsyncCallback (EndSendCallback), _socket);
                              }
                        }
                  }
                  catch (SocketException ex)
                  {
                        if (!_intendedDisconnect)
                        {
                              Log.Error (ex, "Send");
                              
                              ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                              {
                                    OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_SEND", "Data send error."));
                              }));
                        }
                  }
            }
            
            protected virtual void EndSendCallback (IAsyncResult asyncResult)
            {
                  if (asyncResult.IsCompleted)
                  {
                        lock (_lock)
                        {
                              Socket socket = asyncResult.AsyncState as Socket;
                              
                              if (socket != null && socket.Connected)
                              {
                                    socket.EndSend (asyncResult);
                                    _sending = false;
                              }
                        }
                                    
                        ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                        {
                              OnSendComplete (EventArgs.Empty);
                        }));
                  }
            }

            protected virtual void EndReceiveCallback (IAsyncResult asyncResult)
            {
                  try
                  {
                        int len = 0;
                        byte[] buffer = null;
                        
                        if (asyncResult.IsCompleted)
                        {
                              lock (_lock)
                              {
                                    SocketStore store = asyncResult.AsyncState as SocketStore;
                                    Socket socket = store.Socket;
                                    
                                    if (socket != null && socket.Connected)
                                    {
                                          len = socket.EndReceive (asyncResult);
                                          buffer = store.Buffer;
                                    }
                              }
                        }
                        else
                        {
                              SocketStore store = asyncResult.AsyncState as SocketStore;
                              buffer = store.Buffer;
                              FreeBuffer (ref buffer);
                              return;
                        }

                        if (len == 0)
                        {
                              FreeBuffer (ref buffer);
                              
                              ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                              {
                                    OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_RECEIVE_EMPTY", "Connection closed by server."));
                              }));
                              
                              Log.Error("Connection closed by server ["+ConnectionInfo.HostName+"].");
                              Disconnect ();
                              
                              return;
                        }
                        
                        ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                        {
                              OnDataReceived (new ConnectionDataEventArgs (this, buffer, len));
                        }));
                        
                        FreeBuffer (ref buffer);

                        BeginDataReceive ();
                  }
                  catch (Exception ex)
                  {
                        Log.Debug ("The connection ended "+(_intendedDisconnect ? "as expected." : "unexpectedly."));
                        
                        if (!_intendedDisconnect)
                        {
                              Log.Error (ex, "EndReceiveCallback");
                              
                              ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                              {
                                    OnErrorOccurred (new ConnectionErrorEventArgs (this, "PROTOCOL_RECEIVE", "Connection closed."));
                              }));
                              
                              Disconnect ();
                        }
                  }
            }
            
            private class SocketStore
            {
                  public Socket Socket;
                  public byte[] Buffer;

                  public SocketStore (Socket socket, byte[] buffer)
                  {
                        Socket = socket;
                        Buffer = buffer;
                  }
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index