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