我有一个关于this的问题(“异步服务器套接字多客户端”)。
要么微软在groos回复后就改变了,要么我真的不明白-
在这个例子中,它说:

        while (true) {
            // Set the event to nonsignaled state.
            allDone.Reset();

            // Start an asynchronous socket to listen for connections.
            Console.WriteLine("Waiting for a connection...");
            listener.BeginAccept(
                new AsyncCallback(AcceptCallback),
                listener );

            // Wait until a connection is made before continuing.
            allDone.WaitOne();
        }

据我所知,BeginAccept()函数在while(true)循环中被连续调用,只在调用acceptCallback()函数之前被停止,因为发生的第一件事是alldone.set()。
但是格罗说
msdn示例的问题在于它只允许连接单个客户机(listener.beginacept只调用一次)。
事实上,我不明白为什么要使用manuallresetevent alldone…
我认为listener.endaccept(ar)方法无论如何都会阻塞。
listener.beginacept()是否在运行时再次调用时引发异常?
但如果是这样,为什么alldone.set()在listener.endaccept(ar)之前?
还有一个问题:
我可以在收到eof后在readcallback(iasyncresult ar)函数中调用handler.beginreceive(…)来等待来自同一客户端的更多输入数据吗?
有经验的人能给我解释一下吗?
谢谢!

最佳答案

有可能这个例子实际上是更新的,因为另一个so答案被发布了。或者可能是回答者格罗根本不完全理解这个例子。在任何一种情况下,你都正确地注意到他关于只有一个客户可以被接受的陈述是错误的。
我同意usr写的一些东西,但对整个事情有点不同的看法。另外,我认为这些问题应该得到更全面和具体的处理。
首先,虽然我同意在接受回调方法中发出对BeginAccept()的后续调用,而不是使用循环,但在示例中的实现本身没有任何错误。在前一个调用的after completion发出信号之前,不会对BeginAccept()进行新的调用;事件句柄用于将调用BeginAccept()的线程与处理完成的线程同步。只有当先前发出的accept完成时,第一个线程才被释放,然后在线程再次阻塞之前,只有一个对BeginAccept()的新调用。
这有点尴尬,但完全是清白的。有可能这个示例的作者认为,由于在他的控制台程序中,他将有一个线程闲置在那里,所以他可能会给它做点什么。:)
总之,要回答问题1:您是正确的,该链接当前的示例允许多个客户机连接。
问题2,为什么使用事件句柄,我希望上面的解释已经回答了这个问题。该示例使用它来释放调用BeginAccept()的线程,以便在上一次调用完成后可以再次调用该线程。
问题3,EndAccept()是否阻塞?某种程度上。如果在接受操作实际完成之前调用EndAccept(),则为是。它会阻塞。但在本例中,只有在调用了完成回调时才调用它。此时,我们可以确定对EndAccept()的调用不会阻塞。它所要做的就是检索完成操作的结果(当然,假定没有异常)。
问题4,在调用BeginAccept()之前再次调用EndAccept()是否合法?是的,即使有多个接受操作排队是不合法的。这里,对BeginAccept()的调用发生在第一个BeginAccept()的完成回调中。也就是说,虽然代码还没有调用EndAccept(),但是accept操作本身已经完成,因此它甚至不是有多个accept操作未完成的情况。接收和发送操作同样自由;您可以在完成任何操作之前多次合法地调用所有这些方法。
问题5,即使我收到了BeginReceive(),我是否可以打电话给<EOF>?对。事实上,这是msdn示例有缺陷的一个方面,因为一旦接收到最后一个预期数据,它就不会继续接收。实际上,在接收以0字节完成之前,它仍然应该再次调用BeginReceive(),不管是否需要更多的数据,然后通过在该点调用Shutdown(SocketShutdown.Both),以信号确认连接的正常关闭(假设g此时它已经完成发送,此时它应该已经调用了Shutdown(SocketShutdown.Send)。。如果没有,它应该只使用SocketShutdown.Receive和/或根本不调用shutdown,直到它完成发送,并且它可以使用SocketShutdown.BothSocketShutdown.Receive实际上对连接本身没有任何重要作用,所以合理的做法是等待直到SocketShutdown.Both合适)。
换句话说,即使服务器确定客户机不会发送任何额外的数据,正确使用socket api仍然会尝试另一个接收操作,查找表明客户机实际上已经开始关闭连接的0字节返回值。只有在这一点上,服务器才应该开始自己的关闭过程并关闭套接字。
最后,您没有问,但因为USR提出了这个问题:我不同意这个msdn示例在今天没有相关性。不幸的是,Microsoft没有为Socket类提供基于任务的异步API版本。还有其他支持异步/等待的网络API(例如,TcpClient/NetworkStream),但是如果您想直接使用Socket类,那么您就只能使用旧的异步模型(Socket有两个,都是基于回调的)。
您可以将synchronousSocket方法包装在Tasks中,作为旧API的替代方法,但是这样会失去socket类中基于I/O完成端口的异步API的优势。最好是某种任务兼容的包装器,它仍然使用下面的异步api,但是实现起来有点复杂,而且我目前还不知道这样的事情(但它肯定是存在的,所以如果是您更喜欢使用async/await)。
希望能有帮助!我为这个冗长的回答道歉,但这是一个相当宽泛的问题(几乎太宽泛了,但对我来说,它似乎仍然在合理的范围内)。

08-25 22:28