首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > VC/MFC >

IOCP同一个链接上多次Send和Recv的有关问题

2013-09-06 
IOCP同一个链接上多次Send和Recv的问题本帖最后由 woshiqinxue 于 2013-08-15 22:49:13 编辑我有一个Serve

IOCP同一个链接上多次Send和Recv的问题
本帖最后由 woshiqinxue 于 2013-08-15 22:49:13 编辑 我有一个Serve和Client,但是在测试的时候Client只有第一次Send可以在Serve上的处理线程上接收到。请帮忙看看;还有我想问问下面代码这样的方式是不是可以实现1000个连接的并发处理,处理的效率怎么样?
下面是代码:
Serve:


int _tmain(int argc, _TCHAR* argv[])
{
InitWinsock();
HANDLE CompletionPort =CreateIoCompletionPort(INVALID_HANDLE_VALUE, 
NULL, 0, 0);

//根据系统的CPU来创建工作者线程
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);

//线程数目=系统进程数目的两倍.
for(int i=0; i!=SystemInfo.dwNumberOfProcessors * 2; ++i)
{
HANDLE hProcessIO = CreateThread(NULL, 0, ProcessIO, 
CompletionPort, 0, NULL);
if(hProcessIO)
{
CloseHandle(hProcessIO);
}
}

//创建侦听SOCKET
SOCKET sListen = BindServerOverlapped(PORT);

SOCKET sClient;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
while(TRUE)
{
//sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
sClient = accept(sListen, 0, 0);
cout << "Socket " << sClient << "连接进来\n"<< endl;

PerHandleData = new PER_HANDLE_DATA();
PerHandleData->Socket = sClient;

CreateIoCompletionPort((HANDLE)sClient, CompletionPort,
(DWORD)PerHandleData, 0);

// 建立一个Overlapped,并使用这个Overlapped结构对socket投递操作
PerIoData = new PER_IO_OPERATION_DATA();

ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
PerIoData->DataBuf.buf = PerIoData->Buffer;
PerIoData->DataBuf.len = DATA_BUFSIZE;

DWORD Flags = 0;
DWORD dwRecv = 0;
WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv,
 &Flags, &PerIoData->Overlapped, NULL);
}

DWORD dwByteTrans;
//将一个已经完成的IO通知添加到IO完成端口的队列中.
//提供了与线程池中的所有线程通信的方式.
PostQueuedCompletionStatus(CompletionPort,
dwByteTrans, 0, 0); //IO操作完成时接收的字节数.

closesocket(sListen);
return 0;
}

DWORD WINAPI ProcessIO(LPVOID lpParam)
{
HANDLE CompletionPort = (HANDLE)lpParam;
DWORD BytesTransferred;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;

while(true)
{

if(0 == ::GetQueuedCompletionStatus(CompletionPort,
&BytesTransferred, (LPDWORD)&PerHandleData,(LPOVERLAPPED*)&PerIoData, INFINITE))
{


DWORD er = GetLastError();
if( (GetLastError() ==WAIT_TIMEOUT) || 
(GetLastError() == ERROR_NETNAME_DELETED) )
{
cout << "closingsocket" << PerHandleData->Socket \
<< "\n" << endl; 
closesocket(PerHandleData->Socket);

if (PerIoData != NULL)
{
delete PerIoData;
PerIoData = NULL;
}
if (PerHandleData != NULL)
{
delete PerHandleData;
PerHandleData = NULL;
}
continue;
}
else
{
cout << "GetQueuedCompletionStatus failed!\n" << endl;
}
return 0;
}

// 说明客户端已经退出
if(BytesTransferred == 0)
{
cout << "closing socket" <<PerHandleData->Socket << "\n" << endl;
closesocket(PerHandleData->Socket);
if (PerIoData != NULL)
{
delete PerIoData;
PerIoData = NULL;
}
if (PerHandleData != NULL)
{
delete PerHandleData;
PerHandleData = NULL;
}
continue;
}

// 取得数据并处理

memcpy((void*)&dsr, PerIoData->Buffer, strlen(PerIoData->Buffer));
cout << PerHandleData->Socket<< "发送过来的消息:"<< \
PerIoData->Buffer <<"\n" << endl;
}

return 0;
}



Client:

int _tmain(int argc, _TCHAR* argv[])
{
InitializeCriticalSection(&g_cs);
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 2, 2);

err = WSAStartup( wVersionRequested,&wsaData );//WSAStartup()加载套接字库
if ( err != 0 ) {

return -1;
}

if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ){
WSACleanup( );
return -1;
}


HANDLE hThread = CreateThread(NULL, 0, ProcessThread, 0, 0, NULL);
if(hThread==NULL)
{
WSACleanup();
return 0;
}
WaitForSingleObject(hThread, INFINITE);

WSACleanup();
return 0;
}

DWORD WINAPI ProcessThread(LPVOID lpParam)
{
int nCnt = 0;
char sendBuf[MAX_SENDBUF];


char recvBuf[MAX_RECVBUF];
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
if(sockClient == INVALID_SOCKET)
{
cout<<"socket 失败"<<endl;
return -1;
}
nCnt++;
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); 
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(5050);//和服务器端的端口号保持一致
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

while(nCnt < MAXCNT)
{
nCnt++;
memset(sendBuf, 0, sizeof(char)*MAX_SENDBUF);
sprintf(sendBuf, "This is Thread %d:", nCnt++);
send(sockClient, sendBuf, sizeof(sendBuf)+1, 0);
sleep(1000);
}
closesocket(sockClient);
return 0;
}

iocp socket
[解决办法]
触发了WSARecv后就需要再投递WSARecv才能继续接收数据。
还有就是对一些非正常掉线的处理和资源申请释放回收等要注意一下
[解决办法]
既然用IOCP为什么发送的时候不用WSASend函数?


[解决办法]
你只是主线程里面WSARecv了一次告诉系统你要收数据

在ProcessIO处理完接收或者发送动作  你需要再WSARecv才会触发下一次接收

所以一般是再工作线程只要处理完接收动作就继续投递WSARecv  这样才能保证不停的在接收

[解决办法]
IOCP 是不断的投递事件(send,recv,accept等),然后等待事件完成。所以要不断接收要在recv事件完成后再次投递recv事件。
然后IOCP的效率是很高的,1000客户完全没压力。

热点排行