C++ 如何使用IOCP将用户定义的数据传递给工作线程?

C++ 如何使用IOCP将用户定义的数据传递给工作线程?,c++,iocp,user-defined-types,worker-thread,C++,Iocp,User Defined Types,Worker Thread,嘿。。。我使用I/O完成端口和winsock创建了一个小型测试服务器。 我可以成功连接套接字句柄并将其与完成端口关联。 但我不知道如何将用户定义的数据结构传递到wroker线程中 到目前为止,我尝试将用户结构作为(ULONG_PTR)&结构作为CreateIoCompletionPort()的关联调用中的完成键传递 但这并不奏效 现在,我尝试定义自己的重叠结构并使用CONTAINING_RECORD(),如下所述。 但这也不起作用。(pHelper的内容值异常) 所以我的问题是:如何使用WSAR

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

到目前为止,我尝试将用户结构作为
(ULONG_PTR)&结构作为
CreateIoCompletionPort()的关联调用中的完成键传递
但这并不奏效

现在,我尝试定义自己的重叠结构并使用CONTAINING_RECORD(),如下所述。 但这也不起作用。(pHelper的内容值异常)

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

编辑:如何成功传输“每连接数据”?。。。似乎我做这件事的艺术(如上面两个链接中所述)弄错了。

下面是我的代码:(是的,它很难看,也是唯一的测试代码)

struct-helper
{
插座m_短袜;
无符号整数m_键;
重叠;
};
///////
套接字newSock=无效的\u套接字;
WSABUF wsabuffer;
char-cbuf[250];
wsabuffer.buf=cbuf;
wsabuffer.len=250;
德沃德旗,bytesrecvd;
while(true)
{
newSock=accept(AcceptorSock,NULL,NULL);
if(newSock==无效的_套接字)
ErrorAbort(“无法接受连接”);
//将套接字与CP关联
if(CreateIoCompletionPort((HANDLE)newSock,hCompletionPort,3,0)!=hCompletionPort)
ErrorAbort(“与连接相关的端口错误”);
其他的
cout m_key=3;
pHelper->m_sock=newSock;
memset(&(pHelper->over),0,sizeof(重叠);
flags=0;
bytesrecvd=0;
if(WSARecv(newSock,&wsabuffer,1,NULL,&flags,(重叠*)pHelper,NULL)!=0)
{
如果(WSAGetLastError()!=WSA\u IO\u挂起)
错误中止(“WSARecv不工作”);
}
}
//清理
闭合手柄(H完成端口);
cin.get();
返回0;
}
DWORD WINAPI ThreadProc(句柄h)
{
DWORD dwNumberOfBytes=0;
重叠*pOver=nullptr;
helper*pHelper=nullptr;
WSABUF RecvBuf;
char-cBuffer[250];
RecvBuf.buf=cBuffer;
RecvBuf.len=250;
DWORD dwRecvBytes=0;
DWORD dwFlags=0;
ULONG_PTR键=0;
GetQueuedCompletionStatus(h、&dwNumberOfBytes、&Key、&pOver、INFINITE);
//提取助手
pHelper=(helper*)包含_记录(pOver,helper,over);

cout如果像这样通过结构,它应该可以正常工作:

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的一个经常被滥用的功能是,它们不复制重叠结构,只使用提供的结构-因此从GetQueuedCompletionStatus返回的重叠结构指向最初提供的结构。因此:

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

if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, &pHelper->m_over, NULL) != 0)
请再次注意,在原始示例中,您的铸造错误。(重叠*)pHelper正在传递指向helper结构开头的指针,但重叠部分是最后声明的。我将其更改为传递实际重叠部分的地址,这意味着代码在编译时没有强制转换,这让我们知道我们正在做正确的事情。我还将重叠结构移动为结构的第一个成员。

要捕获另一端的数据,请执行以下操作:

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

在这方面,重叠结构是helper结构的第一个成员尤其重要,因为这样可以很容易地从api提供给我们的重叠*和我们实际需要的helper*中回溯出来。

您可以通过将自己的专用数据发送到完成端口

I/O完成数据包将满足 一个未解决的问题 GetQueuedCompletionStatus函数。 此函数返回三个 作为第二个、第三个、, 和调用的第四个参数 PostQueuedCompletionStatus。系统 不使用或验证这些值。 特别是,LPS重叠 参数不必指向 重叠结构


我使用标准套接字例程(socket、closesocket、bind、accept、connect…)为I/O创建/销毁和读/写文件,因为它们允许使用重叠结构

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

您需要检查GetQueuedCompletionStatus是否返回了完成或超时。有了超时,您可以运行索引结构,查看(例如)其中一个是否已超时或其他情况,并采取适当的内部管理操作

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

为IOCP提供服务的函数应该是一个单独的多线程实体。使用与系统中的内核数量相同的线程,或者至少不超过此数量,因为它会浪费系统资源(您没有比系统中的内核数量更多的资源来为事件提供服务)
OVERLAPPED* pOver;
ULONG_PTR key;
if(GetQueuedCompletionStatus(h,&dw,&key,&pOver,INFINITE))
{
  // c cast
  helper* pConnData = (helper*)pOver;