GameServer SocketListener + PlayerManagement
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
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.
add a comment |
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
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
add a comment |
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
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
c# asynchronous socket serialization server
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
add a comment |
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
add a comment |
1 Answer
1
active
oldest
votes
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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
answered Jun 12 '18 at 2:39
Micah EppsMicah Epps
1012
1012
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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