Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用IOCP时如何关闭套接字?_C_Sockets_Winapi_Winsock_Iocp - Fatal编程技术网

使用IOCP时如何关闭套接字?

使用IOCP时如何关闭套接字?,c,sockets,winapi,winsock,iocp,C,Sockets,Winapi,Winsock,Iocp,我有一个使用IOCP的服务器应用程序。我想知道关闭套接字的正确方法是什么 如果我只是调用closesocket()(对于句柄为12345的SOCKET),并且该SOCKET具有挂起的IO操作(例如:挂起的WSARecv()请求),则可能发生以下情况: 我调用closesocket(),这将破坏套接字 我接受另一个套接字,其句柄与12345相同 我使用12345句柄为SOCKET定义挂起的WSARecv()完成数据包。现在我假设这个完成数据包是针对当前SOCKET,句柄是12345,但实际上是针

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

如果我只是调用
closesocket()
(对于句柄为
12345
SOCKET
),并且该
SOCKET
具有挂起的IO操作(例如:挂起的
WSARecv()
请求),则可能发生以下情况:

  • 我调用
    closesocket()
    ,这将破坏
    套接字

  • 我接受另一个
    套接字
    ,其句柄与
    12345
    相同

  • 我使用
    12345
    句柄为
    SOCKET
    定义挂起的
    WSARecv()
    完成数据包。现在我假设这个完成数据包是针对当前
    SOCKET
    ,句柄是
    12345
    ,但实际上是针对先前关闭的
    SOCKET
    (这是这种方法的主要问题)

所以这显然是一个糟糕的方法

第二种似乎正确的方法是:

  • 我将一个
    struct
    实例与每个
    SOCKET
    关联。
    struct
    有以下成员:一个名为
    int
    number\u挂起的操作
    ,一个名为
    boolean
    套接字正在关闭

  • 当我为
    SOCKET
    发出IO操作时(例如:a
    WSASend()
    请求),我通过
    1
    增加
    挂起IO操作的数量,当我为SOCKET发出完成包时,我通过
    1
    减少
    挂起IO操作的数量

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

  • 当我准备发出另一个IO操作(例如:a
    WSASend()
    请求)时,我会检查
    的值是否正在关闭
    ,如果它是
    true
    ,我就不会发出IO操作

  • 现在,我只需等待所有的完成数据包出列,当
    挂起的IO操作的数量
    达到
    0
    并且
    套接字是否关闭
    设置为
    true
    ,我调用
    closesocket()

当然,我会有比赛条件,所以我会使用关键部分


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

我的客户机服务器应用程序中也有类似问题。我想我没有比赛,没有破败保护,也没有关键部分。不过也有一些折衷办法

  • 一次只能有一个WSARecv()。多个缓冲区——可能是,但只有一个WSARecv()。WSARecv完成(可能是内联的),数据包从完成端口弹出,我快速检查得到的数据并发出另一个WSARecv()
  • 我有一个标志(实际上,一个只能上升的计数器)表示插座正在关闭。计数器,因为我可能同时决定在两个不同的位置终止套接字
  • 在关闭设置后,我调用shutdown(),然后调用CancelIoEx(),这将破坏之前发布的WSARecv()
  • CancelIoEx和WSARecv()的完成之间存在竞争——我通过在发出WSARecv()之前仔细检查套接字是否关闭来消除这一竞争。即使发生竞争,WSARecv()也注定会失败,因为shutdown()
  • 该结构是refcounted,其中一个ref由接收状态机持有,一个ref由ptr的每个合法所有者持有(如中的潜在发送方)。如果您有一个合法的引用,您可以制作一个副本,并将其交给其他人(这里的副本不同于详细的引用,因为它们的AddRef()可能会失败)。我只在ref为零时关闭socket(),因为我不能保证没有线程通过了所有检查并即将发出WSARecv/WSASend

“第二种方法”-是的,现在你走上了正确的道路。然而,这里不需要关键部分,所有这些都可以通过联锁操作完成<代码>联锁增量
/
联锁增量
用于
挂起的IO操作数
。about
插座是否处于关闭状态
不够好。这里真的需要耗损保护(插座手柄)。在用户模式下,没有用于此的api,但只需自行实现即可<代码>如果(AcquisiteUndownProtection()){WSASend();ReleaseRundownProtection();}
当需要closesocket时-启动rundown(但不要在此处等待),并在它完成关闭句柄时。关于请注意,传递给I/O操作的
重叠
结构可以是包含上下文信息的较大结构的第一个成员,例如,指向与套接字关联的结构的指针。这样可以节省您在表或类似文件中查找套接字句柄的时间,这反过来意味着,如果方便的话,没有令人信服的理由不关闭套接字。RbMm很高兴在这里看到其他驱动程序开发人员:-)。看起来你有一些使用微型过滤器的工作经验。事实上,我确实在同一个代码库中在userland中实现了rundown保护,并考虑将其用于这个目的(除了其他一些目的之外)——在进行了一些测量之后,我决定不使用它。Rundown保护似乎更适合于引用寿命较长且不会生成引用(或者更容易处理AddRef()的失败)的情况,因此当您有一个明确的所有者时,您可以保证只有一个调用BeginRundown。Harry Johnson,我看不出当另一个线程进入WSARecv/WSASend()时调用closesocket()时,这种方法如何保证正确性。至于包含超过