select
#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdql,fd_set *readset,fd_set *wirteset,fd_set*exceptset,const struct timvale *timeout){}
int select(待测试的描述符个数(值是待测试的最大描述符加1),测试读写异常条件的描述符,任何一个就绪所花费的时间),
改函数返回的时候,三个描述符集合,结果将显示哪些描述符已经就绪,(未就绪描述符值将为0),所以每次调用select的时候,都要待测试描述符置位1.1
关于timeout这是一个结构体变量,如下
struct timeval {
long tv_sec;//秒
long tv_uscc;//微妙
}//表示内核等待一个描述符就绪需要花费多少时间
这个参数有三种可能:
设置为空值:仅在有一个描述符就绪的时候才返回,否则永远等待下去
等待一段固定的时间:在指定时间内如果有就绪就返回
不等待:设置为0的时候,轮询进行检查
描述符的测试:
描述符集是fd_set,首先进行初始化,
fd_set rset;
FD_ZERO(rset);
FD_SET(1,set);
FD_SET(3,set);
三个描述符集是值–结果参数,在函数返回的时候,任何未就绪描述符将会置位0.所以每次调用select的时候,都要待测试的描述符置为1.
这个函数的返回值是就绪描述符的个数.
就绪条件
-接收低水位标记是让select返回“可读”时套接字接收缓冲区中所需的数据量。对于TCP和UDP,其默认值为1。
-发送低水位标记是让select返回“可写”时套接字发送缓冲区中所需的可用空间。对于TCP,其默认值为2048。
对于UDP,只要一个UDP套接字的发送缓冲区大小大于该套接字的低水位标记,该UDP套接字就总是可写。
描述符准备好读的就绪条件:
1 如果接收缓冲区的数量大于套接字低水位标记的当前大小(我理解的是内容可以被读入套接字缓冲区的时候) ,
2 接收了FIN
3 监听套接字完成的连接数不为0
4 有错误待处理
描述符准备好写的就绪条件:
1 套接字缓冲区数量大于发送缓冲区的低水位标记
2 写半部关闭?
3 connect执行完毕(已经建立链接或者链接已经以失败告终)
4 有套接字错误待处理
阻塞于select的客户端处理程序
#include "unp.h"
void str_cli(FILE *fp,int sockfd){
int maxfdp1;
fd_set rset;
char sendline[MAXLINE],recvline[MAXLINE];
FD_ZERO(&rset);
for(;;){
FD_SET(fileno(fd),&rset);
FD_SET(sockfd,&rset);
maxfdp1=max(fileno(fd),sockfd)+1;
select(maxfdp1,&rset,NULL,NULL,NULL);
if(FD_ISSET(sockfd,&rset)){//如果套接字可读
if(Readline(scokfd,recvline,MAXLINE)==0){
printf("服务器意外终止");
}
Fputs(revline,stdout);
}
if(FD_ISSET(fileno(fd),&rset)){//如果标准输入可读
if(Fgets(sendline,&rset)==NULL){
return ;
}
writen(sockfd,sendline,strlen(sendline));
}
}
}
但是这个并不正确:
原因:输入EOF之后,这个函数返回到main函数,然后客户端程序终止,但是仍旧有报文存在于网络中,在传输中的报文来不及接收客户端就终止了.
这个错误可以使用shutdown函数来关闭TCP一半连接.
shutdown 函数:
shutdown函数可以关闭读链接,写链接,连接.
shutdown函数是无关于close的引用计数的
int shutdown(int sockfd,int howto);
howto:
SHUT_RD:关闭读
SHUT_WR:关闭写
SHUT_RDWR:关闭连接
poll函数:
#include<poll.h>
int poll(struct pollfd *fdarray,unsigned long nfds,int timeout)
其中:
struct{
int fd;
short events;//测试的条件
short revents;//返回该描述符的状态
}
int poll(测试数组(值结果参数),第一个数组的个数,等待时间)
返回值,如果为负数,那么就是发生错误,否则返回就绪的描述符个数
如果不再关心某个描述符,就把其对应的fd设置为0
关于引起poll返回的条件:P145
#include "unp.h"
#include<sys/socket.h>
int main(int argc,char **argv){
int i,max,listenfd,connfd,sockfd;
int nready;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr,servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
listen(listenfd,LISTENQ);
client[0].fd = listenfd;
client[0].evets = POLLRDNORM;
for(int i=1;i<OPEN_MAX;i++)
client[i].fd = -1;//将其他置位不可用的时间
maxi=0;
}
for(;;){
nready =Poll(client,maxi+1,INFTIM);
if(client[0].revents && POLLRDNORM){//如果有了新链接
client = sizeof(cliaddr);
connfd = accept(listenfd,(SA*)&cliaddr,&clilen);//接收一个新的套接字
for(int i=1;i<OPEN_MAX;i++){
if(client[i].fd<0){
client[i].fd = connfd;
break;
}
}
if(i==OPENMAX){
printf("太多客户端");
}
if(--nready<=0){//没有更多客户
continue;
}
}
for(int i=1;i<=maxi;i++){
if((sockfd=client[i].fd)<0)continue;
if(client[i].revents & (POLLRDNORM | POLLERR)){
if((n=read(sockfd,buf,MAXLINE))<0){
if(errno == ECONNRESET){
close(sockfd);
client[i].fd=-1;
}else{
err_sys("读入错误");
}
}else if(n==0){
close(sockfd);
client[i].fd=-1
}else writen(sockfd,buf,n);
if(--nready<=0)break;
}
}
}