【问题标题】:GetQueuedCompletionStatus - How to read clients messages without any socket structGetQueuedCompletionStatus - 如何在没有任何套接字结构的情况下读取客户端消息
【发布时间】:2021-08-15 23:57:48
【问题描述】:

我正在尝试在 Windows 中使用 C 来学习 IOCP。这个想法是拥有一个非阻塞服务器,其中包含处理客户端消息的线程。

我现在明白如何接受客户等了。

但我还是不明白如何从GetQueuedCompletionStatus()读取每个客户的消息并打印出来。

我在网上看到所有人都通过GetQueuedCompletionStatus()使用LPOVERLAPPED参数传递一个结构来引用它,但是没有任何结构怎么办?

谁能告诉我这是否可行,并举个小例子?

【问题讨论】:

  • 为什么要避免使用 per-socket 结构?每个缓冲区结构都可以有一个指向套接字结构的字段,没问题,(很像下面的 Remy 详细信息)。几乎不可避免的是,在您开发服务器时,您最终将获得更多的每个套接字数据,而不仅仅是套接字描述符(例如,用于识别处理程序线程中的缓冲区顺序的递增序列号的记录)。跨度>

标签: c windows winsock2 iocp


【解决方案1】:

您必须跟踪传递给WSARecv() 等的缓冲区。您可以通过将OVERLAPPED 与每个缓冲区相关联来做到这一点。当GetQueuedCompletionStatus() 告诉您哪个OVERLAPPED 已完成时,您因此知道相关的缓冲区已准备好。

这通常通过定义structOVERLAPPED 作为其第一个成员来处理,因此指向OVERLAPPED 的指针也等于指向struct 的指针。然后,您可以在 struct 中添加您想要的任何其他详细信息,例如缓冲区、套接字等,无论您的应用需要什么来处理数据。

例如:

enum IocpOp { opRead, opWrite };

typedef struct
{
    WSAOVERLAPPED ov;
    IocpOp opCode;
    SOCKET sckt;
    BYTE buffer[256];
    DWORD numBytesInBuffer;
} MYIOCPINFO, *PMYIOCPINFO;

void IoComplete(PMYIOCPINFO io, DWORD errorCode, DWORD numBytesTransferred)
{
    if (errorCode != ERROR_SUCCESS)
    {
        // handle failure as needed ...
        free(io);
        return;
    }

    switch (io->opCode)
    {
        case opRead:
            // use io->buffer up to numBytesTransferred as needed ...
            break;

        case opWrite:
            if (numBytesTransferred < io->numBytesInBuffer)
            {
                info->numBytesInBuffer -= numBytesTransferred;
                memmove(io->buffer, io->buffer + numBytesTransferred, io->numBytesInBuffer);

                WSABUF buf;
                buf.len = io->numBytesInBuffer;
                buf.buf = (CHAR*) io->buffer;

                if (WSASend(io->sckt, &buf, 1, NULL, 0, &(io->ov), NULL) == SOCKET_ERROR)
                {
                    if (WSAGetLastError() != WSA_IO_PENDING)
                    {
                        // handle failure as needed ...
                        free(io);
                    }
                }

                return;
            }

            break;
        }

        // reuse io for the next I/O operation on io->sckt
        // or free io now if done using it ...
        free(io);
    }
}

...

PMYIOCPINFO io = malloc(sizeof(MYIOCPINFO));
if (!io) ...
io->opCode = opRead;
io->sckt = sckt;

WSABUF buf;
buf.len = sizeof(io->buffer);
buf.buf = (CHAR*) io->buffer;

DWORD dwFlags = 0;
if (WSARecv(sckt, &buf, 1, NULL, &dwFlags, &(io->ov), NULL) == SOCKET_ERROR)
{
    if (WSAGetLastError() != WSA_IO_PENDING)
        IoComplete(io, WSAGetLastError(), 0);
}

...

PMYIOCPINFO io = malloc(sizeof(MYIOCPINFO));
if (!io) ...
io->opCode = opWrite;
io->sckt = sckt;

// fill io->buffer as needed ...
io->numBytesInBuffer = ...;

WSABUF buf;
buf.len = io->numBytesInBuffer;
buf.buf = (CHAR*) io->buffer;

if (WSASend(sckt, &buf, 1, NULL, 0, &(io->ov), NULL) == SOCKET_ERROR)
{
    if (WSAGetLastError() != WSA_IO_PENDING)
        IoComplete(io, WSAGetLastError(), 0);
}

...

DWORD dwBytesTransferred = 0;
ULONG_PTR ulCompletionKey = 0;
LPOVERLAPPED lpOverlapped = NULL;

if (GetQueuedCompletionStatus(hIOCP, &dwBytesTransferred, &ulCompletionKey, &lpOverlapped, INFINITE))
{
    if (ulCompletionKey == MySocketIOCPKey) // specified in CreateIOCompletionPort()...
    {
        IoComplete((PMYIOCPINFO)lpOverlapped, ERROR_SUCCESS, dwBytesTransferred);
    }
    else
    {
        ...
    }
}
else
{
    if (lpOverlapped)
    {
        // I/O operation belonging to ulCompletionKey failed...

        if (ulCompletionKey == MySocketIOCPKey) // specified in CreateIOCompletionPort()...
        {
            IoComplete((PMYIOCPINFO)lpOverlapped, GetLastError(), dwBytesTransferred);
        }
        else
        {
            ...
        }
    }
    else
    {
        // GetQueuedCompletionStatus() itself failed...
    }
}

【讨论】:

  • 当我们将socket绑定到iocp时不需要初始化info-&gt;ov.hEvent。所以电话WSACreateEventWSACloseEvent 可以(并且必须)被丢弃。我认为设计错误在MYIOCPINFO 内有sckt。真的,我们必须有每个套接字的结构,这里有 (SOCKET sckt;) 并且对于每个 I/O 操作 - 分配唯一的 MYIOCPINFO 其中必须是(引用)每个套接字结构的指针
  • 可能是我的英语不好。我的意思是SOCKET sckt;MYIOCPINFO 内部有MY_PER_SOCKET_DATA* pObj; 并且已经在MY_PER_SOCKET_DATA 内部有SOCKET sckt; 我也将创建公共void OnIoComplete(PMYIOCPINFO, ULONG dwError); proc 并在I/O 操作失败或GetQueuedCompletionStatus 之后直接调用它在一个地方处理这个(比如 free(info); )
  • 需要参考 SOCKET。无论是直接完成,还是通过指向其他持有 SOCKET 的指针完成,都是实现细节 - 是的,同意。只是我认为需要有 2 个独立的结构 - 每个套接字一个,每个 I/O 一个
  • @RbMm 我知道你的意思。 OVERLAPPED 结构是直接引用实际的SOCKET,还是具有指向具有SOCKET 的其他东西的指针,是一个实现细节。无论哪种方式都有效。我以前两种方式都做过。
  • 是的,我的错误 :) void IoComplete(PMYIOCPINFO io, ULONG errorCode, DWORD dwNumBytes ); 更正确,并将其称为 IoComplete(info, GetLastError(), 0);IoComplete(info, NOERROR, dwNumBytes );
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-08
  • 1970-01-01
相关资源
最近更新 更多