iocp服务器代码问题

zkl_asdfjkl 2011-10-26 12:01:35
我用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;
}
...全文
244 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
神的理想 2011-11-18
  • 打赏
  • 举报
回复
网络编程区置顶帖子里有个很强大的IOCP下载
carrerahun 2011-11-18
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 zhouxicai 的回复:]

网络编程区置顶帖子里有个很强大的IOCP下载
[/Quote]

在哪??求地址
dfasri 2011-11-03
  • 打赏
  • 举报
回复
写好后期待你的测试结果...本机客户端连接后, 1s内最多可以echo多少次8K的数据
zkl_asdfjkl 2011-11-02
  • 打赏
  • 举报
回复
自己顶一下,兄弟们帮忙.我结贴率很高的哦
zkl_asdfjkl 2011-11-01
  • 打赏
  • 举报
回复
那再问个问题好了
一个socket与完成端口绑定,然后即可能调用了 WSARecv 又可能调用了 WSASend
那么WSASend的参数怎么绑定给GetQueuedCompletionStatus 第三个参数的?
WSARecv 绑定时是用了CreateIoCompletionPort((HANDLE)hSocket, m_hIOCP, (ULONG_PTR)pIoRecvContent, 0) 第三个参数绑定了iocontent ,
WSASend 代码如下,这个代码是没有问题的GetQueuedCompletionStatus 能收到它的iocontent ,
可是我没看见绑定iocontent 的语句啊,谁能回答一下?
try
{
DWORD sendlen=0;
for( DWORD _start=0; _start<buflen; _start+=sendlen )
{
if( buflen-_start < SENDBUFLEN ) sendlen = buflen-_start;
else sendlen = SENDBUFLEN;

DWORD i, ok_count;
IOCONTENT * iocontent;
SENDBUF * sendbuf;
DWORD numbytes;

sendbuf=AllocSendBuf();
if(NULL==sendbuf) return 0;

memcpy(sendbuf->buf, buf+_start, sendlen);
sendbuf->count = (LONG)(count+1);

for(i=0, ok_count=0; i<count; i++)
{
iocontent = AllocIOContent();

if(iocontent)
{
ZeroMemory(&iocontent->overlapped, sizeof(iocontent->overlapped));

if( bcolse && i >= count -1 )
iocontent->state = eIoBeconSendClose;
else
iocontent->state = eIoBeconSend;

iocontent->sendbuf = sendbuf;
iocontent->wsabuf.buf = (char *)sendbuf->buf;
iocontent->wsabuf.len = sendlen;

numbytes = 0;

iocontent->sock = socks[i];
if( (WSASend(socks[i], &iocontent->wsabuf, 1, &numbytes, 0, &iocontent->overlapped, NULL)!=SOCKET_ERROR)
||(ERROR_IO_PENDING==WSAGetLastError()) )
{
ok_count++;

m_sendBufLenArray.Lock();
int sockind = m_sendBufLenArray.FindNode(socks[i]);
if(-1==sockind)
{
sockind = m_sendBufLenArray.NewNode(socks[i],TRUE);
*(m_sendBufLenArray[sockind]) = 0;
}
if(-1!=sockind)
{
(*m_sendBufLenArray[sockind]) ++;
}
m_sendBufLenArray.Unlock();

continue;
}

FreeIOContent(iocontent);
}
InterlockedDecrement(&sendbuf->count);
}

if(InterlockedDecrement(&sendbuf->count)==0)
{
FreeSendBuf(sendbuf);
}
}
//关闭
if( bcolse )
{
for ( DWORD i = 0; i <count; i++)
{
linger m_sLinger;
m_sLinger.l_onoff=1;
m_sLinger.l_linger=5;
setsockopt(socks[i],SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));

closesocket(socks[i]);
}
}

return 1;//ok_count;
}
catch(...)
{
return 0;
}
return 0;
zkl_asdfjkl 2011-11-01
  • 打赏
  • 举报
回复
我知道什么原因的,NND,编译没设成多线程,晕死。
dfasri 2011-11-01
  • 打赏
  • 举报
回复
不用IOCP就是明智的选择
一叶之舟 2011-10-28
  • 打赏
  • 举报
回复
iocp开启工作线程的数量应该是(CUP个数*2)+2
zkl_asdfjkl 2011-10-28
  • 打赏
  • 举报
回复
两个线程都能出错,开多个有意义吗,两个线程就算慢一点,至少要能健壮的跑吧?
能给个例子吗
满衣兄 2011-10-27
  • 打赏
  • 举报
回复
用线程池,根据CPU占用量开启线程。具体实现参考gh0st的源码,里面有。

8,304

社区成员

发帖
与我相关
我的任务
社区描述
游戏开发相关内容讨论专区
社区管理员
  • 游戏开发
  • 呆呆敲代码的小Y
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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