IOCP 的WSARecv问题

何日人再来 2009-07-01 02:43:11
(为简化起见,1个数字对应1个x或- )

比如说将有如下数据被传送到达: 101xx102xxxx103xxxxxx

现在投递了5个WSARecv(),其buf按顺序如下:


Recv1.buf Recv2.buf Recv3.buf Recv4.buf Recv5.buf

-------- | -------- | -------- | -------- | --------

理想情况下应该是这样:

Recv1.buf Recv2.buf Recv3.buf Recv4.buf Recv5.buf

101xx102 | xxxx103x | xxxxx--- | -------- | --------

问题一:会不会出现这种情况:

中间有的WSARecv没装满buf就返回完成了,余下数据被下个WSARecv接着收:
Recv1.buf Recv2.buf Recv3.buf Recv4.buf Recv5.buf

101xx102 | xxxx---- | 103xxxxx | x------- | --------

或者返回是乱序的?:

Recv2.buf Recv1.buf Recv3.buf Recv4.buf Recv5.buf

xxxx103x | 101xx102 | xxxxx--- | -------- | --------

问题二:这包该怎么拆啊?我想这样子处理包
WORD type;
type=取得包的类型;
switch(type)
{
case 101:On101();break;//处理101命令
case 102:On102();break;//处理102命令
...
}
有些不定长度的单个数据流比WSARecv()里准备的buf还大,该怎么办呢?
...全文
515 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
guhan010 2009-07-03
  • 打赏
  • 举报
回复
yes, anythig can happen. so ,it need you control how to send and how to receive.

but , windows API promiss send the date following the order which you send. so , don't worry, just do it.
何日人再来 2009-07-02
  • 打赏
  • 举报
回复
谢谢大家的帮忙,问题一基本解决了,我来补充一下:这个IOCP采用的是内存池+多线程,为了提高效率,所以开始一次就投递了5个WSARecv(),内存池是一个BUFF的链表,BUFF是固定大小的.接收后包序也排好了,就是拆包拼包问题没解决.大家再给点建议呀:


songtao_01 提出的方法不错,可是我要投递多个WSARecv()啊,能把你的解决的函数贴上来吗?


songtao_01 2009-07-02
  • 打赏
  • 举报
回复
上面写错了,不是发送缓冲区的代码,是接收缓冲区代码.
songtao_01 2009-07-02
  • 打赏
  • 举报
回复
不要投递多个WSARecv吧

缓冲区采用循环队列方式,以下是循环队列的代码

#ifndef _CIRCUITQUEUE_H_
#define _CIRCUITQUEUE_H_

#include <windows.h>
#include <stdio.h>
#include <assert.h>

template<typename T>
class CircuitQueue
{
public:
CircuitQueue() : m_pData( NULL ), m_nLength( 0 ), m_nSize( 0 ), m_nHead( 0 ), m_nTail( 0 )
{
InitializeCriticalSection( &m_cs );
}

virtual ~CircuitQueue()
{
if( m_pData ) delete [] m_pData;
DeleteCriticalSection( &m_cs );
}

void Create( int nSize, int nExtraSize = 0 )
{
EnterCriticalSection( &m_cs );
if( m_pData ) delete [] m_pData;

m_pData = new T[nSize + nExtraSize];
m_nSize = nSize;
m_nExtraSize = nExtraSize;
LeaveCriticalSection( &m_cs );
}

inline void Clear()
{
EnterCriticalSection( &m_cs );

m_nLength = 0;
m_nHead = 0;
m_nTail = 0;

LeaveCriticalSection( &m_cs );
}


inline int GetSpace()
{
int iRet;

EnterCriticalSection( &m_cs );
iRet = m_nSize - m_nLength;
LeaveCriticalSection( &m_cs );

return iRet;
}


inline int GetLength()
{
int iRet;

EnterCriticalSection( &m_cs );
iRet = m_nLength;
LeaveCriticalSection( &m_cs );

return iRet;
}


inline int GetBackDataCount()
{
int iRet;

EnterCriticalSection( &m_cs );
iRet = m_nSize - m_nHead;
LeaveCriticalSection( &m_cs );

return iRet;
}


inline T* GetReadPtr()
{
T *pRet;

EnterCriticalSection( &m_cs );
pRet = m_pData + m_nHead;


int nSplitFirstDataCount;
if( m_nHead > m_nTail && ( nSplitFirstDataCount = m_nSize - m_nHead ) < m_nExtraSize )
{
memcpy( m_pData + m_nSize, m_pData, sizeof(T) * ( m_nExtraSize - nSplitFirstDataCount ) );
}

LeaveCriticalSection( &m_cs );

return pRet;
}


inline T* GetWritePtr()//获取可以写的地址
{
T *pRet;

EnterCriticalSection( &m_cs );
pRet = m_pData + m_nTail;
LeaveCriticalSection( &m_cs );

return pRet;
}


inline int GetReadableLen()
{
int iRet;

EnterCriticalSection( &m_cs );
if( m_nHead == m_nTail ) iRet = GetLength() > 0 ? m_nSize - m_nHead: 0;
else if( m_nHead < m_nTail ) iRet = m_nTail - m_nHead;
else iRet = m_nSize - m_nHead;
LeaveCriticalSection( &m_cs );

return iRet;
}


inline int GetWritableLen()
{
int iRet;

EnterCriticalSection( &m_cs );
if( m_nHead == m_nTail ) iRet = GetLength() > 0 ? 0 : m_nSize - m_nTail;
else if( m_nHead < m_nTail ) iRet = m_nSize - m_nTail;
else iRet = m_nHead - m_nTail;
LeaveCriticalSection( &m_cs );

return iRet;
}

//入队
inline BOOL Enqueue( T *pSrc, int nSize )
{
EnterCriticalSection( &m_cs );

if( GetSpace() < nSize )
{

LeaveCriticalSection( &m_cs );
return FALSE;
}

/*

BOOL bCopyToExtraBuffer = ( ( m_nHead <= m_nTail ) && ( m_nSize - m_nTail < nSize ) );
*/


if( pSrc )
{
if( m_nHead <= m_nTail )
{

int nBackSpaceCount = m_nSize - m_nTail;

if( nBackSpaceCount >= nSize )
{

memcpy( m_pData + m_nTail, pSrc, sizeof(T) * nSize );
}
else
{

memcpy( m_pData + m_nTail, pSrc, sizeof(T) * nBackSpaceCount );
memcpy( m_pData, pSrc + nBackSpaceCount, sizeof(T) * ( nSize - nBackSpaceCount ) );
}
}
else
{

memcpy( m_pData + m_nTail, pSrc, sizeof(T) * nSize );
}
}

/*
if( bCopyToExtraBuffer )
{

memcpy( m_pData + m_nSize, pSrc + m_nSize - m_nTail, sizeof(T) * ( nSize - ( m_nSize - m_nTail ) ) );
}
*/

m_nTail += nSize;
m_nTail %= m_nSize;
m_nLength += nSize;

LeaveCriticalSection( &m_cs );

return TRUE;
}
//出队
inline BOOL Dequeue( T *pTar, int nSize )
{
EnterCriticalSection( &m_cs );

if( !Peek( pTar, nSize ) )
{
LeaveCriticalSection( &m_cs );
return FALSE;
}

m_nHead += nSize;
m_nHead %= m_nSize;
m_nLength -= nSize;

LeaveCriticalSection( &m_cs );

return TRUE;
}
//获取数据
inline BOOL Peek( T *pTar, int nSize )
{
EnterCriticalSection( &m_cs );

if( m_nLength < nSize )
{
LeaveCriticalSection( &m_cs );
return FALSE;
}

if( pTar != NULL )
{

if( m_nHead < m_nTail )
{
memcpy( pTar, m_pData + m_nHead, sizeof(T) * nSize );
}
else
{
if( GetBackDataCount() >= nSize )
{
memcpy( pTar, m_pData + m_nHead, sizeof(T) * nSize );
}
else
{
memcpy( pTar, m_pData + m_nHead, sizeof(T) * GetBackDataCount() );
memcpy( pTar + GetBackDataCount(), m_pData, sizeof(T) * ( nSize - GetBackDataCount() ) );
}
}
}

LeaveCriticalSection( &m_cs );

return TRUE;
}

inline void CopyHeadDataToExtraBuffer( int nSize )
{
assert( nSize <= m_nExtraSize );

EnterCriticalSection( &m_cs );

//缓冲阻截后加一个额外的缓冲区写入数据。
memcpy( m_pData + m_nSize, m_pData, nSize );

LeaveCriticalSection( &m_cs );
}

protected:
CRITICAL_SECTION m_cs;
T *m_pData; ///队列缓冲区指针
int m_nLength; ///数据长度
int m_nSize; ///缓冲队列大小
int m_nHead; ///投
int m_nTail; ///尾
int m_nExtraSize; ///额外队列缓冲区大小
};

#endif

以下是发送缓冲区代码:

#ifndef _RECVBUFFER_H_
#define _RECVBUFFER_H_

#include <windows.h>
#include "CircuitQueue.h"
#include "NetBase.h"

class RecvBuffer
{
public:
RecvBuffer() { m_pQueue = NULL; }
virtual ~RecvBuffer() { if( m_pQueue ) delete m_pQueue; }

inline VOID Create( int nBufferSize, DWORD dwExtraBufferSize ) {
if( m_pQueue ) delete m_pQueue;
m_pQueue = new CircuitQueue<BYTE>;
m_pQueue->Create( nBufferSize, dwExtraBufferSize ); }

inline VOID Completion( int nBytesRecvd ) { m_pQueue->Enqueue( NULL, nBytesRecvd ); }

inline VOID Clear() { m_pQueue->Clear(); }

inline VOID GetRecvParam( BYTE **ppRecvPtr, int &nLength ) {
*ppRecvPtr = m_pQueue->GetWritePtr();
nLength = m_pQueue->GetWritableLen(); }

inline BYTE* GetFirstPacketPtr() {
PACKET_HEADER header;

if( !m_pQueue->Peek( (BYTE*)&header, sizeof(PACKET_HEADER) ) ) return NULL;

if( m_pQueue->GetLength() <3 /*(int)( sizeof(PACKET_HEADER) + header.size+2 )*/ ) return NULL;

if( m_pQueue->GetBackDataCount() < (int)( header.size +2 + sizeof(header) ) ) {
m_pQueue->CopyHeadDataToExtraBuffer( header.size + 2 + sizeof(header) - m_pQueue->GetBackDataCount() ); }
return m_pQueue->GetReadPtr(); }

inline VOID RemoveFirstPacket( WORD wSize ) {

if (m_pQueue->Dequeue( NULL, wSize )==FALSE)
{
m_pQueue->Clear();
}

}

private:
CircuitQueue<BYTE> *m_pQueue;
};

#endif

以下是投递WSARecv代码,m_pRecvBuffer是RecvBuffer类型指针:

BOOL Session::PreRecv()
{
WSABUF wsabuf;

m_pRecvBuffer->GetRecvParam( (BYTE**)&wsabuf.buf, (int&)wsabuf.len );//获取可写的首地址和可写大小

ZeroMemory( &m_recvIoData, sizeof(OVERLAPPEDEX) );

m_recvIoData.dwOperationType = RECV_POSTED;

int ret = WSARecv( GetSocket(), &wsabuf, 1, &m_recvIoData.dwIoSize, &m_recvIoData.dwFlags, &m_recvIoData, NULL );

if( ret == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING )
{
return FALSE;
}

return TRUE;
}

以下是工作线程里的接收的代码:

case RECV_POSTED:
pSession->GetRecvBuffer()->Completion( dwIoSize );
//下一个Recv
if( !pSession->PreRecv() )
{
Beep(100,200);
}
break;
songtao_01 2009-07-01
  • 打赏
  • 举报
回复
问题一:会不会出现这种情况:

中间有的WSARecv没装满buf就返回完成了,余下数据被下个WSARecv接着收:
Recv1.buf Recv2.buf Recv3.buf Recv4.buf Recv5.buf

101xx102 | xxxx---- | 103xxxxx | x------- | --------

或者返回是乱序的?:

Recv2.buf Recv1.buf Recv3.buf Recv4.buf Recv5.buf

xxxx103x | 101xx102 | xxxxx--- | -------- | --------

只要你不在在多个线程里同时对一个套接字投递多个WSARecv,就不会乱序.


问题二:这包该怎么拆啊?我想这样子处理包
WORD type;
type=取得包的类型;
switch(type)
{
case 101:On101();break;//处理101命令
case 102:On102();break;//处理102命令
...
}
有些不定长度的单个数据流比WSARecv()里准备的buf还大,该怎么办呢?

可以把把缓冲区开大点吗,单个数据流发送的时候也有大小的吧,你用发送时候的最大数据的大小做接受缓冲区的大小就可以了.拆包问题我是这样解决的:为每个连接都开辟接收缓冲区,这个缓冲区能接收多个数据流.在执行WSARecv()前,获取这个缓冲区里的可用空间的指针和可用大小,然后把这两个变量给WSARecv()用.也就是WSARecv接收的数据都在这个缓冲区里面.你可能会问,如果缓冲区可用空间不足,怎么办.其实空间不足,就投递一个缓冲区大小为0的WSARecv就可以了,不必担心数据会丢失,因为系统已经默认给了个缓冲区给数据存的,WSARecv其实就在那个系统默认缓冲区里去取数据.不知道我说清楚了没有,赫赫,我表达能力有限
xjpresley 2009-07-01
  • 打赏
  • 举报
回复
你的情况乱序和接受部分都是可能的! 必须要自己对包加上标识。。。或者一次只投递一个WSArecv
udknight 2009-07-01
  • 打赏
  • 举报
回复
1 同时投递多个WSARecv,乱序是有可能的。需要对收到的数据加上序号并进行排序。
2 定义包头和包体,包头里面有包数据类型和包长度。

typedef struct _header
{
DWORD dwType; //包类型
DWORD dwLen; //数据长度

}HDR, *PHDER;

scq2099yt 2009-07-01
  • 打赏
  • 举报
回复
只要不在两个线程里同时投递了WSARecv,第二种应该不会出现
Conry 2009-07-01
  • 打赏
  • 举报
回复
第一种情况会出现,第二种应该不会

18,362

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧