这两天看了一下关于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的使用:
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
下面是一个简单的(服务器端)代码示例:
夜深了,睡一下,明天还要早起,其他模型待续,而且其中还有些细节需要详细学习一下。