Windows中依赖重叠io的编码模式

时间:2014-01-20 22:37:16

标签: c windows iocp overlapped-io

我是一名Linux程序员,最近参与移植一个基于epoll的客户端,其中两个文件描述符用c写入windows。 如你所知,在linux中使用epoll或select(我知道windows支持select,但它根本不高效)你可以阻止文件描述符直到文件描述符 准备就绪,你可以知道何时可以写入和阅读。

我已经看过Windows IOCP了,在微软世界中重叠的io听起来没问题。 但在所有示例中,它用于多客户端服务器,每个客户端的套接字独立于其他套接字。

使用完成端口,可以为每个客户端创建一个completionKey结构,并将一个变量放在struct中,并在调用时将其读取 WSAsev和wirt当WSAsend和另一个变量指示套接字值并从GetQueuedCompletionStatus检索它们以知道该做什么时,如果为socket完成写入,则执行读取,反之亦然。

但在我的情况下,文件描述符(fd)确实是重叠的。从一个fd读取,读取和写入其他fd,这使得很难知道什么 在GetQueuedCompletionStatus结果中为每个fd执行操作,因为每个fd都有一个completionKey关联。要明确考虑一下:

有两个名为fd1和fd2的句柄,completionKey1为f1保持句柄和状态,fd2为completionKey2,而completionKey变量为 从GetQueuedCompletionStatus检索完成。

    GetQueuedCompletionStatus(port_handle, &completionKey.bufflen, (PULONG_PTR)&completionKey,(LPOVERLAPPED *)&ovl,INFINITE);

   switch (completionKey.status)
    {
        case READ:
            if(completionKey->handle == fd1)
            {
                fd1_read_is_done(completionKey.buffer,completionKey.bufflen);
                completionKey->status = WRITE;
                do_fd1_write(completionKey);
                completionKey2->status = WRITE;
                completionKey2->buffer = "somedata";
                do_fd2_write(completionKey2);
            }
            else if(completionKey->handle == fd2)
            {
                fd2_read_is_done(completionKey.buffer,completionKey.bufflen);
                completionKey->status = WRITE;
                do_fd2_write(completionKey);
                completionKey1->status = WRITE;
                completionKey1->buffer = "somedata";
                do_fd1_write(completionKey1);
            }
            break;
        case WRITE_EVENT:
            if(completionKey->handle == fd1)
            {
                fd1_write_is_done(completionKey.bufflen);
                completionKey->status = READ;
                do_fd1_read(completionKey);
                completionKey2->status = READ;
                do_fd2_read(completionKey2);
            }
            else if(completionKey->handle == fd2)
            {
                fd2_write_is_done(completionKey.bufflen);
                completionKey->status = READ;
                do_fd2_read(completionKey);
                completionKey1->status = READ;
                do_fd1_read(completionKey1);
            }
            break;
    }

在上面的代码中,出现了一些情况,即一些更改的completionKeys将覆盖挂起的读取或写入,结果 completionKey->状态是错误的(它会报告读取而不是写入),最差的是缓冲区将覆盖。如果我使用锁定 完成凯斯,它将导致死锁情况。

在查看WSAsend或WSArecv之后,注意到每个发送或接收都可以设置重叠参数。 但它导致了两个主要问题。根据WSAOVERLAPPED结构:

    typedef struct _WSAOVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
  union {
    struct {
      DWORD Offset;
      DWORD OffsetHigh;
    };
    PVOID  Pointer;
  };
  HANDLE    hEvent;
} WSAOVERLAPPED, *LPWSAOVERLAPPED;

首先,没有地方可以放置状态和适当的缓冲区,其中大部分是保留的。

第二,如果可以为第一个问题做一个工作,我需要检查是否没有可用的重叠左边,并且所有这些都用于待处理操作, 为每次读写分配一个新的,并且由于客户端将如此繁忙,它可能会发生很多事情,此外,管理这些重叠的池是一个令人头痛的问题。 所以我错过了什么或微软已经搞砸了这个?

由于我不需要多线程,是否有其他方法可以解决我的问题?
提前谢谢
修改
正如我猜测的那样,我在使用重叠结构时提到的第一个问题有答案,我需要创建另一个包含所有缓冲区和状态等的结构,并将OVERLAPPED作为首次提交。 现在你解决了其他问题;)

1 个答案:

答案 0 :(得分:1)

你真的在这里问两个不同的问题。我无法回答第一个问题,因为我从未使用过IO完成端口,但是从我读过的所有内容来看,除了专家之外,每个人都应该避免使用它们。 (我将指出一个明显的解决方案,我认为你正在描述的问题:而不是实际将数据写入另一个套接字,而另一个写入仍在等待,将数据放入队列并写入它仍然需要处理给定套接字上的两个同时操作 - 一个读取和一个写入 - 但这应该不是问题。)

但是,使用OVERLAPPED(或WSAOVERLAPPED)结构来跟踪重叠请求的状态很容易。您所做的只是将OVERLAPPED结构嵌入到更大结构中的第一个元素:

typedef struct _MyOverlapped
{
  WSAOVERLAPPED overlapped;
  ... your data goes here ...
} MyOverlapped, lpMyOverlapped;

然后将发送到完成例程的LPWSAOVERLAPPED投射到lpMyOverlapped以访问您的上下文数据。

或者,如果您正在使用完成例程,hEvent WSAOVERLAPPED成员保证未使用,因此您可以将其设置为指向您所选结构的指针。

我不明白为什么你认为管理重叠结构池会成为一个问题。每个活动缓冲区只有一个重叠结构,因此每次分配缓冲区时,都要分配相应的重叠结构。