2015-01-13 18:29:11 +00:00
|
|
|
|
using System;
|
2014-07-30 15:04:21 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
2014-08-13 20:59:57 +00:00
|
|
|
|
using System.Text;
|
2015-03-17 21:28:51 +00:00
|
|
|
|
using System.Threading;
|
2015-01-13 18:29:11 +00:00
|
|
|
|
using ProtoBuf;
|
|
|
|
|
using ProtoBuf.Meta;
|
|
|
|
|
using xServer.Core.Compression;
|
|
|
|
|
using xServer.Core.Encryption;
|
|
|
|
|
using xServer.Core.Packets;
|
|
|
|
|
using xServer.Core.Packets.ClientPackets;
|
|
|
|
|
using xServer.Core.Packets.ServerPackets;
|
|
|
|
|
using xServer.Settings;
|
|
|
|
|
|
|
|
|
|
namespace xServer.Core
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
|
|
|
|
public class Client
|
|
|
|
|
{
|
|
|
|
|
public event ClientFailEventHandler ClientFail;
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public delegate void ClientFailEventHandler(Client s, Exception ex);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
private void OnClientFail(Exception ex)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
|
|
|
|
if (ClientFail != null)
|
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
ClientFail(this, ex);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
|
2014-07-08 12:58:53 +00:00
|
|
|
|
public event ClientStateEventHandler ClientState;
|
|
|
|
|
public delegate void ClientStateEventHandler(Client s, bool connected);
|
|
|
|
|
|
|
|
|
|
private void OnClientState(bool connected)
|
|
|
|
|
{
|
|
|
|
|
if (ClientState != null)
|
|
|
|
|
{
|
|
|
|
|
ClientState(this, connected);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public event ClientReadEventHandler ClientRead;
|
|
|
|
|
public delegate void ClientReadEventHandler(Client s, IPacket packet);
|
|
|
|
|
|
|
|
|
|
private void OnClientRead(byte[] e)
|
|
|
|
|
{
|
|
|
|
|
if (ClientRead != null)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public event ClientWriteEventHandler ClientWrite;
|
|
|
|
|
public delegate void ClientWriteEventHandler(Client s, IPacket packet, long length, byte[] rawData);
|
|
|
|
|
|
|
|
|
|
private void OnClientWrite(IPacket packet, long length, byte[] rawData)
|
|
|
|
|
{
|
|
|
|
|
if (ClientWrite != null)
|
|
|
|
|
{
|
|
|
|
|
ClientWrite(this, packet, length, rawData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public enum ReceiveType
|
|
|
|
|
{
|
|
|
|
|
Header,
|
|
|
|
|
Payload
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public const int HEADER_SIZE = 4;
|
|
|
|
|
public const int MAX_PACKET_SIZE = (1024 * 1024) * 1; //1MB
|
|
|
|
|
private Socket _handle;
|
|
|
|
|
private int _typeIndex = 0;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
private byte[] Buffer = new byte[MAX_PACKET_SIZE];
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
//receive info
|
|
|
|
|
private int ReadOffset = 0;
|
|
|
|
|
private int WriteOffset = 0;
|
|
|
|
|
private int ReadableDataLen = 0;
|
|
|
|
|
private int TotalReceived = 0;
|
|
|
|
|
private int PayloadLen = 0;
|
|
|
|
|
private ReceiveType ReceiveState = ReceiveType.Header;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
//Connection info
|
|
|
|
|
public bool Connected { get; private set; }
|
|
|
|
|
private List<KeepAlive> _keepAlives;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
public UserState Value { get; set; }
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public IPEndPoint EndPoint { get; set; }
|
|
|
|
|
private Server _parentServer;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
|
|
|
|
private const bool encryptionEnabled = true;
|
|
|
|
|
private const bool compressionEnabled = true;
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public Client(int bufferSize)
|
|
|
|
|
{
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
internal Client(Socket sock, int size, Type[] packets)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
this._handle = sock;
|
|
|
|
|
Initialize();
|
|
|
|
|
|
|
|
|
|
AddTypesToSerializer(typeof(IPacket), packets);
|
|
|
|
|
_handle.BeginReceive(this.Buffer, 0, this.Buffer.Length, SocketFlags.None, AynsReceive, null);
|
|
|
|
|
|
|
|
|
|
if (_handle.Connected)
|
|
|
|
|
{
|
|
|
|
|
Connected = true;
|
|
|
|
|
OnClientState(true);
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal Client(Server server, Socket sock, int size, Type[] packets)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
AddTypesToSerializer(typeof(IPacket), packets);
|
|
|
|
|
_parentServer = server;
|
|
|
|
|
Initialize();
|
|
|
|
|
|
|
|
|
|
_handle = sock;
|
|
|
|
|
|
|
|
|
|
_handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
|
|
|
|
_handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
|
|
|
|
|
_handle.NoDelay = true;
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
_handle.BeginReceive(this.Buffer, 0, this.Buffer.Length, SocketFlags.None, AynsReceive, null);
|
|
|
|
|
EndPoint = (IPEndPoint)_handle.RemoteEndPoint;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
Connected = true;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
Disconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Connect(string host, ushort port)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Disconnect();
|
|
|
|
|
Initialize();
|
|
|
|
|
|
|
|
|
|
_handle = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
|
|
|
_handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
|
|
|
|
|
_handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
|
|
|
|
|
_handle.NoDelay = true;
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
_handle.Connect(host, port);
|
|
|
|
|
|
|
|
|
|
if (_handle.Connected)
|
|
|
|
|
{
|
|
|
|
|
Connected = true;
|
|
|
|
|
_handle.BeginReceive(this.Buffer, 0, this.Buffer.Length, SocketFlags.None, AynsReceive, null);
|
|
|
|
|
OnClientState(true);
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(ex.ToString());
|
2015-03-31 14:35:42 +00:00
|
|
|
|
OnClientFail(ex);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
Disconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Initialize()
|
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
_keepAlives = new List<KeepAlive>();
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
|
|
|
|
AddTypesToSerializer(typeof(IPacket), new Type[]
|
|
|
|
|
{
|
|
|
|
|
typeof(UnknownPacket),
|
|
|
|
|
typeof(KeepAlive),
|
|
|
|
|
typeof(KeepAliveResponse)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
private void AynsReceive(IAsyncResult result)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
int BytesTransferred = -1;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
BytesTransferred = _handle.EndReceive(result);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (BytesTransferred <= 0)
|
|
|
|
|
{
|
|
|
|
|
this.Connected = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
this.Connected = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
_parentServer.BytesReceived += BytesTransferred;
|
|
|
|
|
ReadableDataLen += BytesTransferred;
|
|
|
|
|
bool Process = true;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
while (Process)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (ReceiveState == ReceiveType.Header)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
Process = ReadableDataLen >= HEADER_SIZE;
|
|
|
|
|
if (ReadableDataLen >= HEADER_SIZE)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
PayloadLen = BitConverter.ToInt32(Buffer, ReadOffset);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
TotalReceived = HEADER_SIZE;
|
|
|
|
|
ReadableDataLen -= HEADER_SIZE;
|
|
|
|
|
ReadOffset += HEADER_SIZE;
|
|
|
|
|
ReceiveState = ReceiveType.Payload;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ReceiveState == ReceiveType.Payload)
|
|
|
|
|
{
|
|
|
|
|
Process = ReadableDataLen >= PayloadLen;
|
|
|
|
|
if (ReadableDataLen >= PayloadLen)
|
|
|
|
|
{
|
|
|
|
|
byte[] Payload = new byte[PayloadLen];
|
|
|
|
|
Array.Copy(this.Buffer, ReadOffset, Payload, 0, Payload.Length);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (encryptionEnabled)
|
|
|
|
|
{
|
|
|
|
|
Payload = AES.Decrypt(Payload, Encoding.UTF8.GetBytes(XMLSettings.Password));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (compressionEnabled)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
Payload = new SafeQuickLZ().decompress(Payload, 0, Payload.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using (MemoryStream deserialized = new MemoryStream(Payload))
|
|
|
|
|
{
|
|
|
|
|
IPacket packet = Serializer.DeserializeWithLengthPrefix<IPacket>(deserialized, PrefixStyle.Fixed32);
|
|
|
|
|
|
|
|
|
|
if (packet.GetType() == typeof(KeepAlive))
|
|
|
|
|
new KeepAliveResponse() { TimeSent = ((KeepAlive)packet).TimeSent }.Execute(this);
|
|
|
|
|
else if (packet.GetType() == typeof(KeepAliveResponse))
|
|
|
|
|
{
|
|
|
|
|
HandleKeepAlivePacket((KeepAliveResponse)packet, this);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
ClientRead(this, packet);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
2015-03-31 14:35:42 +00:00
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
TotalReceived = 0;
|
|
|
|
|
ReadOffset += PayloadLen;
|
|
|
|
|
ReadableDataLen -= PayloadLen;
|
|
|
|
|
ReceiveState = ReceiveType.Header;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
int len = ReceiveState == ReceiveType.Header ? HEADER_SIZE : PayloadLen;
|
|
|
|
|
if (ReadOffset + len >= this.Buffer.Length)
|
|
|
|
|
{
|
|
|
|
|
//copy the buffer to the beginning
|
|
|
|
|
Array.Copy(this.Buffer, ReadOffset, this.Buffer, 0, ReadableDataLen);
|
|
|
|
|
WriteOffset = ReadableDataLen;
|
|
|
|
|
ReadOffset = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//payload fits in the buffer from the current offset
|
|
|
|
|
//use BytesTransferred to write at the end of the payload
|
|
|
|
|
//so that the data is not split
|
|
|
|
|
WriteOffset += BytesTransferred;
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (Buffer.Length - WriteOffset > 0)
|
|
|
|
|
{
|
|
|
|
|
_handle.BeginReceive(this.Buffer, WriteOffset, Buffer.Length - WriteOffset, SocketFlags.None, AynsReceive, null);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
//Shoudln't be even possible... very strange
|
2014-07-08 12:58:53 +00:00
|
|
|
|
Disconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
Disconnect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Send<T>(IPacket packet) where T : IPacket
|
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
lock (_handle)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (!Connected)
|
|
|
|
|
return;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream ms = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
Serializer.SerializeWithLengthPrefix<T>(ms, (T)packet, PrefixStyle.Fixed32);
|
|
|
|
|
|
|
|
|
|
byte[] data = ms.ToArray();
|
|
|
|
|
|
|
|
|
|
Send(data);
|
|
|
|
|
OnClientWrite(packet, data.LongLength, data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
2015-03-17 21:28:51 +00:00
|
|
|
|
{ }
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
|
2014-07-08 12:58:53 +00:00
|
|
|
|
private void Send(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
if (!Connected)
|
|
|
|
|
return;
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (compressionEnabled)
|
|
|
|
|
data = new SafeQuickLZ().compress(data, 0, data.Length, 3);
|
|
|
|
|
|
2014-07-08 12:58:53 +00:00
|
|
|
|
if (encryptionEnabled)
|
2014-08-13 20:59:57 +00:00
|
|
|
|
data = AES.Encrypt(data, Encoding.UTF8.GetBytes(XMLSettings.Password));
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
byte[] temp = BitConverter.GetBytes(data.Length);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
byte[] Payload = new byte[data.Length + 4];
|
|
|
|
|
Array.Copy(temp, Payload, temp.Length);
|
|
|
|
|
Array.Copy(data, 0, Payload, 4, data.Length);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
try
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
_handle.Send(Payload);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
Disconnect();
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public void Disconnect()
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
Connected = false;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (_handle != null)
|
|
|
|
|
{
|
|
|
|
|
_handle.Close();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a Type to the serializer so a message can be properly serialized.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="parent">The parent type, i.e.: IPacket</param>
|
|
|
|
|
/// <param name="type">Type to be added</param>
|
|
|
|
|
public void AddTypeToSerializer(Type parent, Type type)
|
|
|
|
|
{
|
|
|
|
|
if (type == null || parent == null)
|
|
|
|
|
throw new ArgumentNullException();
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
bool isAdded = false;
|
|
|
|
|
foreach (SubType subType in RuntimeTypeModel.Default[parent].GetSubtypes())
|
|
|
|
|
if (subType.DerivedType.Type == type)
|
|
|
|
|
isAdded = true;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (!isAdded)
|
|
|
|
|
RuntimeTypeModel.Default[parent].AddSubType(_typeIndex += 1, type);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
public void AddTypesToSerializer(Type parent, params Type[] types)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
foreach (Type type in types)
|
|
|
|
|
AddTypeToSerializer(parent, type);
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-31 14:35:42 +00:00
|
|
|
|
private void HandleKeepAlivePacket(KeepAliveResponse packet, Client client)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
foreach (KeepAlive keepAlive in _keepAlives)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
if (keepAlive.TimeSent == packet.TimeSent && keepAlive.Client == client)
|
2014-07-08 12:58:53 +00:00
|
|
|
|
{
|
2015-03-31 14:35:42 +00:00
|
|
|
|
_keepAlives.Remove(keepAlive);
|
|
|
|
|
break;
|
2014-07-08 12:58:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|