当我需要关闭一个端口时,调用了CancelIO,然后再使用shutdown和closesocket,理论上在此端口的所有pending中的IO操作都会被取消掉。
但为何我原先投递的WSARecv的操作有时候还是会接收到IO完成消息,接收到字节数为○,或者有时候GetQueuedCompliationStatus返回一个错误码,错误码是995或者10038之类的。(995是ABORT可以理解的,但为何只是有时候收到,而不是每次收到?)此外还有问题就是:
一。如何确定我需要投递的acceptex请求?投递多了,浪费资源,投递少了又会引起连接拒绝?有没有函数可以知道listen中指定的那个backlog参数,已经被用掉几个了?二。WSARecv中的缓冲区大小是固定的,也就是说接收满缓冲以后才返回,那如果客户端每次发送包的大小不一样怎么办?我现在是在每个包头加一个4字节大小的包头,每次先收4字节,然后再根据收到的大小来确定WSABUF中缓冲区的大小。有没有更好的方法可以解决?thanks

解决方案 »

  1.   

    看我这里的log,该注意的地方我都已经加以注释了真的是很奇怪阿。
    其中SI是每次投递的IO请求的buffer地址,不同的SI就代表着不同的socket 连接(在投递AcceptEx时生成的),这里有3个不同的连接A,B,C(虽然socket句柄是一样的,但也应该是不同的连接)
    A:
    current Event:close_socket SI:0xd6f1b8 SOCKET:2740 bytes:0
    Cancel socket operator:2740 --->>>调用了CancelIO函数
    Close a socket 2740          --->>>调用closesocket关闭2740current Event:recv SI:0xd6f1b8 SOCKET:2740 bytes:4       -->CancelIO没有起作用?
    receive a packet head SI:0xd6f1b8 SOCKET:2740 bytes:4......................... -->省略了很多无关log记录
    .........................
    B:
    current Event:close_socket SI:0xd68e40 SOCKET:2740 bytes:0
    Cancel socket operator:2740 --->>>调用了CancelIO函数
    Close a socket 2740 --->>>2740被关闭了C:
    current Event:accept_connection SI:0xd83270 SOCKET:2816 bytes:0
    Generate a new SI:0xd71e40 when ACCEPT SOCKET:2740              --->>>>>2740被重新分配给AcceptEx,并投递了一个IO请求current Event:close_socket SI:0xd63388 SOCKET:2876 bytes:0
    Cancel socket operator:2876
    Close a socket 2876
    current Event:close_socket SI:0xd68d88 SOCKET:2724 bytes:0
    Cancel socket operator:2724
    Close a socket 2724
    current Event:recv SI:0xd82600 SOCKET:2780 bytes:4
    receive a packet head SI:0xd82600 socket:2780
    current Event:recv SI:0xd75430 SOCKET:2824 bytes:4
    receive a packet head SI:0xd75430 socket:2824
    WSARecv() 3 failed with error 10038
    current Event:accept_connection SI:0xd93f60 SOCKET:2736 bytes:0
    Generate a new SI:0xda4418 when ACCEPT SOCKET:2724
    current Event:recv SI:0xd8c420 SOCKET:2760 bytes:4
    receive a packet head SI:0xd8c420 socket:2760
    WSARecv() 3 failed with error 10038
    current Event:2 SI:0xd68d88 SOCKET:2724 bytes:4
    receive a packet head SI:0xd68d88 socket:2724current Event:recv SI:0xd6f1b8 SOCKET:2740 bytes:4 -->>>注意SI地址,竟然收到了很早就被我closesocket掉的A处socket投递的IO请求current Event:recv SI:0xd68e40 SOCKET:2740 bytes:4 -->>>收到了已经关闭的B处socket的IO
      

  2.   

    WSARecv 正常返回, 字节数为0,表示: the connection has been gracefully closed.当你需要关闭端口时, 只需要调用 closesocket 即可,所有PENDING的WSARECV操作都会立即完成. 不需要 CancelIO.一。如何确定我需要投递的acceptex请求?投递多了,浪费资源,投递少了又会引起连接拒绝?有没有函数可以知道listen中指定的那个backlog参数,已经被用掉几个了?
    _________
    注册 FD_ACCEPT 消息. 如果你没有ACCEPTEX可用了, 那么有新的连接请求时,你就会收到这个消息. 二。WSARecv中的缓冲区大小是固定的,也就是说接收满缓冲以后才返回,那如果客户端每次发送包的大小不一样怎么办?我现在是在每个包头加一个4字节大小的包头,每次先收4字节,然后再根据收到的大小来确定WSABUF中缓冲区的大小。有没有更好的方法可以解决?thanks
    ___________
    不是啊, 客户端发送一段数据, WSARECV收到了就会返回, 不是一定要填满了才返回. 当底层TCP的接收缓冲区中已经没有数据可以复制到 WSARECV 提供的BUFFER中时,WSARECV就会返回.
      

  3.   

    恩,恩,对于你第一点的回答,你的意思是不是使用WSAEnumEvent这个函数?还是使用WSAAsynSelect来注册窗口消息???效率如何?会不会低下?不过如果没有更好的方法也只能这么用了。
    对于第二个问题。我陈述错了(被这个麻烦的东西搞晕掉了)。当这个问题不存在好了。呵呵 sorry关键在于第一个问题,不管有没有CancelIo的调用,closesocket都会引起一个pending的操作完成。当你需要关闭端口时, 只需要调用 closesocket 即可,所有PENDING的WSARECV操作都会立即完成. 不需要 CancelIO.
    -----------------------
    意思是所有投递了的IO操作会被GetQueuedCompleteStatus立即弹出,还是指不会被弹出了?
      

  4.   

    意思是所有投递了的IO操作会被GetQueuedCompleteStatus立即弹出,还是指不会被弹出了?
    ________
    会弹出呀,比如 WSARecv 正常返回, 字节数为0. 或者返回一个出错的值.
    我的意思是用 WSAEventSelect(... FD_ACCEPT ) + 独立的线程来 WaitForSingleObject .
      

  5.   

    ________
    会弹出呀,比如 WSARecv 正常返回, 字节数为0. 或者返回一个出错的值.-----------------
    不知是不是我理解错了
    WSARecv在调用closesocket后是会返回出错或者0字节,但我关注的是已经使用WSARecv投递了的IO请求。
    在closesocket之前对于GetQueuedCompleteStatus的调用是阻塞在那里的,
    当调用closesocket时GetQueuedCompleteStatus对于此socket会不会立即返回呢?或者GetQueuedCompleteStatus是不会再返回此socket已投递的任何IO信息。
    对于你的回答,我非常感谢。
      

  6.   

    WSARecv 正常返回就说明 投递了的IO请求已经完成。当调用closesocket时GetQueuedCompleteStatus对于此socket会不会立即返回呢?
    ___________
    会立即返回呀,返回的就是 WSARECV 的这个IO操作, 表示 WSARECV 操作完成.
      

  7.   

    糊涂了。^_^例如有4步操作
    1.WSARecv(s)  -->>返回IO_PENDING IO操作投递成功
    2.WSARecv(s)  -->>返回IO_PENDING IO操作投递成功
    3.closesocket(s)    关闭socket
    4.GetQueuedCompleteStatus()那第4步是得到前面两个投递成功了的IO操作呢,还是返回两个WSA_IO_ABORT。或者是阻塞在那里,什么都不返回?
      

  8.   

    第4步这里是 得到前面两个投递成功了的IO操作(或者是ABORT,但这不重要,你知道完成了就好. ) 但你必须连续调用两次 GetQueuedCompleteStatus 才能取得这两次 WSARECV 的IO操作.