我正在开发使用。NET的Socket异步模型异步游戏服务器(BeginAccept/EndAccept ...等)。的.Net 3.5异步套接字服务器性能问题
我所面临的问题是这样描述的: 当我只有一个客户端连接时,服务器响应时间非常快,但是一旦第二个客户端连接,服务器响应时间就会增加太多。
我测量了从客户端发送消息到服务器的时间,直到它在两种情况下都得到回复。我发现一个客户的平均时间大约是17ms,而在2个客户的情况下大约是280ms!我真正看到的是:当2个客户端连接并且只有其中一个正在移动(即向服务器请求服务)时,它等同于只有一个客户端连接(即快速响应)的情况。但是,当两个客户端同时移动时(即同时向服务器请求服务),他们的动作变得非常慢(就好像服务器按照顺序而不是同时回应它们中的每一个)。
基本上,我在做什么是:
当客户端请求许可运动从服务器和服务器授予他的请求,服务器然后广播客户端的新位置,所有的球员。因此,如果两个客户端在同一时间移动,服务器最终将同时向两个客户端广播每个客户端的新位置。
EX:
- 客户端1询问去位置(2,2)
- 客户机2请求去位置(5,5)
- 服务器发送到客户端1的每个客户机2 &相同的两个消息:
- MESSAGE1: “客户端1在(2,2)”
- 消息2: “客户机2在(5,5)”
我相信问题来自Socket类是线程安全的,根据MSDN文档http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.aspx。 (不知道这是问题)
下面是该服务器的代码:
///
/// This class is responsible for handling packet receiving and sending
///
public class NetworkManager
{
///
/// An integer to hold the server port number to be used for the connections. Its default value is 5000.
///
private readonly int port = 5000;
///
/// hashtable contain all the clients connected to the server.
/// key: player Id
/// value: socket
///
private readonly Hashtable connectedClients = new Hashtable();
///
/// An event to hold the thread to wait for a new client
///
private readonly ManualResetEvent resetEvent = new ManualResetEvent(false);
///
/// keeps track of the number of the connected clients
///
private int clientCount;
///
/// The socket of the server at which the clients connect
///
private readonly Socket mainSocket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
///
/// The socket exception that informs that a client is disconnected
///
private const int ClientDisconnectedErrorCode = 10054;
///
/// The only instance of this class.
///
private static readonly NetworkManager networkManagerInstance = new NetworkManager();
///
/// A delegate for the new client connected event.
///
/// the sender object
/// the event args
public delegate void NewClientConnected(Object sender, SystemEventArgs e);
///
/// A delegate for the position update message reception.
///
/// the sender object
/// the event args
public delegate void PositionUpdateMessageRecieved(Object sender, PositionUpdateEventArgs e);
///
/// The event which fires when a client sends a position message
///
public PositionUpdateMessageRecieved PositionUpdateMessageEvent
{
get;
set;
}
///
/// keeps track of the number of the connected clients
///
public int ClientCount
{
get
{
return clientCount;
}
}
///
/// A getter for this class instance.
///
/// only instance.
public static NetworkManager NetworkManagerInstance
{
get
{
return networkManagerInstance;
}
}
private NetworkManager()
{}
/// Starts the game server and holds this thread alive
///
public void StartServer()
{
//Bind the mainSocket to the server IP address and port
mainSocket.Bind(new IPEndPoint(IPAddress.Any, port));
//The server starts to listen on the binded socket with max connection queue //1024
mainSocket.Listen(1024);
//Start accepting clients asynchronously
mainSocket.BeginAccept(OnClientConnected, null);
//Wait until there is a client wants to connect
resetEvent.WaitOne();
}
///
/// Receives connections of new clients and fire the NewClientConnected event
///
private void OnClientConnected(IAsyncResult asyncResult)
{
Interlocked.Increment(ref clientCount);
ClientInfo newClient = new ClientInfo
{
WorkerSocket = mainSocket.EndAccept(asyncResult),
PlayerId = clientCount
};
//Add the new client to the hashtable and increment the number of clients
connectedClients.Add(newClient.PlayerId, newClient);
//fire the new client event informing that a new client is connected to the server
if (NewClientEvent != null)
{
NewClientEvent(this, System.EventArgs.Empty);
}
newClient.WorkerSocket.BeginReceive(newClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), newClient);
//Start accepting clients asynchronously again
mainSocket.BeginAccept(OnClientConnected, null);
}
/// Waits for the upcoming messages from different clients and fires the proper event according to the packet type.
///
///
private void WaitForData(IAsyncResult asyncResult)
{
ClientInfo sendingClient = null;
try
{
//Take the client information from the asynchronous result resulting from the BeginReceive
sendingClient = asyncResult.AsyncState as ClientInfo;
// If client is disconnected, then throw a socket exception
// with the correct error code.
if (!IsConnected(sendingClient.WorkerSocket))
{
throw new SocketException(ClientDisconnectedErrorCode);
}
//End the pending receive request
sendingClient.WorkerSocket.EndReceive(asyncResult);
//Fire the appropriate event
FireMessageTypeEvent(sendingClient.ConvertBytesToPacket() as BasePacket);
// Begin receiving data from this client
sendingClient.WorkerSocket.BeginReceive(sendingClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), sendingClient);
}
catch (SocketException e)
{
if (e.ErrorCode == ClientDisconnectedErrorCode)
{
// Close the socket.
if (sendingClient.WorkerSocket != null)
{
sendingClient.WorkerSocket.Close();
sendingClient.WorkerSocket = null;
}
// Remove it from the hash table.
connectedClients.Remove(sendingClient.PlayerId);
if (ClientDisconnectedEvent != null)
{
ClientDisconnectedEvent(this, new ClientDisconnectedEventArgs(sendingClient.PlayerId));
}
}
}
catch (Exception e)
{
// Begin receiving data from this client
sendingClient.WorkerSocket.BeginReceive(sendingClient.Buffer, 0, BasePacket.GetMaxPacketSize(),
SocketFlags.None, new AsyncCallback(WaitForData), sendingClient);
}
}
///
/// Broadcasts the input message to all the connected clients
///
///
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);
}
}
///
/// Sends the input message to the client specified by his ID.
///
///
/// The message to be sent.
/// The id of the client to receive the message.
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);
}
/// Fires the event depending on the type of received packet
///
/// The received packet.
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();
///
/// Constructor that creates a new instance of the GameController class.
///
private GameController()
{
//Start the server
server = new Thread(networkManager.StartServer);
server.Start();
//Create an event handler for the NewClientEvent of networkManager
networkManager.PositionUpdateMessageEvent += OnPositionUpdateMessageReceived;
}
///
/// this event handler is called when a client asks for movement.
///
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;
// Broadcast the new position
networkManager.BroadcastMessage(new PositionUpdatePacket
{
Position = locationRequested,
PlayerId = e.PositionUpdatePacket.PlayerId
});
}
看起来你可能只能处理你在单线程上收到的数据包。 – sblom 2010-05-23 20:22:40
我还没有看过代码,但有几个问题?客户是否忽视他们自己的移动信息 - 他们应该忽略他们?你多久发一次广播消息? – 2010-05-23 20:31:19
@ sblom-我不相信,事件是从回调函数“WaitForData”触发的。我假设这个回调函数在数据包同时到达服务器时在另一个线程中执行 – iBrAaAa 2010-05-23 20:51:01