iocp服务器代码问题
我用iocp来做大型网络吞吐连接和收发(tcp),接受连接是单独的线程,接收消息是我也只开启了一个线程,总共2个线程。
程序在平时测试没有问题,但是在压力测试下出现几个奇怪的问题:
1 如果有很多连接同时在向服务器发送连接请求 和发送大批量数据,程序可能在一开启就出错,出错位置在一般是AllocIOContent函数或者AllocSocketContent 的new ,不知道是系统出错还是什么问题。如果先开服务器,再开压力测试就不会。
2 在压力测试情况下(500个连接,每秒发16k) 一旦在ProcessReceive(接收并拼装消息函数)这个函数内断点,接收到的消息就会出错,比如一个消息是8k(需要接收8次,然后拼装成一个消息) 但是它可能收7次,剩下的一次被当成下一个消息的消息头接收,消息内容被当成消息头接收当然就直接就报错了。这种情况在压力更大(1000个连接,每秒发16k)时,也会发生。 但是如果连接比较少,测试一整天都不会有任何问题。过程中还可能导致系统连不上网
线程只有2条,我开始怀疑ProcessReceive的拼装消息有问题,但后来觉得不是,因为如果是它,那不应该平时没问题而且断点后也不应该出问题,而且代码我仔细检查过。
我有2点疑问,一是 如果tcp发送者一直在发送消息,而接收者因为断点停下来后,系统tcp缓存是不是会被堆满并破坏?
二 iocp的队列是不是也有溢出问题。 从问题发送的现象来看,更像是win系统的网络遭到了破坏....
accept 线程代码如下
// 接收新连接
int CZIocpSock::Accept()
{
try
{
SOCKET sock;
SOCKETCONTENT* content;//用于接收数据的buf
IOCONTENT* iocontent;
DWORD numbytes;
DWORD flags;
int namelen;
// 接收连接
if( (sock=WSAAccept(m_hSock, NULL, NULL, NULL, NULL))==INVALID_SOCKET )
{
return -1;
}
content = AllocSocketContent();//申请socket上下文缓存 用于存放接收的数据 每次1k,申请加线程锁
if(content)
{
if( CreateIoCompletionPort((HANDLE)sock, m_hIOCP, (ULONG_PTR)content, 0)!=NULL )
{
iocontent = AllocIOContent();//申请完成端口叠加的上下文内存,申请加线程锁
if(iocontent)
{
content->info.sock = content->sock = sock;
memcpy( &(content->info.addr), &addr, namelen );
content->info.pUserData=NULL;
content->recvbytes = 0;
content->ContFlag = 0;//0被动 1主动
content->MsgTime = CZTime::GetCurrentTime(); // 初始化用户连接的时间
content->recvedData = 0; // 初始化用户接收到的数据量
content->recvedCount = 0; // 初始化用户接收次数
OnClientConnect( &(content->info) );
iocontent->state = eIoBeconRecv;
iocontent->wsabuf.buf = (char *)content->buf;
iocontent->wsabuf.len = RECVBUFLEN;
iocontent->socketcontent = content;
ZeroMemory(&iocontent->overlapped, sizeof(iocontent->overlapped));
numbytes = flags = 0;
if( (WSARecv(content->sock, &iocontent->wsabuf, 1, &numbytes, &flags, &iocontent->overlapped, NULL)!=SOCKET_ERROR)
|| ERROR_IO_PENDING==WSAGetLastError() )
{
return 1;
}
OnClientDisconnect( &(content->info) );//断开连接处理
FreeIOContent(iocontent);//IOContent释放缓存
}
}
FreeSocketContent(content);//SocketContent释放缓存
}
closesocket(sock);
return 0;
}
catch(...)
{
return 0;
}
return 0;
}
接收线程如下
//template <unsigned int RECVBUFLEN , unsigned int SENDBUFLEN >
void CZIocpSock::Worker()
{
DWORD numbytes;
SOCKETCONTENT * content;
IOCONTENT * iocontent;
BOOL success;
DWORD worker_index;
bool bclose = false;
for(worker_index=0; worker_index<COMMSERVER_MAXWORKER && 0!=m_workersThread[worker_index]; worker_index++);
if(worker_index>=COMMSERVER_MAXWORKER)
{
return;
}
m_workersThread[worker_index] = GetCurrentThreadId();
CDBWindow::getHinst()->OutputFormatT("线程id%d",m_workersThread[worker_index] );
while(TRUE)
{
//try
//{
success = GetQueuedCompletionStatus(m_hIOCP, &numbytes, (PULONG_PTR)&content, (LPOVERLAPPED *)&iocontent, INFINITE);
if(iocontent==NULL)
{
if(!success)
{
// error GetQueuedCompletionStatus FAIL
}
break;
}
//int state = iocontent->state;
switch(iocontent->state)
{
case eIoBeconRecv:
{
if(success && numbytes!=0 && ProcessReceive(content, iocontent, numbytes)) //ProcessReceive 拼装并处理消息,目前没有对消息进行处理。
{
continue;
}
// 用户掉线
if(INVALID_SOCKET != content->sock)
{
closesocket(content->sock);
}
OnClientDisconnect( &(content->info) );//断开连接处理
FreeIOContent(iocontent);//释放IOContent缓存
FreeSocketContent(content);//释放SocketContent缓存
}
break;
default:
{
}
break;
}
//}
//catch (...)
//{
// DataError();
// continue;
//}
continue;
}
m_workersThread[worker_index] = 0;
}