I am developing an asynchronous game server using the asynchronous .Net Socket model (BeginAccept / EndAccept ... etc.)
The problem I am facing is described as follows: When I have only one client connected, the server response time is very fast, but as soon as the second client is connected, the server response time increases too much.
I measured the time from the client by sending a message to the server until it receives a response in both cases. I found that the average time in the case of one client is about 17 ms, and in the case of 2 clients it is about 280 ms.
I really see that: when 2 clients are connected and only one of them is moving (i.e., requesting a service from the server), it is equivalent to the case when only one client is connected (i.e., a quick response). However, when two clients move at the same time (i.e., Requests a service from the server at the same time), their movement becomes very slow (as if the server answered each of them, that is, not simultaneously).
Basically, I do what:
When a client requests permission to move from the server, and the server provides it with a request, the server then passes the new client position to all players. Therefore, if two clients move at the same time, the server ultimately tries to simultaneously transmit to each client a new position for each of them.
Example:
- Client1 requests to move to position (2.2)
- Client2 asks to go to position (5.5).
- Client1 Client2 :
- message1: "Client1 at (2,2)"
- message2: "Client2 at (5,5)"
, , Socket MSDN http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.aspx. ( , )
:
public class NetworkManager
{
private readonly int port = 5000;
private readonly Hashtable connectedClients = new Hashtable();
private readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
private int clientCount;
private readonly Socket mainSocket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private const int ClientDisconnectedErrorCode = 10054;
private static readonly NetworkManager networkManagerInstance = new NetworkManager();
public delegate void NewClientConnected(Object sender, SystemEventArgs e);
public delegate void PositionUpdateMessageRecieved(Object sender, PositionUpdateEventArgs e);
public PositionUpdateMessageRecieved PositionUpdateMessageEvent
{
get;
set;
}
public int ClientCount
{
get
{
return clientCount;
}
}
public static NetworkManager NetworkManagerInstance
{
get
{
return networkManagerInstance;
}
}
private NetworkManager()
{}
public void StartServer()
{
mainSocket.Bind(new IPEndPoint(IPAddress.Any, port));
mainSocket.Listen(1024);
mainSocket.BeginAccept(OnClientConnected, null);
resetEvent.WaitOne();
}
private void OnClientConnected(IAsyncResult asyncResult)
{
Interlocked.Increment(ref clientCount);
ClientInfo newClient = new ClientInfo
{
WorkerSocket = mainSocket.EndAccept(asyncResult),
PlayerId = clientCount
};
connectedClients.Add(newClient.PlayerId, newClient);
if (NewClientEvent != null)
{
NewClientEvent(this, System.EventArgs.Empty);
}
newClient.WorkerSocket.BeginReceive(newClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), newClient);
mainSocket.BeginAccept(OnClientConnected, null);
}
private void WaitForData(IAsyncResult asyncResult)
{
ClientInfo sendingClient = null;
try
{
sendingClient = asyncResult.AsyncState as ClientInfo;
if (!IsConnected(sendingClient.WorkerSocket))
{
throw new SocketException(ClientDisconnectedErrorCode);
}
sendingClient.WorkerSocket.EndReceive(asyncResult);
FireMessageTypeEvent(sendingClient.ConvertBytesToPacket() as BasePacket);
sendingClient.WorkerSocket.BeginReceive(sendingClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), sendingClient);
}
catch (SocketException e)
{
if (e.ErrorCode == ClientDisconnectedErrorCode)
{
if (sendingClient.WorkerSocket != null)
{
sendingClient.WorkerSocket.Close();
sendingClient.WorkerSocket = null;
}
connectedClients.Remove(sendingClient.PlayerId);
if (ClientDisconnectedEvent != null)
{
ClientDisconnectedEvent(this, new ClientDisconnectedEventArgs(sendingClient.PlayerId));
}
}
}
catch (Exception e)
{
sendingClient.WorkerSocket.BeginReceive(sendingClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), sendingClient);
}
}
public void BroadcastMessage(BasePacket message)
{
byte[] bytes = message.ConvertToBytes();
foreach (ClientInfo client in connectedClients.Values)
{
client.WorkerSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, SendAsync, client);
}
}
public void SendToClient(BasePacket message, int id)
{
byte[] bytes = message.ConvertToBytes();
(connectedClients[id] as ClientInfo).WorkerSocket.BeginSend(bytes, 0, bytes.Length,
SocketFlags.None, SendAsync, connectedClients[id]);
}
private void SendAsync(IAsyncResult asyncResult)
{
ClientInfo currentClient = (ClientInfo)asyncResult.AsyncState;
currentClient.WorkerSocket.EndSend(asyncResult);
}
void FireMessageTypeEvent(BasePacket packet)
{
switch (packet.MessageType)
{
case MessageType.PositionUpdateMessage:
if (PositionUpdateMessageEvent != null)
{
PositionUpdateMessageEvent(this, new PositionUpdateEventArgs(packet as PositionUpdatePacket));
}
break;
}
}
}
, PositionUpdateMessage ( ):
private readonly Hashtable onlinePlayers = new Hashtable();
private GameController()
{
server = new Thread(networkManager.StartServer);
server.Start();
networkManager.PositionUpdateMessageEvent += OnPositionUpdateMessageReceived;
}
private void OnPositionUpdateMessageReceived(object sender, PositionUpdateEventArgs e)
{
Point currentLocation = ((PlayerData)onlinePlayers[e.PositionUpdatePacket.PlayerId]).Position;
Point locationRequested = e.PositionUpdatePacket.Position;
((PlayerData)onlinePlayers[e.PositionUpdatePacket.PlayerId]).Position = locationRequested;
networkManager.BroadcastMessage(new PositionUpdatePacket
{
Position = locationRequested,
PlayerId = e.PositionUpdatePacket.PlayerId
});
}