網絡編程第6章:select和poll

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;
		}
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章