这两天看了一下关于winsock的模型,想深入一点学习网络编程这块,把一些东西记录一下。
winsock提供了两种套接字模型:阻塞与非阻塞;
winsock提供了几种io模型:
select模型
WSAAsyncSelect模型
WSAEvent模型
Overlab io模型
Completion port模型
记录点如下:
1:阻塞模式下,io操作完成前,winsock的调用recv,send会一直等待,直到io可以执行,这种模式会导
致程序进入“假死”状态,一直等待数据,无法进行其他操作;
这种情况的解决办法是:利用多线程进行控制,一个线程在等待数据,而另外一个线程在计算或者做界面
处理,当有数据到达时,等待数据线程接收处理数据,或者将数据返回到界面中;此模型伪代码如下:
DWORD WINAPI ThreadWaitData(lp)
{
while(1)
{
recv(data); //阻塞模式下,一直等待直到有数据;
process(data); //处理数据
}
return 0;
}
main()
{
InitWinSock(); //初始化winsock;
CreateThread(ThreadWaitData); //创建接收数据线程
doSomeOtherThings(); //主线程做关于界面或者其他获取数据的事情
}
这种情况下,对于少数几个socket还可以接受,处理得过来,但是对于大量的socket,将无法管理,而且
开辟多个线程对系统资源也耗费不少;
2:非阻塞模式,通过设置socket的nonblocking功能,来使其达到非阻塞状态,设置方法如下:
unsigned long nonblocking = 1;
ioctlsocket(socket,FIONBIO,(unsigned long*)&nonblocking);
//关于此函数其他功能,可以查看msdn说明;
通过将套接字设置为非阻塞模式后,对于socket的操作就不会进入等待,而是直接立刻返回,如果当前操
作不能完成,则返回错误,所以对于非阻塞模式socket来说,需要通过循环检测来等待数据,直到返回正
确。
这个过程也可以利用上面的多线程伪代码来实现;
3:虽然阻塞模型在使用上比较简洁,但是对于多个socket的管理来说,阻塞模型存在着很难管理,以及
资源耗费等问题,因此阻塞模型主要用于开发原型,具体需要学习以下一些io模型;‘
4:select模型,该模型的重点是select函数,而且select具有可移植性,可以很容易的移植到linux系统
上,select主要通过判断套接字上的状态,来决定是否进行io操作,这样可以避免了等待阻塞的“假死”
现象发生。
select函数原型:
int select(
int nfds,
fd_set FAR* readfds,
fd_set FAR* writefds,
fd_set FAR* exceptfds,
const struct timeval FAR* timeou
);
具体参数解释使用可以查看msdn,很详细
另外有几个重要的宏,需要一起使用
FD_ZERO(*set) 清空fd_set,集合在使用前都要清空,因为select函数会重置这些位
FD_CLR(s,*set) 从set中删除套接字s
FD_ISSET(s,*set) 判断s是否为fd_set中的一员
FD_SET(s,*set) 加入套接字s到fd_set中
具体使用select模型的基本过程如下:
1,调用FD_ZERO,清空集合,初始化集合
2,加入需要监听的socket,分配到fd_set上,FD_SET
3,select函数调用,等待超时或者返回fd_set上的socket总数
4,FD_ISSET判断相应是那个socket的事件发生了,
5,做出相应的io处理,然后继续循环检测
select模型可以利用单线程来管理多个socket,但是上限是64个;(不过好像可以设置)
下面是我写的一个简单的测试代码,感受一下select的使用:
#include "BaseSocket.h" void main() { WORD version; version = MAKEWORD(2,2); WSADATA wsdata; if(0 != WSAStartup(version,&wsdata)) { cout<<"socket lib startup error"<<endl; return; } SOCKET tcpServer; tcpServer = socket(AF_INET,SOCK_STREAM,0); if(tcpServer == INVALID_SOCKET) { cout<<"tcp server socket create error"<<endl; WSACleanup(); return; } SOCKET udpServer; udpServer = socket(AF_INET,SOCK_DGRAM,0); if(udpServer == INVALID_SOCKET) { cout<<"udp server socket create error"<<endl; WSACleanup(); return; } sockaddr_in local; local.sin_family = AF_INET; local.sin_addr.S_un.S_addr = INADDR_ANY; local.sin_port = htons(9000); sockaddr_in ulocal; ulocal.sin_family = AF_INET; ulocal.sin_addr.S_un.S_addr = INADDR_ANY; ulocal.sin_port = htons(9001); if(SOCKET_ERROR == (bind(tcpServer,(sockaddr*)&local,sizeof(local)))) { cout<<"tcp socket bind error"<<endl; cout<<"error code: "<<GetLastError()<<endl; closesocket(tcpServer); WSACleanup(); return; } if(SOCKET_ERROR == (bind(udpServer,(sockaddr*)&ulocal,sizeof(ulocal)))) { cout<<"udp socket bind error"<<endl; cout<<"error code: "<<GetLastError()<<endl; closesocket(udpServer); WSACleanup(); return; } if(SOCKET_ERROR == (listen(tcpServer,1))) { cout<<"socket listen error"<<endl; cout<<"error code :"<<GetLastError()<<endl; closesocket(tcpServer); WSACleanup(); return; } int v = 1; if(SOCKET_ERROR == (ioctlsocket(tcpServer,FIONBIO,(u_long*)&v))) { cout<<"set unblock error "<<endl; } fd_set fread; timeval tv; int nSize; tv.tv_sec = 2; tv.tv_usec = 0; vector<SOCKET>vec_sock; int ttt(0); while(1) { FD_ZERO(&fread); FD_SET(tcpServer,&fread); FD_SET(udpServer,&fread); for(int i = 0;i<vec_sock.size();i++) FD_SET(vec_sock[i],&fread); SOCKET client; int count = select(0,&fread,NULL,NULL,&tv); //select //cout<<"accept :"<<count<<endl; nSize = sizeof(local); if(FD_ISSET(tcpServer,&fread)) { client = accept(tcpServer,(sockaddr*)&local,&nSize); FD_SET(client,&fread); vec_sock.push_back(client); } else if(FD_ISSET(udpServer,&fread)) { nSize = sizeof(ulocal); sockaddr_in from; char uBuf[256]; int len = 256; int size = sizeof(from); int ret = recvfrom(udpServer,uBuf,len,0,(sockaddr*)&from,&size); uBuf[ret] = '/0'; cout<<"receive UDP data .. : "; cout<<uBuf<<endl; } else { //cout<<"vector socket size :"<<vec_sock.size()<<endl; for(int i = 0;i<vec_sock.size();i++) { if(FD_ISSET(vec_sock[i],&fread)) { char buffer[256]; int ret(1); ret = recv(vec_sock[i],buffer,256,0); if(SOCKET_ERROR == ret || 0 == ret) //peer socket gracefully close { closesocket(vec_sock[i]); vec_sock.erase(vec_sock.begin()+i); cout<<"close socket....."<<endl; } else { buffer[ret] = '/0'; cout<<buffer<<endl; } } } } } FD_CLR(tcpServer,&fread); FD_CLR(udpServer,&fread); closesocket(tcpServer); closesocket(udpServer); for(int i = 0;i<vec_sock.size();i++) { FD_CLR(vec_sock[i],&fread); closesocket(vec_sock[i]); } WSACleanup(); system("pause"); return; }
5:WSAEventSelect模型(暂时掠过WSAAsyncSelect)
WSAEventSelect与select过程有点相似,都是通过设置感兴趣的socket事件,然后等待事件发生,然后再
做处理,不过WSAEventSelect模型可以直接将事件对象挂到网络事件中,直接通过网络事件来相应,这个
在多线程里面比较容易使用,举个简单例子:
有个线程需要等待某个网络事件才会响应,那么可以在线程里面WaitSingleObjec(event),然后该event
也同时通过WSASelectEvent()来设置该事件所感兴趣的网络事件(FD_READ|FD_WRITE|FD_CLOSE),这样当
感兴趣的事情发生时,线程也会同时收到该事件的设置状态(signaling),这时线程也可以同时继续做
相应的处理:伪代码如下:
HANDEL waitEvent;
DWORD WINAPI Thread(lp)
{
while(1){
WaitForSingleObject(waitEvet); //等待事件发生
ProcessSomething() //做出相应处理
}
}
main()
{
WSASelect(socket,event,FD_CLOSE); //假设对关闭socket感兴趣
while(1)
{
检测关闭事件,如果有关闭事件发生,Thread线程将进行ProcessSomething()处理
}
}
在WSAEventSelect模型中,需要用的几个重要的函数有:
WSACreateEvent() 创建事件 (需要人工重设)
WSAEventSelect() 设置事件对socket中的哪个操作感兴趣
WaitForMultipleEvents() 设置超时等待网络事件发生’
WSAEnumNetworkEvents() 遍历所发生的网络事件,存放到WSANETWORKSEVENT结构体中,参考msdn
下面是一个简单的(服务器端)代码示例:
#include "WSASelectServer.h" void cleanUp() { cout<<"ERROR CODE :"<<GetLastError()<<endl; WSACleanup(); } //HANDLE g_hEvent; WSAEVENT listenEvent; DWORD WINAPI waitEvent(LPVOID lp) { while(1) { WaitForSingleObject(listenEvent,INFINITE); //ResetEvent(listenEvent); cout<<"listen event happen."<<endl; Sleep(500); //break; } return 0; } void main() { // g_hEvent = CreateEvent(NULL,true,false,NULL); WORD version; version = MAKEWORD(2,2); WSADATA wsadata; if(0 != WSAStartup(version,&wsadata)) { cout<<"WSA startup error"<<endl; return ; } SOCKET server; server = socket(AF_INET,SOCK_STREAM,0); if(server == INVALID_SOCKET) { cout<<"Invalid socket.."<<endl; WSACleanup(); } sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(9000); local.sin_addr.S_un.S_addr = INADDR_ANY; if(SOCKET_ERROR == (bind(server,(sockaddr*)&local,sizeof(local)))) { cout<<"tcp bind error....."<<endl; cleanUp(); closesocket(server); } //WSAEVENT listenEvent;CreateEvent(NULL,true,false,NULL); //人工重设 HANDLE h1 = CreateThread(NULL,0,waitEvent,NULL,0,NULL); if(WSA_INVALID_HANDLE == WSAEventSelect(server,listenEvent,FD_ACCEPT)) { cout<<"Error Handle....."<<endl; } if(SOCKET_ERROR == (listen(server,5))) { cout<<"tcp listen error..."<<endl; cleanUp(); listenEvent = WSACreateEvent();// closesocket(server); } int cEvent(0); WSAEVENT LEvent[10]; SOCKET LSocket[10]; LEvent[cEvent] = listenEvent; LSocket[cEvent] = server; cEvent++; int Index(0); SOCKET client = INVALID_SOCKET; WSANETWORKEVENTS NetEvents; sockaddr_in from; int addrLen = sizeof(from); while(1) { Index = WSAWaitForMultipleEvents(cEvent,LEvent,false,2000,false); cout<<"Index :"<<Index<<endl; cout<<"cEvent :"<<cEvent<<endl; if(Index != WSA_WAIT_FAILED && Index != WSA_WAIT_TIMEOUT) { Index = Index - WSA_WAIT_EVENT_0; cout<<"Index :"<<Index<<endl; for(int i = Index;i<cEvent;i++) { Index = WSAWaitForMultipleEvents(1,&LEvent[i],false,0,false); if(Index == WSA_WAIT_FAILED || Index == WSA_WAIT_TIMEOUT) continue; else { Index = i; WSAEnumNetworkEvents(LSocket[Index],LEvent[Index],&NetEvents); if(NetEvents.lNetworkEvents & FD_ACCEPT) { if(NetEvents.iErrorCode[FD_ACCEPT_BIT] != 0) { cout<<"FD_ACCEPT failed with error "<<NetEvents.iErrorCode[FD_ACCEPT_BIT]<<endl; break; } client = accept(server,(sockaddr*)&from,&addrLen); if(cEvent >= 10) { cout<<"to much socket link"<<endl; closesocket(client); break; } char *buf; buf = inet_ntoa(from.sin_addr); cout<<"Accept socket from :"; cout<<buf<<endl; LEvent[cEvent] = WSACreateEvent(); LSocket[cEvent] = client; WSAEventSelect(LSocket[cEvent],LEvent[cEvent],FD_READ|FD_CLOSE); cEvent++; } if(NetEvents.lNetworkEvents & FD_READ) { if(NetEvents.iErrorCode[FD_READ_BIT] != 0) { cout<<"FD_READ fail error code :"<<NetEvents.iErrorCode[FD_READ_BIT]<<endl; break; } int ret(0); char buffer[256]; while(ret != SOCKET_ERROR) { ret = recv(LSocket[Index],buffer,256,0); buffer[ret] = '/0'; cout<<buffer<<endl; } } if(NetEvents.lNetworkEvents & FD_CLOSE) { if(NetEvents.iErrorCode[FD_CLOSE_BIT] != 0) { cout<<"FD_CLOSE fail error code :"<<NetEvents.iErrorCode[FD_CLOSE_BIT]<<endl; break; } closesocket(LSocket[Index]); cout<<"close socket"<<endl; } // WSAResetEvent(LEvent[Index]); } } } else { cout<<"wait for events"<<endl; } } closesocket(client); closesocket(server); WSACleanup(); system("pause"); return; }
夜深了,睡一下,明天还要早起,其他模型待续,而且其中还有些细节需要详细学习一下。