阻塞和非阻塞
在win32的socket编程中,默认是阻塞的,使用阻塞其实可以简化程序员的代码量,但是通常效率收很低的,因为通常需要等待,非阻塞可以提高效率但是增加了程序员的代码量,因此引入了select等模型。
常见的阻塞函数
- accept函数 这个函数需要等待用户的连接请求,没有用户连接便会一直等待。
- recv函数 这个函数会检查套接字缓冲区里面是否有发送过来的数据,如果没有便会等待,如果有便会将数据放入指定的变量中。
- connect函数 这个函数是用户发送连接请求时,便开始等待,直到三次握手成功返回。
- send函数 这个函数是将指定变量中的数据放入套接字缓冲区中,当套接字缓冲区已经没有多余的空间不足以存入指定的数据时便会阻塞。
非阻塞的工作原理
默认情况下是阻塞的,因此我们需要手动设置非阻塞模式,使用函数ioctlsocket()函数来进行设置。
将socket设置成非阻塞后不管函数是否执行成功都会马上返回,大多情况下这些函数都是执行失败的,这些失败和函数的调用失败是不同的,通常是指recv函数向套接字缓冲区中取数据,但是套接字缓冲区中没有输据,因此便会报错,这类报错代码为WSAEWOULDBLOCK,使用WSAGetLastError()函数来获取错误代码,下面代码是在非阻塞模式下的实现。
while(1)
{
if(recv(s,msg,sizeof(msg),0)<0)
{
err=WSAGetLastError()
{
printf("error\n");
break;
}
}
else
{
printf();
......
}
}
select模型的引出
通过上面的讲述可知,我们可以使用ioctlsocket函数设置非阻塞模式,并且需要通过一些函数的返回值来判断有没有收到数据,上面的操作是比较麻烦的,因此便引出了select模型,使用select模型可以简化上面的操作。
select模型的讲解
- 首先需要了解一个结构体fd_set,这个结构体有两个成员,第一个表示这个结构体中有多少套接字,第二个表示套接字集合。
typedef struct fd_set{
unsigned int fd_count;
SOCKET fd_array[FD_SETSIZE];
}fd_set;
- 操作fd_set的函数
FD_ZERO(* set)函数 这个函数表示初始化set指向的套接字集合,将指向的套接字清空,进行初始化。
FD_CLR(s,* set)函数 这个函数表示清除set指向的套接字集合中的s套接字。
FD_SET(s,* set)函数 这个函数表示将s的套接字添加到set指向的套接字集合中。
FD_ISSET(s,* set)函数 这个函数表示检查set指向的套接字集合中是否包含s这个套接字。 - select函数原型
select(
int nfds,
fd_set readfds,
fd_set writefds,
fd_set exceptfds,
const struct timeval *timeout
);
这个函数执行失败返回值小于零,执行成功返回可读,可写,错误的总个数。
中间三个表示需要操作的三个套接字集合,可以为NULL,但不能全为NULL,readfds表示需要检查可读的套接字集合返回可以读的套接字。writefds同理。