读书人

IOCP的接发数据的类型有关问题?(思路求

发布时间: 2012-08-21 13:00:21 作者: rapoo

IOCP的接发数据的类型问题?(思路求帮助)
问题简述:IOCP模型中,能通过OVERLAPPED这个首地址来知道自定义结构体的地址,但在WSASend和WSARecv中发送的发送的好像都是WSABuf,IOCP是如何通过几个API来知道传送的是什么结构的数据,如何传送的时候将整个这个自定义结构传输过去client的呢?

问题详述:(举例说明)在IOCP中,传送数据用到的API主要有三个:
API:

C/C++ code
int WSASend(  SOCKET s,  LPWSABUF lpBuffers,  DWORD dwBufferCount,  LPDWORD lpNumberOfBytesSent,  DWORD dwFlags,  LPWSAOVERLAPPED lpOverlapped,  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);int WSARecv(  SOCKET s,  LPWSABUF lpBuffers,  DWORD dwBufferCount,  LPDWORD lpNumberOfBytesRecvd,  LPDWORD lpFlags,  LPWSAOVERLAPPED lpOverlapped,  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);BOOL GetQueuedCompletionStatus(  HANDLE CompletionPort,  LPDWORD lpNumberOfBytes,  PULONG_PTR lpCompletionKey,  LPOVERLAPPED* lpOverlapped,  DWORD dwMilliseconds);


假如我现在有自定义数据类型,例子1:
C/C++ code
typedef struct _Client_Context{    SOCKET            IoSocket;    HANDLE            hComPort;    SOCKADDR_IN        LocalAddr;    SOCKADDR_IN        RemoteAddr;    SOCKET_STATUS    SocketStatus;    LPFN_ACCEPTEX    pAcceptEx;    LPFN_CONNECTEX    pConnectEx;    LPFN_GETACCEPTEXSOCKADDRS pGetAcceptExSockaddrs;    struct _Client_Context*    pListenContext;    LPVOID            pFrontMemory;//链表管理    LPVOID            pNextMemory;//链表管理}CLIENT_CONTEXT,*LPCLIENT_CONTEXT;typedef struct _IO_CONTEXT{    WSAOVERLAPPED        ol;    LPCLIENT_CONTEXT    pClientContext;    BYTE                 IoOperation;    WSABUF                wsabuf;    char                buf[MAX_BUF];    LPVOID                pFrontMemory;//链表管理    LPVOID                pNextMemory;//链表管理}IO_CONTEXT,*LPIO_CONTEXT;

然后,我发送数据是通过以下这种方法来发送:send
C/C++ code
WSASend( pclient->IoSocket,&pio->wsabuf,1,&pio->wsabuf.len,Flags,&(pio->ol),NULL);

申请接收数据请求是通过:recv
C/C++ code
WSARecv(pclient->IoSocket,&(pIoContext->wsabuf),1,&pIoContext->wsabuf.len,&flag,&pIoContext->ol,NULL);

获取数据是:(get From IOCP)
C/C++ code
GetQueuedCompletionStatus( comPort,&dwByteTrans,(LPDWORD)&pClientContext,(LPOVERLAPPED*)&pIoContext,1000 );

这样写的代码,然后,问题就是:
为什么Send和Recv的时候Send/Recv的数据是WSABuf,但在GetQueuedCompletionStatus中我可以直接使用LPOVERLAPPED来获取它的首地址,获取完首地址还能直接(有些例子是还需要进行CONTAINING_RECORD进行获取)使用pIoContext中的数据,再说浅白一点就是,Send/Recv的是char*和LPOVERLAPPED,但是,在接收的时候就变成了_IO_CONTEXT整个结构体了?(PS:知道LPOVERLAPPED的首地址就是结构体的首地址的作用)例子上在进行Get...操作后就直接switch(pIoContext->某些成员)了?
应该怎么去理解这个呢?应该怎么才能send/Recv自己定义的struct呢?

因为怕自己表述得不好,再自定义数据类型,例子2:
C/C++ code
typedef struct _PER_IO_DATA{ OVERLAPPED ol; char buf[BUFFER_SIZE]; int nOperationType;}PER_IO_DATA, *PPER_IO_DATA;

receive:
C/C++ code
   PPER_IO_DATA pPerIO = (PPER_IO_DATA)::GlobalAlloc(GPTR, sizeof(PER_IO_DATA));WSARecv(s, &buf, 1, &dwRecv, &dwFlags, &pPerIO->ol, NULL);

get:
C/C++ code
   PPER_IO_DATA pPerIO;GetQueuedCompletionStatus(hCompletion, &dwTrans, (LPDWORD)&pPerHandle, (LPOVERLAPPED*)&pPerIO, WSA_INFINITE);   switch(pPerIO->nOperationType)... 


还是同样的问题,PER_IO_DATA的数据是怎样通过IOCP将WSASend/WSARecv的char*类型转变为PER_IO_DATA类型的呢?

[size=18px]
我在SEND/RECV时如何能直接SEND/RECV自己定义的PER_IO_DATA的数据呢?特别是接收的时候GetQueuedCompletionStatus是怎么将数据转换过来的呢?[/size]

寻求过的解决方法:
弄了这个问题也有一个多月了,还是受这个问题困扰~
从网上看过好些demo,感觉基本上在接收和发送的时候都是一样的调用WSASend和WSARecv,传送的时候只是传送WSABuf类型的数据,而在GetQueuedCompletionStatus的时候没有做过什么显示的转换,就可以将从LPOVERLAPPED获取的指针变为自定义结构的指针,而且可以直接使用刚刚改变的指针的数据了,不解。

从网上找资料,与这问题较接近的而又有些帮助的,看过以下这些贴子,不过感觉自己的问题还是未能得到解决。
困扰多日的问题:IOCP如何通过GetQueuedCompletionStatus获取PER_IO_DATA数据?
http://topic.csdn.net/u/20120720/10/d730bd8c-3d64-4c12-b54d-d5f56ad4eed3.html
IOCP非常疑惑的问题:GetQueuedCompletionStatus到底是如何知道是发送还是接收的?
http://topic.csdn.net/u/20100604/19/019356eb-21c0-42b0-8c9c-2dc4b7263477.html


[解决办法]
IOCP中可以自己定义两个重要的数据结构,一个PRE_IO,一个PER_HANDLE结构,pre_I/O保存与IO相关的数据,比如当前是读,是写还是其他,pre-HANDLE可以保存与当前socket相关的东西,比如SOCKET句柄,客户端的SOCKADDR_IN地址等信息。
[解决办法]
你认真点,就可以看出
WSASend和WSARecv的LPWSAOVERLAPPED lpOverlapped参数
GetQueuedCompletionStatus的LPOVERLAPPED* lpOverlapped参数
[解决办法]
探讨

引用:
你认真点,就可以看出
WSASend和WSARecv的LPWSAOVERLAPPED lpOverlapped参数
GetQueuedCompletionStatus的LPOVERLAPPED* lpOverlapped参数


嗯,是有 LPOVERLAPPED* 参数,不过仅知道它有个类似火车头(可以获取自定义类型在堆中首地址)的作用,它好像没有辨认数据……

[解决办法]
PRE_IO的地址是不变的。明白了是什么意思了么?
[解决办法]
你自己从WSAOVERLAPPED派生一个WSAOVERLAPPEDEX,在GetQueuedCompletionStatus的时候,把传给你的LPWSAOVERLAPPED强传为WSAOVERLAPPEDEX*不就行了,你可以添加你想的数据到WSAOVERLAPPEDEX之中,想要任何信息都可以添加进去。
[解决办法]
探讨

对了,那想顺便问一下,那如果我send的时候将buffer缓冲区也send过去了(WSASend的第二第四个参数),不过我想从GetQueuedCompletionStatus接收这个缓冲区的时候该在哪里接收呢?因为没有很直观地给出对应的形参可以获取,所以有点不解。


[解决办法]
探讨

引用:
有没有留意到你的代码里面:
typedef struct _IO_CONTEXT
{
……
明白了?
另外还有一点要注意的,就是虚函数.如果带有虚函数,类里面第一个元素的地址并不等于类的地址!!


非常感谢firendlys的回答~ 明白了很多,第一,三个问题都明白了~

嘿嘿,不过从第一个问题的回答中又衍生了一个新的问题:
C/C++ co……

[解决办法]
其实overlapped(自定义)结构并没有进行内存拷贝的
只是简单的将地址指针传递过去
->
你调用WSASend时,传一个OVERLAPPED指针,在GetQueuedCompletionStatus,系统又把这个指针回传给了你。

所以,要保证OVERLAPPED的有效性,其次,正是因为是指针操作(否则,如果是拷贝的话,就只会拷贝OVERLAPPED,你的OVERLAPPEDEX多出来的内容并不会拷贝,因为WSASend只接收OVERLAPPED指针,你传的OVERLAPPEDEX*它仍然是当成OVERLAPPED*的),所以可以像我上面说的,把OVERLAPPED*强转为OVERLAPPEDEX*
[解决办法]
WSASend和GetQueuedCompletionStatus工作在同一端。

你GetQueuedCompletionStatus得到一个OVERLAPPEDEX,才知道发送了多少字节,还有多少字节没发送完(以便重新投递),哪个socket正在触发这个事件等等。

WSASend和WASRecv是请求系统执行一个异步IO,GetQueuedCompletionStatus是得到异步IO的结果。

所以说,客户端是WSASend加GetQueuedCompletionStatus,服务端是
WSARecv加GetQueuedCompletionStatus
[解决办法]
探讨

换句话说,为什么它经过了转换也能正确传输数据呢?

读书人网 >VC/MFC

热点推荐