select函数
select函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或多个事件发生或经历一段指定的时间 去唤醒它。
#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset,
fd_set *exceptset, const struct timeval *timeout);
参数:
maxfdp1:最大监视文件描述符。
readset:读事件描述符集。
writeset:写事件描述符集。
exceptset:异常事件描述符集。
timeout:等待时间。
关于timeout这个参数有以下三中可能:
(1)永远等待:仅在有一个描述符准备好I/O时才返回。只需将该参数设置为空指针。
(2)等待一段固定时间:在有一个I/O描述符准备好I/O时或设定的时间到达时返回。
(3)根本不等待:检查描述符后立即返回(即轮询),则该参数必须指向一个为0的timeval的结构体。
关于readset,writeset,expectset三个参数:
FD_ZERO(fd_set *fdset); //将指定的文件描述符集清空
FD_SET(int fd, fd_set *fdset);//在文件描述符集合添加一个新的文件描述符
FD_CLR(int fd, fd_set *fdset);//在文件描述符集合中删除一个文件描述符
FD_ISSET(int fd, fd_set *fdset);//测试指定的文件描述符是否在该集合中
select函数修该由指针指readset,writeset,expectset所指向的描述符集,因而这三个参数都是值-结果参数。调用该函数时,我们指定所关心的描述符的值,该函数返回时,结果将指示哪些描述符已就绪。
关于maxfdp1:
该参数指定待测试的描述符个数,它的值是待测时的最大描述数的值+1。
该函数的返回值表示跨所有描述符集合的已就绪的总位数。如果在任何描述符就绪之前,定时器先到,则返回0。错误则返回-1。
void str_cli(FILE *fp, int sockfd)
{
int maxfdp1;
fd_set rset;
char sendline[MAXLINE], recvline[MAXLINE];
FD_ZERO(&rset);
while(1)
{
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = ((fileno(fp) > sockfd) ? fileno(fp) : sockfd) + 1;
select(maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset))
{
if (read(sockfd, recvline, MAXLINE) == 0)
{
printf("str_cli: recvline terminated prematurely\n");
exit(0);
}
fputs(recvline, stdout);
}
if (FD_ISSET(fileno(fp), &rset))
{
if (fgets(sendline, MAXLINE, fp) == NULL)
{
return ;
}
write(sockfd, sendline, MAXLINE);
}
bzero(recvline, MAXLINE);
bzero(sendline, MAXLINE);
}
}
shutdown函数
终止网络连接的常用方法时调用close函数。不过close有两个限制,却可以使用shutdown来避免。
(1)close把描述的引用计数减一,仅在该计数变为0时才关闭套结字。而使用shutdown可以不管引用计数就激发TCP的正常连接终止序列。
(2)close终止读和写两个方向的数据传递。既然TCP是全双工的,有时候我们需要告知对端我们已经完成了数据发送,即使对端仍有数据要发送给我们。
#include <sys/socket.h>
int shutdown(int sockfd, int howto);
该函数的行为依赖于howto参数的值。
SHUT_RD: 关闭连接的这一半———套结字中不再有数据可接收,而且套结字接受缓冲区中的现有数据都被丢弃。进程不能再对这样的套结字调用任何读函数。对一个TCP套结字这样调用shutdown函数后,由该套结字接收的来自对端的任何数据都被确认,然后悄悄丢弃。
SHUT_WR: 关闭连接的写这一半———对于TCP套结字,这被称为半关闭。当前留在套结字发送缓冲区的数据将被发送掉,后跟TCP的正常连接终止序列。进程不能再对这样的套结字调用任何写函数。
SHUT_RDWR: 连接读半部和写半部都关闭———相当于对上面两个参数一起调用。
void str_cli(FILE *fp, int sockfd)
{
int maxfdp1;
fd_set rset;
char sendline[MAXLINE], recvline[MAXLINE];
int stdineof = 0;
FD_ZERO(&rset);
while(1)
{
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = ((fileno(fp) > sockfd) ? fileno(fp) : sockfd) + 1;
select(maxfdp1, &rset, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &rset))
{
if (read(sockfd, recvline, MAXLINE) == 0)
{
if(stdineof == 1)
return;
else {
printf("str_cli: recvline terminated prematurely\n");
exit(0);
}
}
fputs(recvline, stdout);
}
if (FD_ISSET(fileno(fp), &rset))
{
if (fgets(sendline, MAXLINE, fp) == NULL)
{
stdineof = 1;
shutdown(sockfd, SHUT_WR);
FD_CLR(fileno(fp), &rset);
continue;
}
write(sockfd, sendline, MAXLINE);
}
bzero(recvline, MAXLINE);
bzero(sendline, MAXLINE);
}
}