1、套接字分为两种状态:阻塞和非阻塞态,可以使用ioctlsocket函数来设置FIONBIO指定套接字状态。对套接字进行recv时,在阻塞时,返回值大于0,表示有数据收到;返回值等于0,表示对方关闭连接;返回值为-1,表示连接意外中断(RST)。而在非阻塞时返回值如果为-1,可能表示这次没有收到数据。此时需要判断WSAGetLastError是否是WSAEWOULDBLOCK,是的话表示当前无数据,需要等待下一次接收。
Microsoft Windows是非抢先的多任务环境,如果一个应用程序不主动放弃其控制权,别的应用程序就不能够执行。因此,在Windows Sockets网络程序设计中,尽管它支持阻塞操作,但是它是反对使用阻塞操作的。在Windows Sockets实现中,对于不能立即完成的阻塞操作做如下处理:DLL初始化操作,然后进入一个循环。在循环中,它发送任何Windows消息,并检查这个Windows Sockets调用是否完成,在必要时,它可以放弃CPU让其它应用程序执行。
2、select模型的基本思想就是轮询机制。首先初始化队列(把需要管理的套接字放入三个队列:读/写/意外队列),之后使用一定的超时值来进行查询(通过设置select函数的最后一个参数),然后再依次判断需要管理的套接字是否还在这三个队列中,如果能够在这三个队列中找到,那么就可以进行相应的处理。
3、select使用超时机制,通过设置该函数的最后一个参数(指向timeval结构体的指针),可以实现阻塞和非阻塞等状态。当该指针为NULL的时候,既是阻塞模式。当该指针指向的结构体的两个成员都设置为0时,既是非阻塞模式。使用超时查询时,一旦有套接字满足条件,即返回,返回值只满足条件的套接字的个数。返回0时表示超时时间到,仍旧没有满足条件的套接字。
4、使用select管理多个套接字的时候,请先把套接字设置为非阻塞模式,否则使用select模型基本没有任何效果。另外,使用select需要程序员统一管理每个套接字。并发服务(多进程/线程)的每个线程一般只对应一个套接字,不需要统一管理。
5、select管理的队列(读/写/意外队列)的格式如下:
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;
其中FD_SETSIZE默认是64个。这个默认值可以修改,只需在包含Winsock2.h定义这个值即可。对这个队列的操作有四个宏:FD_CLR(从队列中清楚某个套接字)、FD_ISSET(判断某套接字是不是存在队列中)、FD_SET(把某套接字放入队列中)、FD_ZERO(把队列清空为0)。
6、套接字统一管理要点:使用动态变化的数组来存储所有需要管理的套接字,如借助STL中某些模板类,使得在查询的时候更快。对处理过的套接字进行计数,当计数达到select的返回值的时候,便跳出循环进行下一次的select。
7、不同情况时的处理方式:当某个套接字存在于
Readfds队列:
之前该套接字被监听,并且有一个新的连接到来,那么此时accept可以成功。
套接字上有数据可以接收(如果指定了SO_OOBINLINE,那么此时可以接受带外数据)
连接被关闭/重置/断开。
Writefds队列:
如果这个套接字(非阻塞)之前调用了connect函数,那么连接建立成功。
数据可以发送了。
Exceptfds队列:
如果这个套接字(非阻塞)之前调用了connect函数,那么连接建立失败。
带外数据可以接收(没有指定SO_OOBINLINE)