Quasar/Server/Core/Client.cs

316 lines
9.7 KiB
C#
Raw Normal View History

2015-01-13 18:29:11 +00:00
using System;
2014-07-30 15:04:21 +00:00
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
2015-01-13 18:29:11 +00:00
using ProtoBuf;
using ProtoBuf.Meta;
using xServer.Core.Compression;
using xServer.Core.Encryption;
using xServer.Core.Extensions;
2015-01-13 18:29:11 +00:00
using xServer.Core.Packets;
using xServer.Settings;
namespace xServer.Core
2014-07-08 12:58:53 +00:00
{
public class Client
{
public event ClientStateEventHandler ClientState;
public delegate void ClientStateEventHandler(Client s, bool connected);
private void OnClientState(bool connected)
{
2015-03-31 16:34:29 +00:00
if (Connected == connected) return;
2015-03-31 16:15:48 +00:00
Connected = connected;
2014-07-08 12:58:53 +00:00
if (ClientState != null)
{
ClientState(this, connected);
}
}
public event ClientReadEventHandler ClientRead;
public delegate void ClientReadEventHandler(Client s, IPacket packet);
2015-03-31 16:15:48 +00:00
private void OnClientRead(IPacket packet)
2014-07-08 12:58:53 +00:00
{
if (ClientRead != null)
{
2015-03-31 16:15:48 +00:00
ClientRead(this, packet);
2014-07-08 12:58:53 +00:00
}
}
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);
}
}
public enum ReceiveType
{
Header,
Payload
}
2014-07-08 12:58:53 +00:00
2015-04-06 18:49:34 +00:00
public const uint KEEP_ALIVE_TIME = 25000;
public const uint KEEP_ALIVE_INTERVAL = 25000;
2015-04-05 10:41:26 +00:00
public const int HEADER_SIZE = 4;
public const int MAX_PACKET_SIZE = (1024 * 1024) * 1; //1MB
private Socket _handle;
2015-03-31 16:15:48 +00:00
private int _typeIndex;
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
private byte[] _buffer = new byte[MAX_PACKET_SIZE];
2014-07-08 12:58:53 +00:00
//receive info
2015-03-31 16:15:48 +00:00
private int _readOffset;
private int _writeOffset;
private int _readableDataLen;
private int _payloadLen;
private ReceiveType _receiveState = ReceiveType.Header;
2014-07-08 12:58:53 +00:00
//Connection info
public bool Connected { get; private set; }
2014-07-08 12:58:53 +00:00
public UserState Value { get; set; }
2015-03-31 16:15:48 +00:00
public IPEndPoint EndPoint { get; private 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 16:15:48 +00:00
public Client()
{ }
2015-03-31 16:15:48 +00:00
internal Client(Server server, Socket sock, Type[] packets)
2014-07-08 12:58:53 +00:00
{
try
{
AddTypesToSerializer(typeof(IPacket), packets);
_parentServer = server;
Initialize();
_handle = sock;
SocketExtensions.SetKeepAliveEx(_handle, KEEP_ALIVE_INTERVAL, KEEP_ALIVE_TIME);
2015-04-05 10:41:26 +00:00
2014-07-08 12:58:53 +00:00
_handle.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
_handle.NoDelay = true;
2015-03-31 16:15:48 +00:00
_handle.BeginReceive(this._buffer, 0, this._buffer.Length, SocketFlags.None, AsyncReceive, null);
EndPoint = (IPEndPoint)_handle.RemoteEndPoint;
2015-03-31 16:15:48 +00:00
OnClientState(true);
2014-07-08 12:58:53 +00:00
}
catch
{
Disconnect();
}
}
private void Initialize()
{
2015-04-06 18:49:34 +00:00
AddTypeToSerializer(typeof(IPacket), typeof(UnknownPacket));
2014-07-08 12:58:53 +00:00
}
2015-03-31 16:15:48 +00:00
private void AsyncReceive(IAsyncResult result)
2014-07-08 12:58:53 +00:00
{
2015-03-31 16:15:48 +00:00
int bytesTransferred = -1;
try
{
2015-03-31 16:15:48 +00:00
bytesTransferred = _handle.EndReceive(result);
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
if (bytesTransferred <= 0)
{
2015-03-31 16:15:48 +00:00
OnClientState(false);
return;
}
}
catch (Exception ex)
{
2015-03-31 16:15:48 +00:00
OnClientState(false);
return;
}
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
_parentServer.BytesReceived += bytesTransferred;
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
_readableDataLen += bytesTransferred;
bool process = true;
while (process)
2014-07-08 12:58:53 +00:00
{
2015-03-31 16:15:48 +00:00
if (_receiveState == ReceiveType.Header)
2014-07-08 12:58:53 +00:00
{
2015-03-31 16:15:48 +00:00
process = _readableDataLen >= HEADER_SIZE;
2015-04-03 08:18:52 +00:00
if (process)
2014-07-08 12:58:53 +00:00
{
2015-03-31 16:15:48 +00:00
_payloadLen = BitConverter.ToInt32(_buffer, _readOffset);
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
_readableDataLen -= HEADER_SIZE;
_readOffset += HEADER_SIZE;
_receiveState = ReceiveType.Payload;
}
}
2015-03-31 16:15:48 +00:00
else if (_receiveState == ReceiveType.Payload)
{
2015-03-31 16:15:48 +00:00
process = _readableDataLen >= _payloadLen;
2015-04-03 08:18:52 +00:00
if (process)
{
2015-03-31 16:15:48 +00:00
byte[] payload = new byte[_payloadLen];
Array.Copy(this._buffer, _readOffset, payload, 0, payload.Length);
2014-07-08 12:58:53 +00:00
if (encryptionEnabled)
2015-03-31 16:15:48 +00:00
payload = AES.Decrypt(payload, Encoding.UTF8.GetBytes(XMLSettings.Password));
2015-04-03 08:18:52 +00:00
if (payload.Length > 0)
{
2015-04-03 08:18:52 +00:00
if (compressionEnabled)
payload = new SafeQuickLZ().Decompress(payload, 0, payload.Length);
using (MemoryStream deserialized = new MemoryStream(payload))
{
IPacket packet = Serializer.DeserializeWithLengthPrefix<IPacket>(deserialized,
PrefixStyle.Fixed32);
OnClientRead(packet);
2015-04-03 08:18:52 +00:00
}
}
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
_readOffset += _payloadLen;
_readableDataLen -= _payloadLen;
_receiveState = ReceiveType.Header;
}
}
}
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
int len = _receiveState == ReceiveType.Header ? HEADER_SIZE : _payloadLen;
if (_readOffset + len >= this._buffer.Length)
{
//copy the buffer to the beginning
2015-03-31 16:15:48 +00:00
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
2015-03-31 16:15:48 +00:00
_writeOffset += bytesTransferred;
}
2014-07-08 12:58:53 +00:00
try
{
2015-03-31 16:15:48 +00:00
if (_buffer.Length - _writeOffset > 0)
{
2015-03-31 16:15:48 +00:00
_handle.BeginReceive(this._buffer, _writeOffset, _buffer.Length - _writeOffset, SocketFlags.None, AsyncReceive, null);
2014-07-08 12:58:53 +00:00
}
else
{
//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
{
lock (_handle)
2014-07-08 12:58:53 +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
}
}
private void Send(byte[] data)
{
if (!Connected)
return;
if (compressionEnabled)
2015-03-31 16:15:48 +00:00
data = new SafeQuickLZ().Compress(data, 0, data.Length, 3);
2014-07-08 12:58:53 +00:00
if (encryptionEnabled)
data = AES.Encrypt(data, Encoding.UTF8.GetBytes(XMLSettings.Password));
2014-07-08 12:58:53 +00:00
byte[] temp = BitConverter.GetBytes(data.Length);
2014-07-08 12:58:53 +00:00
2015-03-31 16:15:48 +00:00
byte[] payload = new byte[data.Length + 4];
Array.Copy(temp, payload, temp.Length);
Array.Copy(data, 0, payload, 4, data.Length);
_parentServer.BytesSent += payload.Length;
2014-07-08 12:58:53 +00:00
try
2014-07-08 12:58:53 +00:00
{
2015-03-31 16:15:48 +00:00
_handle.Send(payload);
}
catch
{
Disconnect();
2014-07-08 12:58:53 +00:00
}
}
public void Disconnect()
2014-07-08 12:58:53 +00:00
{
2015-03-31 16:15:48 +00:00
OnClientState(false);
2014-07-08 12:58:53 +00:00
if (_handle != null)
{
_handle.Close();
2015-03-31 16:34:29 +00:00
_readOffset = 0;
_writeOffset = 0;
2015-03-31 16:45:31 +00:00
_readableDataLen = 0;
_payloadLen = 0;
}
}
2014-07-08 12:58:53 +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
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
if (!isAdded)
RuntimeTypeModel.Default[parent].AddSubType(_typeIndex += 1, type);
2014-07-08 12:58:53 +00:00
}
public void AddTypesToSerializer(Type parent, params Type[] types)
2014-07-08 12:58:53 +00:00
{
foreach (Type type in types)
AddTypeToSerializer(parent, type);
2014-07-08 12:58:53 +00:00
}
}
}