首页
社区
课程
招聘
[求助]十万火急,在线求助各位高手---关于基于IOCP的通讯
发表于: 2014-11-12 21:42 4408

[求助]十万火急,在线求助各位高手---关于基于IOCP的通讯

2014-11-12 21:42
4408
    我们公司是做制造业软件(C/S架构)的,为了数据安全性,请外面的通讯专家(号称)帮我们做了套基于IOCP的通讯程序。由于我们这边管理上的疏忽和经验上的不足,导致正式上线后问题很多。我之前从来没接触过这方面,后来边学边试,硬着头皮改了很多bug。不过现在碰到个问题实在没法解决,所以借同事的账号在这里请教一下大家,希望能得到帮助。
    说了那么多废话, 以下进入正题!
    在服务端有一个主线程,一个管理者线程和n个工作者线程。主线程和工作者线程的作用和其他基于IOCP的通讯程序差不多,就不啰嗦了。管理者线程负责针对一些可能无效的资源的释放。比如用户在长时间内都没有通讯,管理者线程会关闭此用户之前建立的连接closesocket(socket)。正常情况下,此时某一个线程的GetQueuedCompletionStatus会返回一个错误值,然后线程有机会把此socket对应的内存资源释放掉。但问题来了!!!!极少数情况下(1个月出现5次),没有任何线程会从GetQueuedCompletionStatus返回。后果是那个socket对应的内存资源会泄露了。由于泄露的内存中包含了此socket,在我们的程序中,管理者线程还会对此调用closesocket,由于释放掉的socket可能稍后被其他用户申请,因此其他客户端的连接会被关闭,导致数据混乱。
    说了那么多,不知道有没有说清楚。其实简单地说就是closesocket后GetQueuedCompletionStatus有时候会不响应。我看了网上的一些文档,尝试在调用closesocket之前先调用shutdown,也不起作用。补充下,他们都正确返回了。
    谢谢各位了!!!!

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (7)
雪    币: 167
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
我记得IOCP关闭连接好像是用PostQueuedCompletionStatus发送消息 然后再处理此消息释放相应资源 而不是像你这样在管理者线程中直接closesocket.
2014-11-12 22:43
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
多谢,我明天试试
2014-11-12 23:13
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
PostQueuedCompletionStatus会关闭线程,我仅仅是需要把此socket对应的资源释放掉。
2014-11-13 07:51
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
把程序贴在这里吧,顺便说一下,通过日志我发现这个错误有个奇怪的现象,如果在“1111111”处成功close了n个socket,第一个和第n个不会引起“22222222”处的运行,即这两个socket对应的资源(PPER_IO_DATA , PPER_SOCKET_DATA)泄露了!

// 单I/O数据
typedef struct _IO_DATA
{
        OVERLAPPED ol;
        WSABUF     DataBuf;
        char       Buffer[DEFAULT_BUFLEN];
        MISC_HDR   MiscHdr;
        int        LockFlag;   
        IO_TYPE    IoType;
}PER_IO_DATA, *PPER_IO_DATA;

// 单句柄数据
typedef struct _SOCKET_DATA
{
        SOCKET hSock;
        SOCKADDR_IN ClientAddr;
        bool ActiveFlag;   
}PER_SOCKET_DATA, *PPER_SOCKET_DATA;

typedef vector<PPER_SOCKET_DATA>   SocketDataVector;
typedef vector<PPER_IO_DATA>       IoDataVector;

SocketDataVector  gSockDataVec;
IoDataVector      gIoDataVec;

//0----主线程
int _tmain( int argc, _TCHAR* argv[] )
{
        //创建一个IO完成端口
        hIocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 );
        //初始化 Winsock.
        iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
        //创建一个监听套接字
        ListenSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
        //绑定端口至SPD_SERVER_LISTEN_PORT
        iResult = bind( ListenSocket, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr) );
        //监听
        iResult = listen( ListenSocket, SOMAXCONN );
        //创建工作线程
        siSysInfo.dwNumberOfProcessors <<= 1;
        for( int i=0; i < siSysInfo.dwNumberOfProcessors; i++ ) {
                QueueUserWorkItem( WorkerThreadFunc, hIocp, WT_EXECUTELONGFUNCTION );
        }
        //创建连接管理线程
        DWORD dwCMThreadId = 0;
        HANDLE hCMThread = CreateThread(NULL, 0, ConnManagerThreadFunc,
                NULL, 0, &dwCMThreadId);

                //accept
        SockLen = sizeof(ClientAddr);
        while (true) {
                SOCKET hAccept = accept(ListenSocket, (struct sockaddr *)&ClientAddr, &SockLen);
                if(hAccept != INVALID_SOCKET) {
                        //分配单IO数据结构
                        PPER_IO_DATA pPerIoData = new PER_IO_DATA;
                        //分配单socket数据结构
                        PPER_SOCKET_DATA pPerHandleData = new PER_SOCKET_DATA;
                        pPerHandleData->hSock = hAccept;
                        memcpy(&pPerHandleData->ClientAddr, &ClientAddr, sizeof(ClientAddr));
                        pPerHandleData->ActiveFlag = true;
                        EnterCriticalSection(&csPerDataVec);
                        CreateIoCompletionPort( reinterpret_cast<HANDLE>(hAccept), hIocp,
                                reinterpret_cast<DWORD>(pPerHandleData), 0);
                               
                        memset(pPerIoData, 0, sizeof(PER_IO_DATA));
                        pPerIoData->IoType = IoRecv;
                        pPerIoData->DataBuf.len = DEFAULT_BUFLEN;
                        pPerIoData->DataBuf.buf = pPerIoData->Buffer;
                        DWORD dwRecvd = 0;
                        DWORD dwFlags = 0;
                        WSARecv( hAccept, &pPerIoData->DataBuf, 1,
                                &dwRecvd, &dwFlags,
                                reinterpret_cast<LPWSAOVERLAPPED>(pPerIoData), NULL );
               
                        gSockDataVec.push_back(pPerHandleData);
                        gIoDataVec.push_back(pPerIoData);
                        LeaveCriticalSection(&csPerDataVec);
                }
        }

        CloseHandle(hCMThread);
        closesocket(ListenSocket);
        WSACleanup();
        CloseHandle(hIocp);
        CloseLogFile(LogName);
        return 0;
}

//1----工作者线程
DWORD WINAPI WorkerThreadFunc( LPVOID lpParam )
{
        HANDLE hIocp = reinterpret_cast<HANDLE>( lpParam );
        DWORD dwSend = 0;
        DWORD dwRecvd = 0;
        DWORD dwFlags = 0;
        DWORD dwTransCount = 0;
        PPER_IO_DATA pPerIoData = NULL;
        PPER_SOCKET_DATA pPerHandleData = NULL;

        int iResult = 0;
        int nRetShutDown = -1;
        int nRetClSockect = -1;
        IoDataVector::iterator itrIoDelete;

        while( TRUE ) {
                if( GetQueuedCompletionStatus( hIocp, &dwTransCount,
                        reinterpret_cast<LPDWORD>(&pPerHandleData),
                        reinterpret_cast<LPOVERLAPPED*>(&pPerIoData), INFINITE ) )
                {
                                if( dwTransCount == 0 && pPerIoData->IoType != IoQuit ) {
                                        //客户端主动关闭连接
                                        shutdown(pPerHandleData->hSock,SD_BOTH);
                                        closesocket(pPerHandleData->hSock)!=0);
                                        FreePerData(pPerIoData, pPerHandleData);
                                        continue;
                                }
                               
                                switch( pPerIoData->IoType ) {
                                case IoSend:
                                //一些处理
                                case IoRecv:
                                //另外一些处理
                                default:
                                        ;
                                }
                }
                else {//2222222222222222
                        //服务器已主动断开连接或客户端断开连接
                        if(pPerHandleData!=NULL&&pPerIoData!=NULL) {
                                FreePerData(pPerIoData, pPerHandleData);
                        }
                }
        }

LQUIT:
        return 0;
}

//2----管理者线程
DWORD WINAPI ConnManagerThreadFunc( LPVOID lpParam )
{
        while (true) {  
                EnterCriticalSection(&csPerDataVec); //这里需要加上互斥锁,否则工作线程对连接池进行修改会造成迭代器失效
                SocketDataVector::iterator iter = gSockDataVec.begin();
                for (; iter != gSockDataVec.end(); iter++)
                {
                        //关闭该连接,这里不释放数据结构,工作线程会检测到断开连接并代为释放
                        shutdown((*iter)->hSock,SD_BOTH)!=0);
                        closesocket((*iter)->hSock)!=0);//11111111111111111111
                }
                LeaveCriticalSection(&csPerDataVec);
                Sleep(SCAN_COON_INTERVAL);
        }
        return 0;
}

//3..释放单IO和单句柄数据结构
void FreePerData(PPER_IO_DATA pIoData, PPER_SOCKET_DATA pSocketData)
{
        EnterCriticalSection( &csPerDataVec );
        IoDataVector::iterator itrIoDelete = find( gIoDataVec.begin(), gIoDataVec.end(), pIoData );
        SocketDataVector::iterator itrSockDelete = find( gSockDataVec.begin(), gSockDataVec.end(), pSocketData );
        bool bFindIoData = itrIoDelete != gIoDataVec.end();
        bool bFindSockData = itrSockDelete != gSockDataVec.end();
        if ( bFindIoData&&bFindSockData) {
                delete *itrIoDelete;
                delete *itrSockDelete;
                gIoDataVec.erase( itrIoDelete );
                gSockDataVec.erase( itrSockDelete );
        }
        LeaveCriticalSection( &csPerDataVec );
}
2014-11-13 13:55
0
雪    币: 55
活跃值: (75)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
为什么要用外面的所谓“专家”新开发的库?boost.asio 现成的东西不用?说实话,boost.asio 是一套非常不错的库,至少比那些没经过考验的库好很多的!
2014-11-13 21:07
0
雪    币: 367
活跃值: (20)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
must try boost::asio
2014-11-13 22:58
0
雪    币: 293
活跃值: (239)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
标题吸引了我
2014-12-2 13:51
0
游客
登录 | 注册 方可回帖
返回
//