2

嘿...我使用 I/O 完成端口和 winsock 创建了一个小型测试服务器。我可以成功连接套接字句柄并将其与完成端口相关联。但我不知道如何将用户定义的数据结构传递到工作线程......

到目前为止,我尝试的是(ULONG_PTR)&structure as在关联调用中将用户结构作为完成键传递,CreateIoCompletionPort() 但这不起作用。

现在我尝试定义自己的 OVERLAPPED 结构并使用 CONTAINING_RECORD(),如此处所述http://msdn.microsoft.com/en-us/magazine/cc302334.aspxhttp://msdn.microsoft.com/en-us /杂志/bb985148.aspx。但这也行不通。(我得到 pHelper 内容的怪异值)

所以我的问题是:如何使用 WSARecv()、GetQueuedCompletionStatus() 和 Completion 数据包或 OVERLAPPED 结构将数据传递给工作线程?

编辑:我怎样才能成功传输“每个连接数据”?...似乎我做错了艺术(如上面两个链接中所述)。

这是我的代码:(是的,它很丑,也是唯一的测试代码)

struct helper
    {
        SOCKET m_sock;
        unsigned int m_key;
        OVERLAPPED over;
    };


///////

SOCKET newSock = INVALID_SOCKET;
    WSABUF wsabuffer;
    char cbuf[250];
    wsabuffer.buf = cbuf;
    wsabuffer.len = 250;
    DWORD flags, bytesrecvd;


    while(true)
    {
        newSock = accept(AcceptorSock, NULL, NULL);
        if(newSock == INVALID_SOCKET)
            ErrorAbort("could not accept a connection");

        //associate socket with the CP
        if(CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, 3,0) != hCompletionPort)
            ErrorAbort("Wrong port associated with the connection");
        else
            cout << "New Connection made and associated\n";

        helper* pHelper = new helper;
        pHelper->m_key = 3;
        pHelper->m_sock = newSock;
        memset(&(pHelper->over), 0, sizeof(OVERLAPPED));
        flags = 0;
        bytesrecvd = 0;

        if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, (OVERLAPPED*)pHelper, NULL) != 0)
        {
            if(WSAGetLastError() != WSA_IO_PENDING)
                ErrorAbort("WSARecv didnt work");
        }
    }

    //Cleanup
    CloseHandle(hCompletionPort);
    cin.get();
    return 0;
}

DWORD WINAPI ThreadProc(HANDLE h)
{
    DWORD dwNumberOfBytes = 0;
    OVERLAPPED* pOver = nullptr;
    helper* pHelper = nullptr;
    WSABUF RecvBuf;
    char cBuffer[250];
    RecvBuf.buf = cBuffer;
    RecvBuf.len = 250;
    DWORD dwRecvBytes = 0;
    DWORD dwFlags = 0;
    ULONG_PTR Key = 0;

    GetQueuedCompletionStatus(h, &dwNumberOfBytes, &Key, &pOver, INFINITE);

    //Extract helper
    pHelper = (helper*)CONTAINING_RECORD(pOver, helper, over);


    cout << "Received Overlapped item" << endl;
    if(WSARecv(pHelper->m_sock, &RecvBuf, 1, &dwRecvBytes, &dwFlags, pOver, NULL) != 0)
        cout << "Could not receive data\n";
    else
        cout << "Data Received: " << RecvBuf.buf << endl;

    ExitThread(0);
}
4

3 回答 3

4

如果你像这样传递你的结构,它应该可以正常工作:

helper* pHelper = new helper;
CreateIoCompletionPort((HANDLE)newSock, hCompletionPort, (ULONG_PTR)pHelper,0);
...


helper* pHelper=NULL;
GetQueuedCompletionStatus(h, &dwNumberOfBytes, (PULONG_PTR)&pHelper, &pOver, INFINITE);

编辑以添加每个 IO 数据:

异步 API 经常被滥用的特性之一是它们不复制 OVERLAPPED 结构,它们只是使用提供的结构 - 因此从 GetQueuedCompletionStatus 返回的重叠结构指向最初提供的结构。所以:

struct helper {
  OVERLAPPED m_over;
  SOCKET     m_socket;
  UINT       m_key;
};

if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, &pHelper->m_over, NULL) != 0)

请再次注意,在您的原始样本中,您的投射错误。(OVERLAPPED*)pHelper 传递了一个指向辅助结构的 START 的指针,但最后声明了 OVERLAPPED 部分。我将其更改为传递实际重叠部分的地址,这意味着代码在没有强制转换的情况下编译,这让我们知道我们正在做正确的事情。我还将重叠的结构移动为结构的第一个成员。

要捕获另一端的数据:

OVERLAPPED* pOver;
ULONG_PTR key;
if(GetQueuedCompletionStatus(h,&dw,&key,&pOver,INFINITE))
{
  // c cast
  helper* pConnData = (helper*)pOver;

在这一方面,重叠结构是辅助结构的第一个成员尤为重要,因为这样可以很容易地从 API 给我们的 OVERLAPPED* 以及我们真正想要的辅助* 中回滚。

于 2010-12-03T10:24:40.020 回答
2

您可以通过PostQueuedCompletionStatus将您自己的特殊用途数据发送到完成端口。

I/O 完成数据包将满足对 GetQueuedCompletionStatus 函数的未完成调用。此函数返回作为调用 PostQueuedCompletionStatus 的第二个、第三个和第四个参数传递的三个值。系统不使用或验证这些值。特别是,lpOverlapped 参数不需要指向 OVERLAPPED 结构。

于 2010-12-03T12:01:19.767 回答
0

我使用标准套接字例程(socket、closesocket、bind、accept、connect ...)来创建/销毁,并使用 ReadFile/WriteFile 进行 I/O,因为它们允许使用 OVERLAPPED 结构。

在您的套接字接受或连接后,您应该将它与它所服务的会话上下文相关联。然后将您的套接字关联到一个 IOCP 并(在第三个参数中)为它提供对会话上下文的引用。IOCP 不知道这个引用是什么,也不关心这个问题。该引用供您使用,因此当您通过 GetQueuedCompletionStatus 获得 IOC 时,参数 3 指向的变量将使用该引用填充,以便您立即找到与套接字事件关联的上下文并开始为该事件提供服务。我通常使用包含(除其他外)套接字声明、重叠结构以及其他特定于会话的数据的索引结构。

您需要检查 GetQueuedCompletionStatus 是否返回完成或超时。通过超时,您可以遍历索引结构并查看(例如)其中一个是否超时或其他原因,并采取适当的内务处理措施。

还需要检查重叠结构以查看 I/O 是否正确完成。

服务于 IOCP 的函数应该是一个单独的多线程实体。使用与系统中的内核数相同的线程数,或者至少不超过这个数,因为它会浪费系统资源(您没有比系统中的内核数更多的资源来服务事件,对吧?) .

IOCP 确实是世界上最好的(好得令人难以置信),任何说“每个套接字一个线程”或“在一个函数中等待多个套接字列表”的人都不知道他们在说什么。前者强调你的调度程序,后者是轮询,轮询总是非常浪费。

于 2010-12-14T08:06:47.473 回答