如何使用IOCP将用户定义的数据传递到工作线程?
-
30-09-2019 - |
题
嘿...我使用I/O完成端口和Winsock创建了一个小型测试服务器。我可以成功地将插座手柄与完成端口联系起来。但是我不知道如何将用户定义的数据结构传递到Wroker线程...
到目前为止,我已经尝试的是通过用户结构 (ULONG_PTR)&structure as
协会通话中的完成密钥 CreateIoCompletionPort()
但这无效。
现在,我尝试定义自己的重叠结构,并使用containing_record()如下所述 http://msdn.microsoft.com/en-us/magazine/cc302334.aspx 和 http://msdn.microsoft.com/en-us/magazine/bb985148.aspx。但这也行不通。 (我得到了菲尔珀内容的怪异值)
因此,我的问题是:如何使用wsarecv(),getQueuedCompletionStatus()和完成数据包或重叠 - 解决方案将数据传递到工作线程?
编辑:我如何成功传输“每连接数据”?...似乎我有这样做的艺术(就像上面两个链接中的解释)错误。
这里是我的代码:(是的,它的丑陋和唯一的测试代码)
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);
}
解决方案
您可以通过 PastqueCompletionStatus.
I/O完成数据包将满足对GetQueuedCompletionStatus函数的出色呼叫。该函数以三个值传递的三个值返回,作为呼叫呼叫的第二,第三和第四参数。该系统不使用或验证这些值。特别是,lpoverlaped参数不必指向重叠的结构。
其他提示
如果您像这样通过结构,应该可以正常工作:
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的经常滥用功能之一是它们不复制重叠的结构,它们只是使用提供的一个 - 因此,从GetQuequeudCompletionStatus点返回的重叠结构点到了最初提供的结构。所以:
struct helper {
OVERLAPPED m_over;
SOCKET m_socket;
UINT m_key;
};
if(WSARecv(newSock, &wsabuffer, 1, NULL, &flags, &pHelper->m_over, NULL) != 0)
请注意,再次,在原始样本中,您弄错了铸造。 (重叠*)菲尔珀(Phelper)将指针传递到了辅助架结构的开始,但重叠的部分被宣布为最后一次。我更改了它以传递实际重叠部分的地址,这意味着代码不带铸件编译,这使我们能够 知道 我们正在做正确的事情。我还将重叠的结构移至结构的第一个成员。
在另一侧捕获数据:
OVERLAPPED* pOver;
ULONG_PTR key;
if(GetQueuedCompletionStatus(h,&dw,&key,&pOver,INFINITE))
{
// c cast
helper* pConnData = (helper*)pOver;
在这方面,重叠的结构是辅助结构的第一个成员,这一点尤其重要,因为这使得从API给我们的重叠*和我们实际想要的助手*的助手*中轻松降回。
我使用标准套接字例程(套接字,关闭,绑定,接受,连接...)来创建/破坏和读取I/O,因为它们允许使用重叠的结构。
插座接受或连接后,您应将其与IT服务的会话上下文相关联。然后,将套接字关联到IOCP,(在第三个参数中)向其提供了对会话上下文的引用。 IOCP不知道该参考是什么,也不关心这一点。该引用是供您使用的,因此当您通过GetQueuedCompletionStatus获得IOC时,参数3指向的变量将被参考填充,以便您立即找到与套接字事件相关的上下文,并可以开始为事件提供服务。我通常使用索引结构,其中包含(除其他事项)插座声明,重叠结构以及其他特定于会话的数据。参数i传递到参数3中创建的iococteptionport将是包含插座的结构成员的索引。
您需要检查GetQueuedCompletionStatus是否返回完成或超时。有了超时,您可以通过索引结构进行操作,例如(例如),如果其中一个已经计时或其他东西并采取适当的房屋保管措施。
还需要检查重叠的结构,以查看I/O正确完成。
服务IOCP的功能应为一个单独的多线程实体。使用与系统中有内核相同数量的线程,或者至少不超过它,因为它浪费了系统资源(您没有比系统中的内核数量更多的资源来维修事件,对吗? 。
IOCP确实是所有世界中最好的(太好了),任何人说“每个插座”或“在一个函数中的多个插座列表上等待”的人都不知道他们在说什么。前者强调您的调度程序,后者进行了投票,投票总是极为浪费。