嘿......我使用I / O完成端口和winsock创建了一个小型测试服务器。 我可以成功连接套接字句柄并将其与完成端口相关联。 但我不知道如何将用户定义的数据结构传递给wroker线程......
我到目前为止所尝试的是在(ULONG_PTR)&structure as
的关联调用中将用户结构作为CreateIoCompletionPort()
完成密钥传递
但那没用。
现在我尝试定义我自己的OVERLAPPED结构并使用CONTAINING_RECORD(),如此处http://msdn.microsoft.com/en-us/magazine/cc302334.aspx和http://msdn.microsoft.com/en-us/magazine/bb985148.aspx所述。 但这也行不通。 (我得到了pHelper含量的奇怪值)
所以我的问题是:如何使用WSARecv(),GetQueuedCompletionStatus()和Completion数据包或OVERLAPPED-strucutre将数据传递给工作线程?
编辑:如何成功传输“每个连接数据”?...我觉得这样做的艺术(如上面两个链接中所解释的那样)是错误的。 < / p>
这是我的代码:(是的,它的丑陋和唯一的TEST代码)
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);
}
答案 0 :(得分: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数据:
异步apis经常被滥用的一个特性是它们不复制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 *和我们实际想要的帮助器*回退。 p>
答案 1 :(得分:2)
您可以通过PostQueuedCompletionStatus将自己的专用数据发送到完成端口。
I / O完成包将满足 一个出色的电话 GetQueuedCompletionStatus函数。 此函数返回三个 值作为第二个,第三个, 和调用的第四个参数 PostQueuedCompletionStatus。系统 不使用或验证这些值。 特别是lpOverlapped 参数不必指向一个 重叠的结构。
答案 2 :(得分:0)
我使用标准套接字例程(socket,closesocket,bind,accept,connect ...)来创建/销毁I / O的ReadFile / WriteFile,因为它们允许使用OVERLAPPED结构。
套接字接受或连接后,您应将其与其服务的会话上下文关联。然后将套接字关联到IOCP,并(在第三个参数中)为其提供对会话上下文的引用。 IOCP不知道这个引用是什么,也不关心这个问题。引用是供您使用的,这样当您通过GetQueuedCompletionStatus获取IOC时,参数3指向的变量将被引用填充,以便您立即找到与套接字事件关联的上下文并可以开始为事件提供服务。我通常使用索引结构,其中包含(除其他外)套接字声明,重叠结构以及其他特定于会话的数据。我在参数3中传递给CreateIoCompletionPort的引用将是包含套接字的结构成员的索引。
您需要检查GetQueuedCompletionStatus是否返回完成或超时。如果超时,您可以浏览索引结构并查看(例如)其中一个是否已超时或采取其他措施并采取适当的内务处理措施。
还需要检查重叠结构,以确保I / O正确完成。
为IOCP提供服务的功能应该是一个单独的多线程实体。使用与系统中具有内核相同数量的线程,或至少不超过系统资源浪费的线程(您没有更多资源来处理事件而不是系统中的内核数量,对吧?)
IOCP确实是所有世界中最好的(太好了不可能),任何说“每个插槽一个线程”或“在一个函数中等待多个套接字列表”的人都不知道他们在说什么。前者强调你的调度员,后者是民意调查,民意调查总是非常浪费。