using System; using System.Threading; using System.Net.Sockets; using Uber.Core; using Uber.Util; using Uber.Messages; namespace Uber.Net { /// /// Represents a TCP network connection accepted by an IonTcpConnectionListener. Provides methods for sending and receiving data, aswell as disconnecting the connection. /// public class TcpConnection { #region Fields /// /// The buffer size for receiving data. /// private const int RECEIVEDATA_BUFFER_SIZE = 512; /// /// The amount of milliseconds to sleep when receiving data before processing the message. When this constant is 0, the data will be processed immediately. /// private static int RECEIVEDATA_MILLISECONDS_DELAY = 1; /// /// The ID of this connection as a 32 bit unsigned integer. /// private readonly uint mID; /// /// A DateTime object representing the date and time this connection was created. /// private readonly DateTime mCreatedAt; /// /// The System.Net.Sockets.Socket object providing the connection between client and server. /// private Socket mSocket = null; /// /// The byte array holding the buffer for receiving data from client. /// private byte[] mDataBuffer = null; /// /// The AsyncCallback instance for the thread for receiving data asynchronously. /// private AsyncCallback mDataReceivedCallback; /// /// The RouteReceivedDataCallback to route received data to another object. /// private RouteReceivedDataCallback mRouteReceivedDataCallback; private bool mEncryptionStarted; /// /// The HabboHexRC4 instance providing the deciphering of enciphered client messages. /// private HabboHexRC4 mRc4; #endregion #region Members public delegate void RouteReceivedDataCallback(ref byte[] Data); #endregion #region Properties /// /// Gets the ID of this connection as a 32 bit unsigned integer. /// public uint ID { get { return mID; } } /// /// Gets the DateTime object representing the date and time this connection was created at. /// public DateTime createdAt { get { return mCreatedAt; } } /// /// Gets the age of this connection in whole seconds, by comparing the current date and time to the date and time this connection was created at. /// public int ageInSeconds { get { int Seconds = (int)(DateTime.Now - mCreatedAt).TotalSeconds; if (Seconds <= 0) return 0; return Seconds; } } /// /// Gets the IP address of this connection as a string. /// public string ipAddress { get { if (mSocket == null) return ""; return mSocket.RemoteEndPoint.ToString().Split(':')[0]; } } /// /// Gets if this connection still posesses a socket object. /// public bool Alive { get { return (mSocket != null); } } #endregion #region Constructors /// /// Constructs a new instance of IonTcpConnection for a given connection identifier and socket. /// /// The unique ID used to identify this connection in the environment. /// The System.Net.Sockets.Socket of the new connection. public TcpConnection(uint ID, Socket pSocket) { mID = ID; mSocket = pSocket; mCreatedAt = DateTime.Now; } #endregion #region Methods /// /// Starts the connection, prepares the received data buffer and waits for data. /// public void Start(RouteReceivedDataCallback dataRouter) { mDataBuffer = new byte[RECEIVEDATA_BUFFER_SIZE]; mDataReceivedCallback = new AsyncCallback(DataReceived); mRouteReceivedDataCallback = dataRouter; WaitForData(); } /// /// Stops the connection, disconnects the socket and disposes used resources. /// public void Stop() { if (!this.Alive) return; // Already stopped mSocket.Close(); mSocket = null; mDataBuffer = null; mDataReceivedCallback = null; mRc4 = null; } public bool TestConnection() { try { return mSocket.Send(new byte[] { 0 }) > 0; //mSocket.Send(new byte[] { 0 }); //return true; } catch { } return false; } private void ConnectionDead() { UberEnvironment.GetGame().GetClientManager().StopClient(mID); } public void SendData(byte[] Data) { if (this.Alive) { try { mSocket.Send(Data); } catch (SocketException) { ConnectionDead(); } catch (ObjectDisposedException) { ConnectionDead(); } catch (Exception ex) { UberEnvironment.GetLogging().WriteUnhandledExceptionError("IonTcpConnection.Send", ex); } } } public void SendData(string sData) { SendData(UberEnvironment.GetDefaultEncoding().GetBytes(sData)); } public void SendMessage(ServerMessage message) { //UberEnvironment.GetLogging().WriteLine(" [" + mID + "] <-- " + message.Header + message.GetContentString()); SendData(message.GetBytes()); } public void SetEncryption(string sPublicKey) { mRc4 = new HabboHexRC4(sPublicKey); mEncryptionStarted = true; } /// /// Starts the asynchronous wait for new data. /// private void WaitForData() { if (this.Alive) { try { mSocket.BeginReceive(mDataBuffer, 0, RECEIVEDATA_BUFFER_SIZE, SocketFlags.None, mDataReceivedCallback, null); } catch (SocketException) { ConnectionDead(); } catch (ObjectDisposedException) { ConnectionDead(); } catch (Exception ex) { UberEnvironment.GetLogging().WriteUnhandledExceptionError("IonTcpConnection.WaitForData", ex); ConnectionDead(); } } } private void DataReceived(IAsyncResult iAr) { // Connection not stopped yet? if (this.Alive == false) return; // Do an optional wait before processing the data if (RECEIVEDATA_MILLISECONDS_DELAY > 0) Thread.Sleep(RECEIVEDATA_MILLISECONDS_DELAY); // How many bytes has server received? int numReceivedBytes = 0; try { numReceivedBytes = mSocket.EndReceive(iAr); } catch (ObjectDisposedException) { ConnectionDead(); return; } catch (Exception ex) { UberEnvironment.GetLogging().WriteUnhandledExceptionError("IonTcpConnection.DataReceived", ex); ConnectionDead(); return; } if (numReceivedBytes > 0) { // Copy received data buffer byte[] dataToProcess = ByteUtil.ChompBytes(mDataBuffer, 0, numReceivedBytes); // Decipher received data? if (mEncryptionStarted) { dataToProcess = mRc4.Decipher(dataToProcess, numReceivedBytes); } // Route data to GameClient to parse and process messages RouteData(ref dataToProcess); //Environment.GetHabboHotel().GetClients().GetClient(this.ID).HandleConnectionData(ref dataToProcess); } // Wait for new data WaitForData(); } /// /// Routes a byte array passed as reference to another object. /// /// The byte array to route. private void RouteData(ref byte[] Data) { if (mRouteReceivedDataCallback != null) { mRouteReceivedDataCallback.Invoke(ref Data); } } #endregion } }