【发布时间】:2017-07-18 05:49:42
【问题描述】:
我有一个使用 IOCP 的服务器应用程序。我想知道关闭SOCKET 的正确方法是什么。
如果我只是简单地调用closesocket()(例如SOCKET 的句柄为12345),而这个SOCKET 有待处理的IO 操作(例如:待处理的WSARecv() 请求),那么可能会发生以下情况:
我打电话给
closesocket(),这将破坏SOCKET。我接受另一个
SOCKET与12345相同的句柄。我使用
12345的句柄将SOCKET的待处理WSARecv()完成数据包加入队列。现在我假设这个完成包是针对当前SOCKET,句柄为12345,但实际上它是针对之前关闭的SOCKET(这是他使用这种方法的主要问题)。
所以这显然是一个不好的方法。
似乎正确的第二种方法如下:
我将
struct实例与每个SOCKET关联起来。struct具有以下成员:一个名为number_of_pending_IO_operations的int和一个名为Is_SOCKET_being_closed的boolean。
1234563将当我要发出另一个IO操作时(例如:一个
WSASend()请求),我会检查Is_SOCKET_being_closed的值,如果是true,我不会发出IO操作。
1234563
number_of_pending_IO_operations 递减1。
1234563将Is_SOCKET_being_closed 设置为true。
当然我会有竞争条件,所以我会使用临界区。
第二种方法是关闭SOCKET的正确方法,还是有更好的方法?
【问题讨论】:
-
“第二种方法” - 是的,现在你走上了正确的道路。但是,您在这里不需要关键部分,所有这些都可以通过联锁操作来完成。
InterlockedIncrement/InterlockedDecrement为number_of_pending_IO_operations。关于Is_SOCKET_being_closed不够好。真的这里需要停机保护(用于插座手柄)。在用户模式下没有 api,但非常简单地自己实现它。if (AcquireRundownProtection()) { WSASend(); ReleaseRundownProtection(); }when 想要 closesocket - 启动 rundown(但不在此处等待)以及何时完成关闭句柄。 -
请注意,您传递给 I/O 操作的
OVERLAPPED结构可以是包含上下文信息的较大结构的第一个成员,例如,指向与套接字关联的结构的指针。这样可以节省您在表格或类似物中查找套接字句柄的时间,这反过来意味着如果方便的话,没有令人信服的理由不关闭套接字。 -
RbMm 很高兴在这里见到其他驱动程序开发人员:-)。看起来你对微过滤器有一些工作经验。实际上,我确实在同一个代码库中的用户空间中实现了破旧保护,并且正在考虑将它用于这个目的(除了其他一些目的)——并在一些测量后决定反对它。破旧保护似乎更适合引用寿命较长且它们不产生的情况(或者处理 AddRef() 的失败更容易),并且当您有明确的所有者时,因此您保证只有一次调用 BeginRundown。
-
Harry Johnson,我看不出这种方法如何单独保证在另一个线程进入 WSARecv/WSASend() 时调用 closesocket() 时的正确性。至于在另一个结构中包含 OVERLAPPED,这确实是一个非常有用的技术。
标签: c sockets winapi winsock iocp