1. 程式人生 > >網絡編程——I/O復用

網絡編程——I/O復用

head accep 傳輸 創建文件 接收 ont tps perror ike

int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);

  nfds:是一個整數值,是指集合中所有文件描述符的範圍,即所有文件描述符的最大值加1。在Windows中這個參數的值無所謂,可以設置不正確。   readfds:(可選)指針,指向一組等待可讀性檢查的套接口。   writefds:(可選)指針,指向一組等待可寫性檢查的套接口。   exceptfds:(可選)指針,指向一組等待錯誤檢查的套接口。   timeout:select()最多等待時間,對阻塞操作則為NULL。   如果對readfds、writefds或exceptfds中任一個組類不感興趣,可將它置為空NULL。 在socket.h頭文件中共定義了四個宏來操作描述字集。   FD_ZERO(fd_set *fdset);將指定的文件描述符集清空,在對文件描述符集合進行設置前,必須對其進行初始化,如果不清空,由於在系統分配內存空間後,通常並不作清空處理,所以結果是不可知的。
FD_SET(fd_set *fdset);用於在文件描述符集合中增加一個新的文件描述符。
FD_CLR(fd_set *fdset);用於在文件描述符集合中刪除一個文件描述符。
FD_ISSET(int fd,fd_set *fdset);用於測試指定的文件描述符是否在該集合中。 服務端代碼:(客戶端基本不變)
  1
#include "common.h" 2 3 struct cli_t 4 { 5 int cfd; 6 struct sockaddr_in caddr; 7 struct cli_t *next; 8 }; 9 10 typedef struct stu//學生結構體 11 { 12 int id; 13 char name[20]; 14 int score; 15 struct stu *next; 16 }Stu,*PStu; 17 18 void fun(int sig)//捕捉信號
19 { 20 printf("連接中斷\n"); 21 return; 22 } 23 24 unsigned int ListLength(PStu ptr) //計算鏈表長度 25 { 26 unsigned int l=0; 27 while(ptr!=NULL) 28 { 29 l++; 30 ptr=ptr->next; 31 } 32 return l; 33 } 34 35 PStu ListSort(PStu ptr) //鏈表排序 36 { 37 PStu sorthead=NULL;
38 unsigned int l=ListLength(ptr); 39 while(l>0) 40 { 41 PStu p1=ptr; 42 PStu p2=p1; 43 int max=p1->score; 44 while(p1!=NULL) 45 { 46 if(p1->score>max) 47 {max=p1->score; p2=p1;} 48 p1=p1->next; 49 } 50 PStu p3=ptr; 51 if (p3==p2) 52 ptr=p3->next; 53 else 54 { while(p3->next!=p2) 55 p3=p3->next; 56 57 p3->next=p2->next; 58 } 59 60 PStu r; 61 p2->next=NULL; 62 if(sorthead==NULL) 63 sorthead=p2; 64 else 65 r->next=p2; 66 67 r=p2; 68 l--; 69 } 70 return sorthead; 71 } 72 73 void show(PStu head)//打印鏈表函數 74 { 75 printf("學號\t姓名\t分數\n"); 76 while(head!=NULL) 77 { 78 printf("%d\t%s\t%d\n",head->id,head->name,head->score); 79 head=head->next; 80 } 81 return ; 82 } 83 84 int read_file(int agv)//接受信息(子線程) 85 { 86 PStu head=NULL; 87 Stu *ptr; 88 PStu r; 89 int nfd; 90 int ret; 91 nfd=(int)agv;//接收傳參nfd 92 93 signal(SIGPIPE,fun); 94 printf("read...\n"); 95 while(1) 96 { 97 ptr=malloc(sizeof(Stu)); 98 ret=read(nfd,ptr,sizeof(Stu)); 99 if(ret<0) 100 { 101 perror("read");return -1; 102 } 103 if(ret==0) 104 { 105 head=ListSort(head); 106 show(head); 107 printf("read over\n"); 108 return 0; 109 } 110 if(ret>0);//把鏈表讀出來 111 { 112 ptr->next=NULL; 113 if(head==NULL) 114 { 115 head=ptr; 116 } 117 else 118 { 119 r->next=ptr; 120 } 121 r=ptr; 122 } 123 } 124 return 1; 125 } 126 127 int main()//主線程,一直工作,接收客戶端 128 { 129 PStu head=NULL; 130 pthread_t pthid; 131 int ret; 132 int fd,nfd; 133 struct sockaddr_in saddr,caddr; 134 int addr_len; 135 signal(SIGPIPE,fun); 136 fd = socket(AF_INET,SOCK_STREAM,0); 137 if(fd<0) 138 { 139 perror("socket"); 140 return -1; 141 } 142 saddr.sin_family = AF_INET; 143 saddr.sin_port = htons(9000); 144 inet_pton(AF_INET,"192.168.6.128",&saddr.sin_addr.s_addr); 145 ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr)); 146 if(ret<0) 147 { 148 perror("bind"); 149 goto END; 150 } 151 ret = listen(fd,20); 152 if(ret<0) 153 { 154 perror("listen"); 155 goto END; 156 } 157 158 fd_set set,rset;//創建文件描述符集合 159 int *pfd; 160 struct cli_t *chead=NULL; 161 struct cli_t *pcli; 162 int maxfd; 163 164 FD_ZERO(&set);//將文件描述符集清空 165 FD_SET(fd,&set);//在文件描述符集合中增加一個新的文件描述符 166 maxfd=fd; 167 168 while(1)//循環接收多個客戶端 169 { 170 rset=set;//把set的文件描述符拷貝給reset,防止set發生改變 171 printf("select...\n"); 172 ret=select(maxfd+1,&rset,NULL,NULL,NULL);//對讀進行操作等待(無限等待) 173 printf("select over && ret= %d\n",ret); 174 175 if(ret<0) 176 { 177 perror("select"); 178 break; 179 } 180 if(FD_ISSET(fd,&rset))//檢查fd是否在文件描述符集中, select將更新這個集合,只保留符合條件的套節字在這個集合裏面 181 { 182 //接收連接 183 addr_len = sizeof(caddr); 184 printf("accept..\n"); 185 nfd = accept(fd,(struct sockaddr*)&caddr,&addr_len); 186 if(nfd<0) 187 { 188 perror("accept"); 189 } 190 printf("accept over..\n"); 191 //加入集合 192 FD_SET(nfd,&set);//把nfd加入到set集合中 193 if(nfd>maxfd) 194 maxfd=nfd; 195 196 // 加入鏈表 197 pcli=malloc(sizeof(struct cli_t)); 198 pcli->cfd=nfd; 199 pcli->caddr=caddr; 200 pcli->next=chead; 201 chead=pcli; 202 } 203 for(pcli=chead;pcli!=NULL;pcli=pcli->next)//循環遍歷,哪個客戶端準備好了(寫入並傳輸了數據就讀出數據) 204 { 205 int tfd=pcli->cfd; 206 if(!FD_ISSET(tfd,&rset))//防止客戶端沒寫入數據時阻塞在read裏面 207 continue; 208 ret=read_file(tfd); 209 if(ret<=0) 210 { 211 printf("read ret =0 tcp broken\n"); 212 FD_CLR(tfd,&set);//客戶端讀取完畢且關閉,清空集合 213 } 214 }printf("asd"); 215 } 216 END: 217 close(fd); 218 return 0; 219 }

網絡編程——I/O復用