微软异步服务器套接字示例

我有一个关于这个问题的问题(“异步服务器套接字多个客户端”)。

要么微软改变了这个例子,因为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(); } 

据我所知,在while(true)循环中连续调用BeginAccept()函数,只有在调用AcceptCallback()函数时才会被调用,因为首先发生的是allDone.Set()。

但格罗说

MSDN示例的问题在于它只允许连接单个客户端(listener.BeginAccept只调用一次)。

实际上我不明白为什么使用ManualResetEvent allDone …而且我认为listener.EndAccept(ar)方法无论如何都是阻塞的。

listener.BeginAccept()是否在仍然运行时第二次调用时抛出exception? 但是,如果是这样,为什么allDone.Set()在listener.EndAccept(ar)之前呢?

还有一个问题:

在收到EOF后,我可以在ReadCallback(IAsyncResult ar)函数中调用handler.BeginReceive(…)来等待来自同一客户端的更多数据吗?

任何有经验的人都可以向我解释一下吗?

谢谢 !

这个例子实际上可能已更新,因为其他SO答案已经发布。 或者回答者Groo可能根本就没有完全理解他自己的例子。 在任何一种情况下,你都可以观察到他只能接受一个客户的声明是不正确的。

我同意usr写的一些内容,但对整个事情有一些不同的看法。 此外,我认为这些问题值得更全面和具体的处理。

首先,虽然我同意优先设计通常是在接受回调方法中发出对BeginAccept()后续调用而不是使用循环,但在示例中的实现本身没有任何错误。 在发出上一次呼叫完成之前,不会对BeginAccept()进行新的调用; 事件句柄用于同步调用BeginAccept()的线程与处理完成的任何线程。 第一个线程仅在先前发出的accept完成时释放,然后在该线程再次阻塞之前仅对BeginAccept()进行一次新调用。

这有点尴尬,但完全是犹太人。 这个样本的作者可能认为,因为在他的控制台程序中,无论如何他都会在那里闲置一个线程,他不妨给它做点什么。 🙂

无论如何,回答问题#1:你是对的,当前在该链接上的例子确实允许多个客户端连接。

问题#2,为什么使用事件句柄,我希望上面的解释已经回答了。 这是该示例用于释放调用BeginAccept()的线程的示例,以便在前一次调用完成后它可以再次调用它。

问题3, EndAccept()阻塞了吗? 有点。 如果在accept操作实际完成之前调用EndAccept()EndAccept() yes。 它会阻止。 但在这种情况下,只有在调用完成回调时才会调用它。 此时,我们可以确定对EndAccept()的调用不会阻塞。 它所要做的就是在那时检索已完成操作的结果(当然,假设没有例外)。

问题#4,在调用EndAccept()之前第二次调用BeginAccept()是否合法? 是的,即使排队的多个接受操作不合法(事实上也是如此)。 这里,对BeginAccept()的调用发生在第一个BeginAccept()的完成回调中。 也就是说,虽然代码尚未调用EndAccept() ,但是接受操作本身已经完成,因此甚至不会出现多个接受操作未完成的情况。 接收和发送操作同样自由; 你可以合法地调用所有这些方法,然后才能完成所有这些方法。

问题#5,即使我收到我还可以调用BeginReceive()吗? 是。 实际上,这是MSDN示例存在缺陷的区域,因为一旦接收到最后一个预期数据,它就不会继续接收。 实际上,在接收完成0字节之前,它仍应始终再次调用BeginReceive() ,无论是否需要更多数据,然后通过调用Shutdown(SocketShutdown.Both)处理完成的接收,其中字节计数为0指向连接正常关闭的信号确认(假设已经完成了该点的发送,此时它已经调用了Shutdown(SocketShutdown.Send) ……如果没有,它应该只使用SocketShutdown.Receive和/或只是在完成发送之前根本不调用Shutdown,它可以使用SocketShutdown.BothSocketShutdown.Receive实际上并没有对连接本身做任何重要的事情,所以等到SocketShutdown.Both是合适的是合理的)。

换句话说,即使服务器确定客户端不会发送任何其他数据,正确使用套接字API仍然是尝试另一个接收操作,寻找指示客户端的0字节返回值事实上已经开始关闭连接。 只有在那时,服务器才能开始自己的关闭过程并关闭套接字。

最后,你没有问,但因为usr提出了它:我不同意这个MSDN示例今天没有相关性。 不幸的是,Microsoft没有为Socket类提供基于任务的异步API版本。 还有其他网络API支持async / await(例如TcpClient / NetworkStream ),但是如果你想直接使用Socket类,你就会遇到旧的异步模型( Socket有两个,都是基于回调的)。

您可以将Tasks的同步Socket方法包装为旧API的替代方法,但是您将失去Socket类中基于I / O完成端口的异步API的优势。 更好的是某种任务兼容的包装器仍然使用下面的异步API,但实现起来有点复杂,我现在还不知道这样的事情(但它肯定存在,所以可能是如果您更喜欢使用async / await,那么值得您进行网络搜索。

希望有所帮助! 我为这个长期答案道歉,但这是一个相当广泛的问题(几乎太宽泛,但对我而言似乎仍然在合理的SO范围内:))。

样本很混乱。 不需要这个活动。 相反,接受回调应该发出下一个接受操作,以便总有一个接受未完成。

在非节流循环中调用BeginAccept将是不正确的,因为这将启动无限数量的未完成的接受操作。

您是否意识到, await旧APM的引入已经过时了? 所有这些代码今天都没有相关性。

另请注意,Web上的大多数套接字代码都有不同的方式存在严重缺陷。