日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UNIX网络编程笔记(6):I/O复用之select函数

發布時間:2023/11/30 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UNIX网络编程笔记(6):I/O复用之select函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上一講中我們正確處理了僵尸子進程,使得這個簡單的服務器更加健壯。不幸的是,這個程序仍然有問題。想象一下,如果一個客戶正在和一個服務器子進程連接建立完畢正在通話,而服務器子進程意外終止(比如kill),服務器TCP向客戶TCP發送一個FIN,但客戶端正在調用fgets函數等待用戶輸入字符而得不到這個FIN,直到套接字讀為止(可能過了很長時間)。

1、來看看問題是什么

首先啟動服務器與客戶,建立連接:


客戶輸入nihao后服務器正確回射,然后kill掉服務器子進程后客戶輸入and...,輸出錯誤:


服務器端重啟(上一講中解決的問題)。

這個過程發生了什么?

(1)當kill掉服務器子進程后,服務器父進程正確處理了SIGCHLD信號。服務器也向客戶發送一個FIN,客戶回應一個ACK,TCP連接終止的前半部分完成;

(2)客戶上沒有發生任何特殊的事,客戶TCP接收到服務器的FIN后回應一個ACK,但問題是客戶正阻塞在fgets上,等待用戶輸入;

(3)在客戶上再輸入and...,導致錯誤發生:str_cli調用write,客戶TCP接著把數據發送給服務器,但是客戶FIN的接收并沒有告知客戶TCP服務器進程已經終止。當服務器TCP接收到客戶的數據時,發送一個RST;

(4)但是客戶進程看不到這個RST,因為調用write后直接調用readline,并且由于之前接收的FIN,導致readline直接返回0(表示EOF)。但客戶沒有預期收到EOF,于是出錯;

(5)客戶終止,所有打開的描述符關閉;

問題的根本在于,當FIN到達套接字時,客戶正阻塞在fgets調用上。客戶實際上在應對兩個描述符:套接字和用戶輸入,它不能單純阻塞在某個特定的源上。select函數可以解決這個問題。

2、select函數

有些進程需要一種預先告知內核的能力,使得內核一旦發現進程指定的一個或多個I/O條件就緒時,它就通知進程。這個能力就是I/O復用。

select函數允許進程指示內核等待多個事件中的任何一個發生,并只在有一個或多個事件發生或經歷一段指定的時間后才喚醒它。

下面的例子指出select可以告知內核在什么條件下發生時返回:

  • 集合{1,4,5}中的任何描述符準備好讀;
  • 集合{2,7}中的任何描述符準備好寫;
  • 集合{1,4}中的任何描述符由異常條件待處理;
  • 已經過了12.8秒;
也就是說,調用select函數告知內核對哪些描述符(就讀、寫或異常條件)感興趣以及等待多長時間。描述符不局限于套接字,任何描述符都可以使用select。 函數定義如下,包含在<sys/select.h>頭文件中: #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);函數如果有描述符就緒就返回數量,超時返回0,出錯返回-1。 參數含義如下: maxfdp1:指定待測試描述符的個數,通常是待測試的最大描述符加1; 中間三個參數指定內核測試讀、寫和異常條件的描述符,這里設置成NULL即可; timeval:告知內核等待所指定的時間,有三種情況: (1)永遠等待:設置為NULL; (2)等待固定時間:在有一個描述符就緒后返回,但不超過參數所指定的時間; (3)根本不等待:檢查描述符后立刻返回; 3、重寫客戶端中的str_cli函數 客戶的套接字上的三個條件: (1)如果對端TCP發送數據,那么該套接字變為可讀,并且read返回一個大于0的值(即讀入數據的字節數); (2)如果對端TCP發送一個FIN,那么該套接字變為可讀,read返回0(EOF); (3)如果對端TCP發送一個RST(對端主機崩潰并重啟),那么該套接字變為可讀,并且read返回-1,errno中有錯誤碼; 下面是修改后的str_cli函數: void str_cli(FILE *fp,int sockfd) {int maxfdp1;fd_set rset;char sendline[MAXLINE],recvline[MAXLINE];FD_ZERO(&rset);int n;for(;;){FD_SET(fileno(fp),&rset);FD_SET(sockfd,&rset);maxfdp1=max(fileno(fp),sockfd)+1;if((n=select(maxfdp1,&rset,NULL,NULL,NULL))==0){printf("select timeout\n");return;}else if(n<0){printf("select error\n");return;}if(FD_ISSET(sockfd,&rset)){if(readline(sockfd,recvline,MAXLINE)==0){printf("str_cli:server terminated prematurely\n");return;}fputs(recvline,stdout);}if(FD_ISSET(fileno(fp),&rset)){if(fgets(sendline,MAXLINE,fp)==NULL)return;write(sockfd,sendline,strlen(sendline));}} }函數首先檢查套接字,如果套接字是可讀的,那么就用readline讀入回射文本并輸出;如果標準輸入是可讀的,就調用fgets讀入一行,然后寫入套接字。 運行結果: (1)服務器進程終止后客戶端直接終止:

(2)服務器端:
可以看到,程序運行良好。

總結

以上是生活随笔為你收集整理的UNIX网络编程笔记(6):I/O复用之select函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。