如果要实现多个socket同时工作的话,
一 . 同步阻塞 + 多线程
二 . 同步非阻塞(ioctrlsocket):通俗讲,就是每个套接字都去内核看看收没收到消息,没收到再返回
三 . I/O模型select:
①集合 fd_set
②清空集合 FD_ZERO
③将Socket放入集合内 FD_SET
④将集合交给select管理 select()
⑤校验数据(谁在集合中即收到数据)FD_ISSET
优点:轮循查看集合内socket是否发生网络事件,将发生网络事件的socket留在集合内,简单方便跨平台
缺点:有个数限制(最多处理64个,在winsock2.h定义的宏,我们可以重定义,但最多不超过1024),仍然为阻塞函数(查看,拷回数据)
示例:
bool UDPINet::SelectMySocket(SOCKET sock) { timeval tm; tm.tv_usec = 100; fd_set fd_sets; FD_ZERO(&fd_sets); FD_SET(sock,&fd_sets); select(NULL,&fd_sets,NULL,NULL,&tm); if(!FD_ISSET(sock,&fd_sets))//列表中没有套接字,则没收到消息,false return false; return true; }
select函数的参数:
四 . 异步选择(消息):当socket发生事件时发消息
①加载库
②socket
③bind
④创建窗口,注册(socket,事件,消息)WSAAsyncSelect()
优点:阻塞时间短,只有拷贝数据时阻塞
缺点:平台限制,只适用于Windows(因为它基于消息),时间消耗多(基于消息)
示例:
MyWnd = MyWnd->GetWnd(); //创建窗口 if(!MyWnd->Create(NULL,"MyWnd")) { UnInitInet(); return false; } //注册 WSAAsyncSelect(sock,*MyWnd,NM_MESSAGE,FD_READ);
WSAAsyncSelect()的参数:
其中MyWnd为新创建的窗口,用来接收NM_MESSAGE消息
其中采用单例模型:懒汉模式:单线程
饿汉模式:多线程
五 . 异步事件(内核对象,操作系统通知)
与异步消息不同:当socket发生事件时发事件
单个socket情况:
ws = WSACreateEvent();
WSAEventSelect(sock,ws,FD_READ);
线程实现函数:
if(WAIT_TIMEOUT == WaitForSingleObject(pthis->ws,100)) continue; ResetEvent(pthis->ws);
多个socket情况:将socket和event放入数组,下标相同则对应。
WSAEVENT ws = WSACreateEvent(); if(!WSAEventSelect(sock,ws,FD_READ))//成功返回零 { aryevent[m_EvetNum] = ws; arysocket[m_EvetNum] = sock; m_EvetNum++; } thread = (HANDLE )_beginthreadex(NULL,0,&UDPINet::ThreadProc,this,0,0);
线程函数:
WSANETWORKEVENTS ws_network; DWORD dwResult = WSAWaitForMultipleEvents(pthis->m_EvetNum,pthis->aryevent,FALSE,100,TRUE); if(WAIT_TIMEOUT == dwResult) { continue; } dwResult -= WSA_WAIT_EVENT_0 ;//获取事件索引 //发生了什么类型的网络事件,成功返回0 if(WSAEnumNetworkEvents(pthis->arysocket[dwResult],pthis->aryevent[dwResult],&ws_network)) continue; if(ws_network.lNetworkEvents & FD_READ)//得到事件为可读消息 { nRes = recvfrom(pthis->sock,buf,MAX_SIZE,0,(sockaddr*)&addr,&len); //调用处理函数 if(nRes > 0) { pthis->m_pIMeditor->DealMessage(buf,addr.sin_addr.S_un.S_addr); } }