io完成端口问题,每个GetQueuedCompletionStatus调用多个wsarecv或wsasend

时间:2014-07-19 14:17:04

标签: c sockets iocp

我有一个应该与套接字(udp)和设备通信的应用程序,我正在使用IOCP进行通信。它的工作方式是从通过套接字向远程对等方发送和接收一些数据开始,然后它开始读取和写入设备,所有这些都在一个线程中完成(以避免锁定)。所以对句柄的读写都是相互依赖的,如果没有正确调用,其中一个就会变得饿死。像大多数IOCP编码风格一样:

WSASendTo(...)

while(true)
{
    GetQueuedCompletionStatus(...,event,...)

    switch (event->e_type)
    {
      case READ_EVENT:
        if(e->event->handle == socket)
        {
                 get_socket_data(...);
                 start_read_socket(...);
                 start_write_socket(...);
                 start_write_dev(...);
        }
        else if(event->handle == dev)
        {
                 get_dev_data(...);
                 start_read_dev(...);
                 start_write_socket(...);
                 start_write_dev(...);
        }
        break;
      case WRITE_EVENT:
        if(event->handle == socket)
        {
                 check_socket_write(...);
                 start_write_socket(...);
                 start_read_socket(...);
                 start_read_dev(...);
        }
        else if(event->handle == dev)
        {
                check_dev_write(...);
                start_write_dev(...);
                start_read_dev(...);
                start_read_socket(...);
        }
            break;
    }
}

但我的情况不同之处在于获取GetQueuedCompletionStatus之后 我对socket和dev执行多个异步读取和写入以确保始终应用程序  愿意从socket和dev获取数据。我还创建了一个io数据结构,并为每个句柄分配了两个。一个用于读取,一个用于写入:

typedef struct
{
    WSAOVERLAPPED io_ovl;

    int e_type;

    HANDLE handle;

    WSABUF wsabuf;

    BUFFER buffer;

    uint8_t pending;

} ESTATE;

...

ESTATE eread_socket;

ESTATE eread_dev;

ESTATE ewrite_socket;

ESTATE ewrite_dev;

而且我还在检查是否有待处理的读写(意味着我已经请求了)然后我没有注册新的操作。
有了这种风格,一切都工作正常,那些日子我正在将小数据传输到套接字和设备,直到有一天我决定发送套接字发送和接收更大的数据,它开始显示有线的东西。

虽然应用程序本身甚至wireshark显示数据发送很好,但另一个同行正在获取大数据包的修改数据!甚至另一个同行的wireshark显示udp校验和错误。并且正如你所知,计算校验和是内核空间(或者如果启用了校验和卸载,则在网络驱动程序内)我开始认为驱动程序存在问题并且......所以我写了一个简单的IOCP客户端来发送它大数据,并发现它的工作正常!

我想不出我的应用程序正在搞乱Windows内核,其中数据正常传递到Windows内核但内核在将其传递给网络驱动程序时搞砸了。 (我使用的是linux内核后台。内核空间,用户空间....)

但是我开始以多种方式调试我的应用程序并找出WSASendTo何时无法发送数据(因为它很大并且可能分配那么多缓冲区)并进入WSA_IO_PENDING状态,从这一点开始任何其他调用任何其他ESTATE都会使有线结果发送。考虑不会弹出错误。它发送正常。但是在另一端接收的数据被修改,因此变得无用。

当我说“对任何其他ESTATEs的任何其他调用都会产生有线结果”时,即使我将它们传递给函数并在函数的开头放置一个断点(意味着我不会在函数内部更改它们) ,只是传递它们)它使它在发送时修改数据包内容,如果我在每次发送后放入例如1秒(进入WSA_IO_PENDING)并因此购买一些时间让Windows发送它们,它工作正常。

例如在这个块中:

case WRITE_EVENT:
        if(event->handle == socket)
        {
                 check_socket_write(...);
                 start_write_socket(...);
                 start_read_socket(...);
                 start_read_dev(...);
        }

当它说套接字成功写入时,我做check_socket_write(...) 做一些后期处理然后start_write_socket(...) 如果进入WSA_IO_PENDING,则调用start_read_socket(...)使其成为有线连接。

因为所有这些写入和读取功能都与他们自己的ESTATE一起工作,所以首先我认为这是因为有些东西弄乱了那些ESTATE数据(如果是因为这样,我会严重检查是否有待处理,如果是正在等待我优雅地从那些功能返回并且不改变任何数据)我多次检查不使用那些ESTATE数据而不是相互使用了几天并且确保它不是我的应用程序故障并且行为如此有线可能是因为一些内部地址弄乱了iocp api!毕竟只是将数据结构传递给函数应该不是问题,并提醒我SEGMENTATION FAULT行为。

在获取GetQueuedCompletionStatus结果后,所有在互联网上使用IOCP的样本只调用一个WSA(发送/接收),那么我是否在调用多个写入和读取请求时出错?

感谢阅读这篇长篇故事

修改 即使我在上述块中注释掉start_read_socket(...)和start_read_dev(...) 对其他结构的其他调用使其修改发送数据包因此它听起来更多的是IOCP缺少待处理的发送请求。在调用进入挂起状态的WSASendTo之后的声音,对于包含或接近WSASend消耗的重叠结构的结构不做任何事情,直到你到达GetQueuedCompletionStatus !!! 我'我真的很喜欢这个:|

1 个答案:

答案 0 :(得分:1)

我得到了这个工作,所有都是基于一个错误!

在start_write_socket(...)中,当我使用有效缓冲区的地址分配WSABUF时,我正在从队列中读取内容然后获取它的地址,并且没有注意到该地址仅在该函数内有效。这就是为什么当它没有进入待决状态时,工作正常。但是在待处理状态和该函数之外,该地址不再有效并产生有线结果。