我有一个程序,它通过监听连接开始。我想实现一种模式,服务器可以在其中接受连接,将该单个连接传递给用户类进行处理:将来的数据包接收和数据处理。
在我发现异步使用Socket
类并不可怕之前,我遇到了同步模式的问题。但后来我遇到了更多麻烦。似乎在while (true)
循环中,由于BeginAccept()
是异步的,程序将不断地遍历此循环并最终进入OutOfMemoryException
。我需要一些东西来监听连接,并立即将该连接的责任移交给其他类。
所以我读了微软的例子,发现了ManualResetEvent
。我实际上可以指定何时我准备好让循环再次开始收听!但在阅读Stack Overflow上的一些问题后,我感到很困惑。
我担心的是,即使我异步接受了连接,整个程序也会在重新进入循环时尝试侦听新连接时阻塞。如果我处理多个用户,这并不理想。
我对异步I / O的世界非常陌生,所以即使是对我的词汇或滥用短语的最愤怒的评论,我也会感激。
代码:
static void Main(string[] args)
{
MainSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
MainSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.1.74"), 1626));
MainSocket.Listen(10);
while (true)
{
Ready.Reset();
AcceptCallback = new AsyncCallback(ConnectionAccepted);
MainSocket.BeginAccept(AcceptCallback, MainSocket);
Ready.WaitOne();
}
}
static void ConnectionAccepted(IAsyncResult IAr)
{
Ready.Set();
Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
}
答案 0 :(得分:1)
他们使用基于旧式WaitHandle
的事件的Microsoft示例将起作用,但坦率地说,实现异步代码是一种非常奇怪和尴尬的方式。我觉得事件在这个例子中主要是作为人工同步主线程的一种方式,所以它有事情要做。但它并不是真正正确的做法。
一种选择是甚至不能异步接受套接字。相反,在连接套接字时使用异步I / O,并在主线程中使用同步循环来接受套接字。这最终完全是Microsoft示例所做的事情,但保留主线程中的所有接受逻辑,而不是在主线程(启动接受操作)和处理完成的某个IOCP线程之间来回切换
另一个选择是给主线程别的东西做。举一个简单的例子,这可能只是等待一些用户输入来表示程序应该关闭。当然,在实际程序中,主线程可能是有用的(例如,在GUI程序中处理消息循环)。
如果给主线程做了其他事情,那么你可以按照预期的方式使用异步BeginAccept()
:你调用方法来启动接受操作,然后不要打电话再一次,直到该操作完成。初始化调用在初始化服务器时发生,但所有后续调用都在完成回调中发生。
在这种情况下,您的完成回调方法看起来更像是这样:
static void ConnectionAccepted(IAsyncResult IAr)
{
Connection UserConnection = new Connection(MainSocket.EndAccept(IAr));
MainSocket.BeginAccept(ConnectionAccepted, MainSocket);
}
也就是说,您只需在完成回调本身中调用BeginAccept()
方法。 (请注意,不需要显式创建AsyncCallback
对象;编译器将代表您隐式地将方法名转换为正确的委托类型实例。