繁体   English   中英

Windows中相关的重叠io的编码模式

[英]Coding pattern for dependant overlapped io in windows

我是一名Linux程序员,最近参与了将带有两个用c编写的文件描述符的epoll客户端移植到Windows的工作。
如您所知,在Linux中使用epoll或select(我知道Windows支持select,但根本没有效率),您可以阻止文件描述符,直到文件描述符准备就绪,并且可以知道何时可以写入和读取。

我看过Windows IOCP,对于Microsoft世界中重叠的io来说还可以。 但是在所有示例中,它都用于多客户端服务器,每个客户端的套接字都独立于其他套接字。

使用完成端口,可以为每个客户端创建一个完成密钥结构,并将一个变量放入struct中,并在调用WSArecv时读取它,并在WSAsend和另一个指示套接字值的变量从wirt读取,并从GetQueuedCompletionStatus检索它们以了解操作时,如果对套接字完成了写入操作,请进行读取,反之亦然。

但就我而言,文件描述符(fd)实际上是重叠的。 从一个fd读取数据会导致对另一个fd的读取和写入,这使得很难知道GetQueuedCompletionStatus结果中的每个fd实际发生了什么操作,因为每个fd都有一个完成密钥。 为了清楚起见,请考虑以下事项:

有两个称为fd1和fd2的句柄,completionKey1持有f1的句柄和状态,以及fd2的completedKey2,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;
    }

在上面的代码中,出现了这样一种情况,即某些更改的完成密钥将覆盖未决的读取或写入,并且结果的完成密钥->状态将是错误的(例如,它将报告读取而不是写入),并且最糟糕的是缓冲区将被覆盖。 如果我对完成键使用锁定,则将导致死锁情况。

查看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放在第一个文件中即可。 现在你解决了我其他人;)

您实际上是在问两个不同的问题。 我无法回答第一个问题,因为我从未使用过IO完成端口,但是从我读过的所有内容来看,除专家外,最好避免使用它们。 (我将为您认为正在描述的问题指出一个显而易见的解决方案:与其将数据实际写入另一个套接字,而不是在另一个写入仍处于挂起状态,而是将数据放入队列中,然后再写入。您仍然必须在给定的套接字上处理两个同时的操作-一个读和一个写-但这不应该是一个问题。)

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

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

再铸造LPWSAOVERLAPPED发送到完成例程lpMyOverlapped访问您的上下文数据。

或者,如果使用的是完成例程,则确保WSAOVERLAPPEDhEvent成员未使用,因此可以将其设置为指向您选择的结构的指针。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM