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
}
}