如何在使用IOCP时关闭SOCKET?

时间:2017-02-27 14:50:35

标签: c sockets winapi winsock iocp

我有一个使用IOCP的服务器应用程序。我想知道关闭SOCKET的正确方法是什么。

如果我只是简单地调用closesocket()(对于SOCKET,其句柄为12345),并且此SOCKET具有待处理的IO操作(例如:a pending) WSARecv()请求),然后可能发生以下情况:

  • 我致电closesocket()会破坏SOCKET

  • 我接受SOCKET具有相同句柄的其他12345

  • 我使用句柄WSARecv()SOCKET的待处理12345完成数据包进行了解除。现在我假设这个完成包是针对当前SOCKET的句柄12345,但事实上它是针对之前关闭的SOCKET(这是他的主要问题这种方法)。

所以这显然是一种糟糕的做法。

第二种似乎正确的方法如下:

  • 我将struct个实例与每个SOCKET相关联。 struct有以下成员:int名为number_of_pending_IO_operationsboolean名为Is_SOCKET_being_closed

  • 当我为SOCKET发出IO操作时(例如:WSASend()请求),我将number_of_pending_IO_operations增加1,当我deque SOCKET的完成数据包,我number_of_pending_IO_operations递减1

  • 现在,当我要关闭SOCKET时,我不是简单地调用closesocket(),而是调用CancelIOEx()来取消所有挂起的IO操作SOCKET,我还将Is_SOCKET_being_closed设置为true

  • 当我即将发出另一个IO操作(例如:WSASend()请求)时,我会检查Is_SOCKET_being_closed的值,如果是true,我不会发出IO操作。

  • 现在我只是等待所有完成数据包出列,当number_of_pending_IO_operations到达0Is_SOCKET_being_closed设置为true时,我打电话给closesocket()

当然我会有竞争条件,所以我会使用关键部分。

第二种方法是关闭SOCKET的正确方法,还是有更好的方法?

1 个答案:

答案 0 :(得分:1)

我的客户端 - 服务器应用程序中存在类似的问题。我想我没有比赛,没有破坏保护,也没有关键部分。但是有一些权衡。

  • 一次只能有一个WSARecv()。多个缓冲区 - 可能,但只有一个WSARecv()。 WSARecv完成(可能是内联)来自完成端口的数据包弹出,我快速检查我得到了什么并发出另一个WSARecv()
  • 我有一个标志(实际上,一个只能上升的计数器)用于Is_SOCKET_being_closed。因为我可能会同时决定在两个不同的地方杀死套接字。
  • 设置Is_SOCKET_being_closed之后,我调用shutdown(),然后调用CancelIoEx(),这将破坏以前发布的WSARecv()。
  • 在CancelIoEx和WSARecv()完成之间存在争用 - 我在发出WSARecv()之前立即双重检查Is_SOCKET_being_closed来消除它。即使发生了竞争,WSARecv()也注定要失败,因为shutdown()。
  • 结构被重新计算,其中一个ref由接收状态机保持,并且为ptr的每个合法所有者保留一个ref(如潜在发送者)。如果您有合法的参考,您可以制作一份副本,您可以将其交给其他人(因为他们的AddRef()可能会失败,因此它与rundown引用不同)。当ref返回到零时,我只会关闭closesocket(),因为我无法保证没有线程通过所有检查并即将发出WSARecv / WSASend。