【发布时间】:2014-02-10 07:23:18
【问题描述】:
我是一名 linux 程序员,最近参与了将基于 epoll 的客户端移植到 Windows 中,该客户端带有两个用 c 编写的文件描述符。
如您所知,在 linux 中使用 epoll 或 select(我知道 windows 支持 select,但它根本没有效率)您可以阻塞文件描述符,直到文件描述符
准备就绪,您可以知道它何时准备好写入和何时读取。
我查看了 Windows IOCP,在微软世界中重叠 io 听起来不错。
但在所有示例中,它都用于多客户端服务器,每个客户端的套接字都独立于其他套接字。
使用完成端口,可以为每个客户端创建一个completionKey结构,并在结构中放入一个变量,并在调用时读取
WSArecv 和 wirt 当 WSAsend 和其他变量指示套接字值并从 GetQueuedCompletionStatus 检索它们以知道要做什么时,如果对套接字进行了写入,则进行读取,反之亦然。
但就我而言,文件描述符 (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->status 将是错误的(例如,它将报告读取而不是写入),最糟糕的是缓冲区将被覆盖。如果我使用锁定
completionKeys,会导致死锁的情况。
查看 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 作为第一个文件。
现在你解决我的其他问题;)
【问题讨论】:
-
出于好奇,Linux端对应的问题是怎么处理的?也就是说,当您在套接字 A 上读取请求并且(因此)想要写入套接字 B,但套接字 B 还没有准备好写入时?
-
实际上我有一个队列,我只是写到队列中,然后尝试出列并在任何可能的时候写入。
-
好的,那么每个套接字只需要两个 OVERLAPPED 结构,然后呢?一个用于单个挂起的读取操作,一个用于单个挂起的写操作?
-
我试图在你的回答下解释。在我看来,仅靠排队是无法完成的。排队正好可以写。对于阅读,我需要盲目地从开关块中读出,并且需要 GetQueuedCompletionStatus 不是 INFINITE。而且看起来有点乱
-
考虑到所有两个重叠的结构都可能在套接字上挂起
标签: c windows iocp overlapped-io