请注意,编写异步服务器比重写 MSDN 示例需要更多的工作(我目前正在编写处理 4-5000 个同时连接的异步服务器,这不是一项简单的任务!)。
话虽如此,该示例似乎完全能够处理多个客户端;但是,只要您不管理连接,您就无法向客户端发送任何消息,也无法以优雅的方式关闭服务器(在关闭之前断开所有客户端的连接)。
如果您只需要向所有客户端广播消息,您可以轻松地使用所有已连接套接字的列表。 IE。在 AcceptCallback 中,您应该将处理程序保存在列表中。要保持连接打开,请删除 SendCallback 中的 handler.Shutdown() 和 handler.Close()。像这样的:
private List<Socket> _clients = new List<Socket>();
public static void AcceptCallback(IAsyncResult ar) {
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
_clients.Add(handler); // Maintain connected clients
}
public void BroadcastMessage(string message)
{
// Send the message to all clients
var bytes = Encoding.ASCII.GetBytes(message);
foreach(var c in _clients)
{
c.BeginSend(bytes, 0, bytes.Length, SocketFlags.Broadcast, OnMessageBroadcast, c);
}
}
private void OnMessageBroadcast(IAsyncResult res)
{
Socket client = (Socket)res.AsyncState;
client.EndSend(res);
}
如果你对服务器和套接字连接不熟悉,大致是这样的事件流程:
服务器创建一个监听器,它将处理传入的连接,并提供回调
在侦听器上调用 BeginAccept 以指示服务器已准备好处理连接
-
当客户端连接时,监听套接字回调:
3a。在侦听器套接字上调用 EndAccept 会提供另一个进行实际通信的套接字。
3b。要开始通信,请在通信套接字上调用 BeginReceive,并提供一个回调来接收来自客户端的消息
3c。再次调用 BeginAccept 表示您已准备好接收新连接
-
收到消息后,通信套接字回调:
4a。调用 EndReceive 以读取字节。如果字节数为0,则对方正在断开连接
4b。处理消息并调用 BeginReceive 接收下一条消息
当你想关闭连接时发送一个 Shutdown(Send),并等待对方回复关闭(在 BeginReceive 回调中将变为 0 字节),然后关闭连接。
这只是一个粗略的大纲,但还有很多很多事情需要解决。尤其是异常处理可能非常棘手!
为了使整个内容更具可读性,我首先将客户端和服务器部分拆分为单独的类 - 否则所有发送和读取方法很快就会变得无法区分。