GameServer SocketListener + PlayerManagement












4














Today I was posting a question to get a bit of information on how to optimize my code for maximum performance. I'm a beginner to C# (in a sense) and I don't really know as much as all of you other developers on this site.



I'm looking for just any improvements that you think I should make to write better quality code for higher performance, I'm not saying the performance is bad but what I have mostly learnt is there is always room for improved with code.



So, what I am posting is a Socket Listener class which listens for players connecting to the game server. When they have connected the player gets a new instance of 'PlayerConnection' with their socket and added to the PlayerManager's dictionary of players.



The purpose for this is each player will send and receive data to the server. We do it in packets and each packet has a unique identifier.



As of now I have only coded the receiving from Player side of things, I am yet to code sending data to the Player. You'll see I have coded something to do with sending because I have to first of all send a crossdomain policy but I haven't implemented a proper system for sending packets to the player from the server yet.



I've included a fair amount of detail, the rest is pretty much self explanatory or I've added comments in certain places.



ServerListener:



public class SocketListener : IDisposable, IPandaClass
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _listener;

public SocketListener()
{
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}

public void Load()
{
var panda = Program.GetPandaServer();
var config = panda.GetHandler("configuration") as ConfigurationData;

if (config == null)
{
return;
}

var socketPort = config.GetValueByKeyInt("game.socket.port");
var socketBacklog = config.GetValueByKeyInt("game.socket.backlog");

_listener.Bind(new IPEndPoint(IPAddress.Any, socketPort));
_listener.Listen(socketBacklog);
_listener.BeginAccept(OnAccept, _listener);
}

private void OnAccept(IAsyncResult asyncResult)
{
try
{
if (_listener == null)
{
return;
}

var server = (Socket)asyncResult.AsyncState;
var client = server.EndAccept(asyncResult);

var player = new PlayerConnection(client);

var panda = Program.GetPandaServer();
var pandaBase = panda.GetHandler("base") as BaseHandler;

if (pandaBase == null)
{
return;
}

var playerHandler = pandaBase.GetHandler("player") as PlayerHandler;

if (playerHandler != null && !playerHandler.TryAddPlayer(player))
{
Logger.Error($"Failed to register players connection on: {client.RemoteEndPoint}");
}
}
catch (SocketException socketException)
{
Logger.Error(socketException, $"Failed to accept socket connection: {socketException.Message}");
}
finally
{
_listener?.BeginAccept(OnAccept, _listener);
}
}

public void Dispose()
{
_listener.Shutdown(SocketShutdown.Both);
_listener = null;
}


PlayerManager: There will be many more methods like GetPlayer DisconnectPlayer MassPacket and more once finished.



internal class PlayerHandler : IPandaClass
{
private readonly ConcurrentDictionary<int, PlayerConnection> _players;

public PlayerHandler()
{
_players = new ConcurrentDictionary<int, PlayerConnection>();
}

public bool TryAddPlayer(PlayerConnection player)
{
return _players.TryAdd(_players.Count, player);
}
}


PlayerConnection:



public sealed class PlayerConnection : PlayerData, IDisposable
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _playerSocket;
private readonly byte _buffer;

private bool _halfDataReceived;
private byte _halfData;
public bool _decrypted;

public PlayerConnection(Socket socket)
{
_playerSocket = socket;
_buffer = new byte[8192];

try
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
catch (Exception exception)
{
Logger.Error(exception, "Failed to start receiving on players socket.");
}
}

private void OnReceivedData(IAsyncResult asyncResult)
{
try
{
var bytesReceived = _playerSocket.EndReceive(asyncResult);

if (bytesReceived == 0)
{
Dispose();
return;
}

var packet = new byte[bytesReceived];
Array.Copy(_buffer, packet, bytesReceived);
ProcessReceivedData(packet);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Failed while receiving data from players socket.");
}
finally
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
}

private void ProcessReceivedData(byte receivedBytes)
{
if (!_decrypted)
{
// todo: decrypt here
_decrypted = true;
}

var packetId = receivedBytes[0];

if (packetId == 67)
{
return;
}

if (_halfDataReceived)
{
var fullDataRcv = new byte[_halfData.Length + receivedBytes.Length];

Buffer.BlockCopy(_halfData, 0, fullDataRcv, 0, _halfData.Length);
Buffer.BlockCopy(receivedBytes, 0, fullDataRcv, _halfData.Length, receivedBytes.Length);

_halfDataReceived = false;

ProcessReceivedData(fullDataRcv); // todo: repeat now we have the combined array
return;
}

if (packetId == 60)
{
const string crossDomainPolicy = "<?xml version="1.0"?>rn"
+ "<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">rn"
+ "<cross-domain-policy>rn"
+ "<site-control permitted-cross-domain-policies="master-only"/>rn"
+ "<allow-access-from domain="*" to-ports="*" />rn"
+ "</cross-domain-policy>x0";

SendString(crossDomainPolicy);
}
else
{
using (var reader = new BinaryReader(new MemoryStream(receivedBytes)))
{
if (receivedBytes.Length < 4)
{
return;
}

var packetLength = PacketUtilities.DecodeInt32(reader.ReadBytes(4));

if (reader.BaseStream.Length - 4 < packetLength)
{
_halfData = receivedBytes;
_halfDataReceived = true;
return;
}

if (packetLength < 0 || packetLength > 5120)
{
return;
}

var packetBytes = reader.ReadBytes(packetLength);

using (var binaryReader = new BinaryReader(new MemoryStream(packetBytes)))
{
var packetHeader = PacketUtilities.DecodeInt16(binaryReader.ReadBytes(2));
var packetBodyBytes = new byte[packetBytes.Length - 2];

Buffer.BlockCopy(packetBytes, 2, packetBodyBytes, 0, packetBytes.Length - 2);

// now we handle the packet: packetHeader and packetBodyBytes

_decrypted = false;
}

if (reader.BaseStream.Length - 4 <= packetLength)
{
return;
}

var extra = new byte[reader.BaseStream.Length - reader.BaseStream.Position];
Buffer.BlockCopy(receivedBytes, (int)reader.BaseStream.Position, extra, 0, (int)(reader.BaseStream.Length - reader.BaseStream.Position));

_decrypted = true;

ProcessReceivedData(extra);
}
}
}

private void SendString(string data)
{
SendData(Encoding.UTF8.GetBytes(data));
}

private void SendData(byte data)
{
try
{
_playerSocket.BeginSend(data, 0, data.Length, 0, OnSend, null);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Error sending message to players socket.");
Dispose();
}
}

private void OnSend(IAsyncResult asyncResult)
{
try
{
if (_playerSocket == null)
{
return;
}

_playerSocket.EndSend(asyncResult);
}
catch (SocketException)
{
Dispose();
}
}

public void Dispose()
{
if (_playerSocket == null || !_playerSocket.Connected)
{
return;
}

_playerSocket.Shutdown(SocketShutdown.Both);
_playerSocket.Close();
}
}


I'll most the other methods I used (Utility methods) below also.



PacketUtilities:



internal static int DecodeInt32(byte bytes)
{
if ((bytes[0] | bytes[1] | bytes[2] | bytes[3]) < 0)
{
return -1;
}

return (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
}

internal static int DecodeInt16(byte bytes)
{
if ((bytes[0] | bytes[1]) < 0)
{
return -1;
}

return (bytes[0] << 8) + bytes[1];
}









share|improve this question
















bumped to the homepage by Community 23 hours ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.











  • 1




    If that is a question you posted, you should consider requesting a moderator to merge your accounts into one.
    – Denis
    Jun 13 '17 at 23:37
















4














Today I was posting a question to get a bit of information on how to optimize my code for maximum performance. I'm a beginner to C# (in a sense) and I don't really know as much as all of you other developers on this site.



I'm looking for just any improvements that you think I should make to write better quality code for higher performance, I'm not saying the performance is bad but what I have mostly learnt is there is always room for improved with code.



So, what I am posting is a Socket Listener class which listens for players connecting to the game server. When they have connected the player gets a new instance of 'PlayerConnection' with their socket and added to the PlayerManager's dictionary of players.



The purpose for this is each player will send and receive data to the server. We do it in packets and each packet has a unique identifier.



As of now I have only coded the receiving from Player side of things, I am yet to code sending data to the Player. You'll see I have coded something to do with sending because I have to first of all send a crossdomain policy but I haven't implemented a proper system for sending packets to the player from the server yet.



I've included a fair amount of detail, the rest is pretty much self explanatory or I've added comments in certain places.



ServerListener:



public class SocketListener : IDisposable, IPandaClass
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _listener;

public SocketListener()
{
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}

public void Load()
{
var panda = Program.GetPandaServer();
var config = panda.GetHandler("configuration") as ConfigurationData;

if (config == null)
{
return;
}

var socketPort = config.GetValueByKeyInt("game.socket.port");
var socketBacklog = config.GetValueByKeyInt("game.socket.backlog");

_listener.Bind(new IPEndPoint(IPAddress.Any, socketPort));
_listener.Listen(socketBacklog);
_listener.BeginAccept(OnAccept, _listener);
}

private void OnAccept(IAsyncResult asyncResult)
{
try
{
if (_listener == null)
{
return;
}

var server = (Socket)asyncResult.AsyncState;
var client = server.EndAccept(asyncResult);

var player = new PlayerConnection(client);

var panda = Program.GetPandaServer();
var pandaBase = panda.GetHandler("base") as BaseHandler;

if (pandaBase == null)
{
return;
}

var playerHandler = pandaBase.GetHandler("player") as PlayerHandler;

if (playerHandler != null && !playerHandler.TryAddPlayer(player))
{
Logger.Error($"Failed to register players connection on: {client.RemoteEndPoint}");
}
}
catch (SocketException socketException)
{
Logger.Error(socketException, $"Failed to accept socket connection: {socketException.Message}");
}
finally
{
_listener?.BeginAccept(OnAccept, _listener);
}
}

public void Dispose()
{
_listener.Shutdown(SocketShutdown.Both);
_listener = null;
}


PlayerManager: There will be many more methods like GetPlayer DisconnectPlayer MassPacket and more once finished.



internal class PlayerHandler : IPandaClass
{
private readonly ConcurrentDictionary<int, PlayerConnection> _players;

public PlayerHandler()
{
_players = new ConcurrentDictionary<int, PlayerConnection>();
}

public bool TryAddPlayer(PlayerConnection player)
{
return _players.TryAdd(_players.Count, player);
}
}


PlayerConnection:



public sealed class PlayerConnection : PlayerData, IDisposable
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _playerSocket;
private readonly byte _buffer;

private bool _halfDataReceived;
private byte _halfData;
public bool _decrypted;

public PlayerConnection(Socket socket)
{
_playerSocket = socket;
_buffer = new byte[8192];

try
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
catch (Exception exception)
{
Logger.Error(exception, "Failed to start receiving on players socket.");
}
}

private void OnReceivedData(IAsyncResult asyncResult)
{
try
{
var bytesReceived = _playerSocket.EndReceive(asyncResult);

if (bytesReceived == 0)
{
Dispose();
return;
}

var packet = new byte[bytesReceived];
Array.Copy(_buffer, packet, bytesReceived);
ProcessReceivedData(packet);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Failed while receiving data from players socket.");
}
finally
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
}

private void ProcessReceivedData(byte receivedBytes)
{
if (!_decrypted)
{
// todo: decrypt here
_decrypted = true;
}

var packetId = receivedBytes[0];

if (packetId == 67)
{
return;
}

if (_halfDataReceived)
{
var fullDataRcv = new byte[_halfData.Length + receivedBytes.Length];

Buffer.BlockCopy(_halfData, 0, fullDataRcv, 0, _halfData.Length);
Buffer.BlockCopy(receivedBytes, 0, fullDataRcv, _halfData.Length, receivedBytes.Length);

_halfDataReceived = false;

ProcessReceivedData(fullDataRcv); // todo: repeat now we have the combined array
return;
}

if (packetId == 60)
{
const string crossDomainPolicy = "<?xml version="1.0"?>rn"
+ "<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">rn"
+ "<cross-domain-policy>rn"
+ "<site-control permitted-cross-domain-policies="master-only"/>rn"
+ "<allow-access-from domain="*" to-ports="*" />rn"
+ "</cross-domain-policy>x0";

SendString(crossDomainPolicy);
}
else
{
using (var reader = new BinaryReader(new MemoryStream(receivedBytes)))
{
if (receivedBytes.Length < 4)
{
return;
}

var packetLength = PacketUtilities.DecodeInt32(reader.ReadBytes(4));

if (reader.BaseStream.Length - 4 < packetLength)
{
_halfData = receivedBytes;
_halfDataReceived = true;
return;
}

if (packetLength < 0 || packetLength > 5120)
{
return;
}

var packetBytes = reader.ReadBytes(packetLength);

using (var binaryReader = new BinaryReader(new MemoryStream(packetBytes)))
{
var packetHeader = PacketUtilities.DecodeInt16(binaryReader.ReadBytes(2));
var packetBodyBytes = new byte[packetBytes.Length - 2];

Buffer.BlockCopy(packetBytes, 2, packetBodyBytes, 0, packetBytes.Length - 2);

// now we handle the packet: packetHeader and packetBodyBytes

_decrypted = false;
}

if (reader.BaseStream.Length - 4 <= packetLength)
{
return;
}

var extra = new byte[reader.BaseStream.Length - reader.BaseStream.Position];
Buffer.BlockCopy(receivedBytes, (int)reader.BaseStream.Position, extra, 0, (int)(reader.BaseStream.Length - reader.BaseStream.Position));

_decrypted = true;

ProcessReceivedData(extra);
}
}
}

private void SendString(string data)
{
SendData(Encoding.UTF8.GetBytes(data));
}

private void SendData(byte data)
{
try
{
_playerSocket.BeginSend(data, 0, data.Length, 0, OnSend, null);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Error sending message to players socket.");
Dispose();
}
}

private void OnSend(IAsyncResult asyncResult)
{
try
{
if (_playerSocket == null)
{
return;
}

_playerSocket.EndSend(asyncResult);
}
catch (SocketException)
{
Dispose();
}
}

public void Dispose()
{
if (_playerSocket == null || !_playerSocket.Connected)
{
return;
}

_playerSocket.Shutdown(SocketShutdown.Both);
_playerSocket.Close();
}
}


I'll most the other methods I used (Utility methods) below also.



PacketUtilities:



internal static int DecodeInt32(byte bytes)
{
if ((bytes[0] | bytes[1] | bytes[2] | bytes[3]) < 0)
{
return -1;
}

return (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
}

internal static int DecodeInt16(byte bytes)
{
if ((bytes[0] | bytes[1]) < 0)
{
return -1;
}

return (bytes[0] << 8) + bytes[1];
}









share|improve this question
















bumped to the homepage by Community 23 hours ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.











  • 1




    If that is a question you posted, you should consider requesting a moderator to merge your accounts into one.
    – Denis
    Jun 13 '17 at 23:37














4












4








4


1





Today I was posting a question to get a bit of information on how to optimize my code for maximum performance. I'm a beginner to C# (in a sense) and I don't really know as much as all of you other developers on this site.



I'm looking for just any improvements that you think I should make to write better quality code for higher performance, I'm not saying the performance is bad but what I have mostly learnt is there is always room for improved with code.



So, what I am posting is a Socket Listener class which listens for players connecting to the game server. When they have connected the player gets a new instance of 'PlayerConnection' with their socket and added to the PlayerManager's dictionary of players.



The purpose for this is each player will send and receive data to the server. We do it in packets and each packet has a unique identifier.



As of now I have only coded the receiving from Player side of things, I am yet to code sending data to the Player. You'll see I have coded something to do with sending because I have to first of all send a crossdomain policy but I haven't implemented a proper system for sending packets to the player from the server yet.



I've included a fair amount of detail, the rest is pretty much self explanatory or I've added comments in certain places.



ServerListener:



public class SocketListener : IDisposable, IPandaClass
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _listener;

public SocketListener()
{
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}

public void Load()
{
var panda = Program.GetPandaServer();
var config = panda.GetHandler("configuration") as ConfigurationData;

if (config == null)
{
return;
}

var socketPort = config.GetValueByKeyInt("game.socket.port");
var socketBacklog = config.GetValueByKeyInt("game.socket.backlog");

_listener.Bind(new IPEndPoint(IPAddress.Any, socketPort));
_listener.Listen(socketBacklog);
_listener.BeginAccept(OnAccept, _listener);
}

private void OnAccept(IAsyncResult asyncResult)
{
try
{
if (_listener == null)
{
return;
}

var server = (Socket)asyncResult.AsyncState;
var client = server.EndAccept(asyncResult);

var player = new PlayerConnection(client);

var panda = Program.GetPandaServer();
var pandaBase = panda.GetHandler("base") as BaseHandler;

if (pandaBase == null)
{
return;
}

var playerHandler = pandaBase.GetHandler("player") as PlayerHandler;

if (playerHandler != null && !playerHandler.TryAddPlayer(player))
{
Logger.Error($"Failed to register players connection on: {client.RemoteEndPoint}");
}
}
catch (SocketException socketException)
{
Logger.Error(socketException, $"Failed to accept socket connection: {socketException.Message}");
}
finally
{
_listener?.BeginAccept(OnAccept, _listener);
}
}

public void Dispose()
{
_listener.Shutdown(SocketShutdown.Both);
_listener = null;
}


PlayerManager: There will be many more methods like GetPlayer DisconnectPlayer MassPacket and more once finished.



internal class PlayerHandler : IPandaClass
{
private readonly ConcurrentDictionary<int, PlayerConnection> _players;

public PlayerHandler()
{
_players = new ConcurrentDictionary<int, PlayerConnection>();
}

public bool TryAddPlayer(PlayerConnection player)
{
return _players.TryAdd(_players.Count, player);
}
}


PlayerConnection:



public sealed class PlayerConnection : PlayerData, IDisposable
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _playerSocket;
private readonly byte _buffer;

private bool _halfDataReceived;
private byte _halfData;
public bool _decrypted;

public PlayerConnection(Socket socket)
{
_playerSocket = socket;
_buffer = new byte[8192];

try
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
catch (Exception exception)
{
Logger.Error(exception, "Failed to start receiving on players socket.");
}
}

private void OnReceivedData(IAsyncResult asyncResult)
{
try
{
var bytesReceived = _playerSocket.EndReceive(asyncResult);

if (bytesReceived == 0)
{
Dispose();
return;
}

var packet = new byte[bytesReceived];
Array.Copy(_buffer, packet, bytesReceived);
ProcessReceivedData(packet);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Failed while receiving data from players socket.");
}
finally
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
}

private void ProcessReceivedData(byte receivedBytes)
{
if (!_decrypted)
{
// todo: decrypt here
_decrypted = true;
}

var packetId = receivedBytes[0];

if (packetId == 67)
{
return;
}

if (_halfDataReceived)
{
var fullDataRcv = new byte[_halfData.Length + receivedBytes.Length];

Buffer.BlockCopy(_halfData, 0, fullDataRcv, 0, _halfData.Length);
Buffer.BlockCopy(receivedBytes, 0, fullDataRcv, _halfData.Length, receivedBytes.Length);

_halfDataReceived = false;

ProcessReceivedData(fullDataRcv); // todo: repeat now we have the combined array
return;
}

if (packetId == 60)
{
const string crossDomainPolicy = "<?xml version="1.0"?>rn"
+ "<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">rn"
+ "<cross-domain-policy>rn"
+ "<site-control permitted-cross-domain-policies="master-only"/>rn"
+ "<allow-access-from domain="*" to-ports="*" />rn"
+ "</cross-domain-policy>x0";

SendString(crossDomainPolicy);
}
else
{
using (var reader = new BinaryReader(new MemoryStream(receivedBytes)))
{
if (receivedBytes.Length < 4)
{
return;
}

var packetLength = PacketUtilities.DecodeInt32(reader.ReadBytes(4));

if (reader.BaseStream.Length - 4 < packetLength)
{
_halfData = receivedBytes;
_halfDataReceived = true;
return;
}

if (packetLength < 0 || packetLength > 5120)
{
return;
}

var packetBytes = reader.ReadBytes(packetLength);

using (var binaryReader = new BinaryReader(new MemoryStream(packetBytes)))
{
var packetHeader = PacketUtilities.DecodeInt16(binaryReader.ReadBytes(2));
var packetBodyBytes = new byte[packetBytes.Length - 2];

Buffer.BlockCopy(packetBytes, 2, packetBodyBytes, 0, packetBytes.Length - 2);

// now we handle the packet: packetHeader and packetBodyBytes

_decrypted = false;
}

if (reader.BaseStream.Length - 4 <= packetLength)
{
return;
}

var extra = new byte[reader.BaseStream.Length - reader.BaseStream.Position];
Buffer.BlockCopy(receivedBytes, (int)reader.BaseStream.Position, extra, 0, (int)(reader.BaseStream.Length - reader.BaseStream.Position));

_decrypted = true;

ProcessReceivedData(extra);
}
}
}

private void SendString(string data)
{
SendData(Encoding.UTF8.GetBytes(data));
}

private void SendData(byte data)
{
try
{
_playerSocket.BeginSend(data, 0, data.Length, 0, OnSend, null);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Error sending message to players socket.");
Dispose();
}
}

private void OnSend(IAsyncResult asyncResult)
{
try
{
if (_playerSocket == null)
{
return;
}

_playerSocket.EndSend(asyncResult);
}
catch (SocketException)
{
Dispose();
}
}

public void Dispose()
{
if (_playerSocket == null || !_playerSocket.Connected)
{
return;
}

_playerSocket.Shutdown(SocketShutdown.Both);
_playerSocket.Close();
}
}


I'll most the other methods I used (Utility methods) below also.



PacketUtilities:



internal static int DecodeInt32(byte bytes)
{
if ((bytes[0] | bytes[1] | bytes[2] | bytes[3]) < 0)
{
return -1;
}

return (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
}

internal static int DecodeInt16(byte bytes)
{
if ((bytes[0] | bytes[1]) < 0)
{
return -1;
}

return (bytes[0] << 8) + bytes[1];
}









share|improve this question















Today I was posting a question to get a bit of information on how to optimize my code for maximum performance. I'm a beginner to C# (in a sense) and I don't really know as much as all of you other developers on this site.



I'm looking for just any improvements that you think I should make to write better quality code for higher performance, I'm not saying the performance is bad but what I have mostly learnt is there is always room for improved with code.



So, what I am posting is a Socket Listener class which listens for players connecting to the game server. When they have connected the player gets a new instance of 'PlayerConnection' with their socket and added to the PlayerManager's dictionary of players.



The purpose for this is each player will send and receive data to the server. We do it in packets and each packet has a unique identifier.



As of now I have only coded the receiving from Player side of things, I am yet to code sending data to the Player. You'll see I have coded something to do with sending because I have to first of all send a crossdomain policy but I haven't implemented a proper system for sending packets to the player from the server yet.



I've included a fair amount of detail, the rest is pretty much self explanatory or I've added comments in certain places.



ServerListener:



public class SocketListener : IDisposable, IPandaClass
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _listener;

public SocketListener()
{
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}

public void Load()
{
var panda = Program.GetPandaServer();
var config = panda.GetHandler("configuration") as ConfigurationData;

if (config == null)
{
return;
}

var socketPort = config.GetValueByKeyInt("game.socket.port");
var socketBacklog = config.GetValueByKeyInt("game.socket.backlog");

_listener.Bind(new IPEndPoint(IPAddress.Any, socketPort));
_listener.Listen(socketBacklog);
_listener.BeginAccept(OnAccept, _listener);
}

private void OnAccept(IAsyncResult asyncResult)
{
try
{
if (_listener == null)
{
return;
}

var server = (Socket)asyncResult.AsyncState;
var client = server.EndAccept(asyncResult);

var player = new PlayerConnection(client);

var panda = Program.GetPandaServer();
var pandaBase = panda.GetHandler("base") as BaseHandler;

if (pandaBase == null)
{
return;
}

var playerHandler = pandaBase.GetHandler("player") as PlayerHandler;

if (playerHandler != null && !playerHandler.TryAddPlayer(player))
{
Logger.Error($"Failed to register players connection on: {client.RemoteEndPoint}");
}
}
catch (SocketException socketException)
{
Logger.Error(socketException, $"Failed to accept socket connection: {socketException.Message}");
}
finally
{
_listener?.BeginAccept(OnAccept, _listener);
}
}

public void Dispose()
{
_listener.Shutdown(SocketShutdown.Both);
_listener = null;
}


PlayerManager: There will be many more methods like GetPlayer DisconnectPlayer MassPacket and more once finished.



internal class PlayerHandler : IPandaClass
{
private readonly ConcurrentDictionary<int, PlayerConnection> _players;

public PlayerHandler()
{
_players = new ConcurrentDictionary<int, PlayerConnection>();
}

public bool TryAddPlayer(PlayerConnection player)
{
return _players.TryAdd(_players.Count, player);
}
}


PlayerConnection:



public sealed class PlayerConnection : PlayerData, IDisposable
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

private readonly Socket _playerSocket;
private readonly byte _buffer;

private bool _halfDataReceived;
private byte _halfData;
public bool _decrypted;

public PlayerConnection(Socket socket)
{
_playerSocket = socket;
_buffer = new byte[8192];

try
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
catch (Exception exception)
{
Logger.Error(exception, "Failed to start receiving on players socket.");
}
}

private void OnReceivedData(IAsyncResult asyncResult)
{
try
{
var bytesReceived = _playerSocket.EndReceive(asyncResult);

if (bytesReceived == 0)
{
Dispose();
return;
}

var packet = new byte[bytesReceived];
Array.Copy(_buffer, packet, bytesReceived);
ProcessReceivedData(packet);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Failed while receiving data from players socket.");
}
finally
{
_playerSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnReceivedData, _playerSocket);
}
}

private void ProcessReceivedData(byte receivedBytes)
{
if (!_decrypted)
{
// todo: decrypt here
_decrypted = true;
}

var packetId = receivedBytes[0];

if (packetId == 67)
{
return;
}

if (_halfDataReceived)
{
var fullDataRcv = new byte[_halfData.Length + receivedBytes.Length];

Buffer.BlockCopy(_halfData, 0, fullDataRcv, 0, _halfData.Length);
Buffer.BlockCopy(receivedBytes, 0, fullDataRcv, _halfData.Length, receivedBytes.Length);

_halfDataReceived = false;

ProcessReceivedData(fullDataRcv); // todo: repeat now we have the combined array
return;
}

if (packetId == 60)
{
const string crossDomainPolicy = "<?xml version="1.0"?>rn"
+ "<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">rn"
+ "<cross-domain-policy>rn"
+ "<site-control permitted-cross-domain-policies="master-only"/>rn"
+ "<allow-access-from domain="*" to-ports="*" />rn"
+ "</cross-domain-policy>x0";

SendString(crossDomainPolicy);
}
else
{
using (var reader = new BinaryReader(new MemoryStream(receivedBytes)))
{
if (receivedBytes.Length < 4)
{
return;
}

var packetLength = PacketUtilities.DecodeInt32(reader.ReadBytes(4));

if (reader.BaseStream.Length - 4 < packetLength)
{
_halfData = receivedBytes;
_halfDataReceived = true;
return;
}

if (packetLength < 0 || packetLength > 5120)
{
return;
}

var packetBytes = reader.ReadBytes(packetLength);

using (var binaryReader = new BinaryReader(new MemoryStream(packetBytes)))
{
var packetHeader = PacketUtilities.DecodeInt16(binaryReader.ReadBytes(2));
var packetBodyBytes = new byte[packetBytes.Length - 2];

Buffer.BlockCopy(packetBytes, 2, packetBodyBytes, 0, packetBytes.Length - 2);

// now we handle the packet: packetHeader and packetBodyBytes

_decrypted = false;
}

if (reader.BaseStream.Length - 4 <= packetLength)
{
return;
}

var extra = new byte[reader.BaseStream.Length - reader.BaseStream.Position];
Buffer.BlockCopy(receivedBytes, (int)reader.BaseStream.Position, extra, 0, (int)(reader.BaseStream.Length - reader.BaseStream.Position));

_decrypted = true;

ProcessReceivedData(extra);
}
}
}

private void SendString(string data)
{
SendData(Encoding.UTF8.GetBytes(data));
}

private void SendData(byte data)
{
try
{
_playerSocket.BeginSend(data, 0, data.Length, 0, OnSend, null);
}
catch (SocketException socketException)
{
Logger.Error(socketException, "Error sending message to players socket.");
Dispose();
}
}

private void OnSend(IAsyncResult asyncResult)
{
try
{
if (_playerSocket == null)
{
return;
}

_playerSocket.EndSend(asyncResult);
}
catch (SocketException)
{
Dispose();
}
}

public void Dispose()
{
if (_playerSocket == null || !_playerSocket.Connected)
{
return;
}

_playerSocket.Shutdown(SocketShutdown.Both);
_playerSocket.Close();
}
}


I'll most the other methods I used (Utility methods) below also.



PacketUtilities:



internal static int DecodeInt32(byte bytes)
{
if ((bytes[0] | bytes[1] | bytes[2] | bytes[3]) < 0)
{
return -1;
}

return (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
}

internal static int DecodeInt16(byte bytes)
{
if ((bytes[0] | bytes[1]) < 0)
{
return -1;
}

return (bytes[0] << 8) + bytes[1];
}






c# asynchronous socket serialization server






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 10 '18 at 5:41









200_success

128k15152414




128k15152414










asked Jun 13 '17 at 22:30









SerioskSeriosk

211




211





bumped to the homepage by Community 23 hours ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







bumped to the homepage by Community 23 hours ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.










  • 1




    If that is a question you posted, you should consider requesting a moderator to merge your accounts into one.
    – Denis
    Jun 13 '17 at 23:37














  • 1




    If that is a question you posted, you should consider requesting a moderator to merge your accounts into one.
    – Denis
    Jun 13 '17 at 23:37








1




1




If that is a question you posted, you should consider requesting a moderator to merge your accounts into one.
– Denis
Jun 13 '17 at 23:37




If that is a question you posted, you should consider requesting a moderator to merge your accounts into one.
– Denis
Jun 13 '17 at 23:37










1 Answer
1






active

oldest

votes


















0














I'd rename OnAccept to something like BeginAccept_callback. You will mainly see the OnSomething naming convention for virtual functions. Case in point- System.Windows.Forms.Control is chock full of virtual functions where the base implementation is to raise an event. Likewise for the other callback handlers.






share|improve this answer





















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f165702%2fgameserver-socketlistener-playermanagement%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    I'd rename OnAccept to something like BeginAccept_callback. You will mainly see the OnSomething naming convention for virtual functions. Case in point- System.Windows.Forms.Control is chock full of virtual functions where the base implementation is to raise an event. Likewise for the other callback handlers.






    share|improve this answer


























      0














      I'd rename OnAccept to something like BeginAccept_callback. You will mainly see the OnSomething naming convention for virtual functions. Case in point- System.Windows.Forms.Control is chock full of virtual functions where the base implementation is to raise an event. Likewise for the other callback handlers.






      share|improve this answer
























        0












        0








        0






        I'd rename OnAccept to something like BeginAccept_callback. You will mainly see the OnSomething naming convention for virtual functions. Case in point- System.Windows.Forms.Control is chock full of virtual functions where the base implementation is to raise an event. Likewise for the other callback handlers.






        share|improve this answer












        I'd rename OnAccept to something like BeginAccept_callback. You will mainly see the OnSomething naming convention for virtual functions. Case in point- System.Windows.Forms.Control is chock full of virtual functions where the base implementation is to raise an event. Likewise for the other callback handlers.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jun 12 '18 at 2:39









        Micah EppsMicah Epps

        1012




        1012






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f165702%2fgameserver-socketlistener-playermanagement%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

            is 'sed' thread safe

            How to make a Squid Proxy server?