libcurl實現解析(3) - libcurl對select的使用

來源:互聯網
上載者:User

標籤:運行   通過   out   其他   推斷   net   article   signed   輸出   

1.前言

在本系列的前一篇文章中。介紹了libcurl對poll()的使用。

參考"libcurl原理解析(2) - libcurl對poll的使用"。

本篇文章主要分析curl_poll()中對select()的封裝使用。與前一篇類似,我們僅僅分離出與select相關的代碼。

2.curl_poll函數分析

這個函數中使用到的一些其他的資料結構,能夠參考前一篇文章中的介紹。本篇不再介紹。

/*這個函數是對poll()的封裝。假設poll()不存在,則使用select()替代。假設使用的是select(),而且檔案描寫敘述符fd太大,超過了FD_SETSIZE,則返回error。假設傳入的timeout值是一個負數。則會無限的等待。直到沒有有效fd被提供。

當發生這樣的情況(沒有有效fd)時。則負數timeout值會被忽略,且函數會馬上逾時。傳回值:-1 = 系統調用錯誤或fd>=FD_SETSIZE.0 = timeout.N = 返回的pollfd結構體的個數,且當中的revents成員不為0.*/int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms){struct timeval pending_tv;struct timeval *ptimeout;fd_set fds_read;fd_set fds_write;fd_set fds_err;curl_socket_t maxfd;struct timeval initial_tv = { 0, 0 };bool fds_none = TRUE; //用於驗證傳入的ufds數組是否有效 unsigned int i;int pending_ms = 0;int error; //儲存錯誤碼int r;//檢測全部fd中是否存在有效fd。 //假設至少存在一個有效fd,則fds_none置為false。停止檢測 if (ufds) {for (i = 0; i < nfds; i++) {if (ufds[i].fd != CURL_SOCKET_BAD) {fds_none = FALSE;break;}}}//假設全部的fd都是無效的(即bad socket, -1)。則等待一段時間後。直接返回。if (fds_none){r = Curl_wait_ms(timeout_ms); //此函數會隨後進行分析 return r;}//當傳入的timeout值是一個負數(堵塞情形)或者0時。則無需衡量elapsed time. //否則,擷取目前時間。

if (timeout_ms > 0) {pending_ms = timeout_ms;initial_tv = curlx_tvnow();//調用gettimeofday()或time()擷取目前時間 }//每次調用select()前都須要又一次初始化fdset,由於它們既是輸入參數又是輸出參數。FD_ZERO(&fds_read);FD_ZERO(&fds_write);FD_ZERO(&fds_err);maxfd = (curl_socket_t)-1;for (i = 0; i < nfds; i++) {ufds[i].revents = 0;if (ufds[i].fd == CURL_SOCKET_BAD) //跳過無效的fdcontinue;VERIFY_SOCK(ufds[i].fd); //檢測是否0<=fd<FD_SETSIZE.超出這個範圍。則返回-1.if (ufds[i].events & (POLLIN | POLLOUT | POLLPRI |POLLRDNORM | POLLWRNORM | POLLRDBAND)) {if (ufds[i].fd > maxfd) //擷取到最大的fd,做為select()的第一個參數。maxfd = ufds[i].fd;if (ufds[i].events & (POLLRDNORM | POLLIN))FD_SET(ufds[i].fd, &fds_read);if (ufds[i].events & (POLLWRNORM | POLLOUT))FD_SET(ufds[i].fd, &fds_write);if (ufds[i].events & (POLLRDBAND | POLLPRI))FD_SET(ufds[i].fd, &fds_err);}}//做為select()的timeout參數ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;do {if (timeout_ms > 0) {pending_tv.tv_sec = pending_ms / 1000;pending_tv.tv_usec = (pending_ms % 1000) * 1000;}else if (!timeout_ms) {pending_tv.tv_sec = 0;pending_tv.tv_usec = 0;}//真正調用select(). 第2。3,4參數已經在前面初始化(清空)過了。r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);if (r != -1) //select調用成功,結束迴圈break;//select調用失敗。返回-1。通過errno能夠擷取到錯誤碼。

error = SOCKERRNO; //宏定義。

#define SOCKERRNO (errno) //以下的error_not_EINTR 是宏定義. //#define error_not_EINTR (0 || error != EINTR) if (error && error_not_EINTR) //檢測是否存在error,且不是EINTR錯誤break;//沒有出錯或者存在EINTR錯誤。則推斷是否繼續運行select()if (timeout_ms > 0) {//elapsed_ms是宏定義。//#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv) pending_ms = timeout_ms - elapsed_ms;if (pending_ms <= 0) {r = 0; //類比select逾時的情形 break;}}} while (r == -1);/*如今能夠對上面的這個while迴圈總結一下:1.假設select調用成功(即傳回值>=0),則結束迴圈2.假設select調用失敗(即傳回值==-1),則檢測errno是否為EINTR錯誤。 假設不是EINTR,則結束迴圈。假設是EINTR,則檢測是否已經逾時。逾時則結束迴圈,沒有逾時則繼續select()。 */ if (r < 0) //select()調用失敗return -1;if (r == 0) //select()逾時return 0;//select()調用成功, 統計當中狀態發生改變的fd的個數,儲存至r.r = 0;for (i = 0; i < nfds; i++) {ufds[i].revents = 0;if (ufds[i].fd == CURL_SOCKET_BAD)continue;if (FD_ISSET(ufds[i].fd, &fds_read)) //fd可讀ufds[i].revents |= POLLIN;if (FD_ISSET(ufds[i].fd, &fds_write)) //fd可寫ufds[i].revents |= POLLOUT;if (FD_ISSET(ufds[i].fd, &fds_err)) //fd出錯ufds[i].revents |= POLLPRI;if (ufds[i].revents != 0)r++;}return r;}

這個函數運行完畢後。第一個輸入參數ufds中的成員revents可能會被改動。最後。函數返回select()的實際傳回值。

3.curl_wait_ms函數分析

以下是curl_wait_ms()函數的詳細實現。

也是基於poll或者select來實現的。這裡也僅僅討論它的select()實現版本號碼。


/*這個函數用於等待特定的時間值。在函數Curl_socket_ready()以及Curl_poll()中被調用。當沒有提供不論什麼fd來檢測時。則僅僅是等待特定的一段時間。

假設是在windows平台下,則winsock中的poll()以及select()逾時機制,須要一個有效socket fd.這個函數不同意無限等待,假設傳入的值是0或者負數。則馬上返回。逾時時間的精度以及最大值。取決於系統。

傳回值:-1 = 系統調用錯誤,或無效的輸入值(timeout),或被中斷。0 = 指定的時間已經逾時*/int Curl_wait_ms(int timeout_ms){struct timeval pending_tv;struct timeval initial_tv;int pending_ms;int error;int r = 0;if (!timeout_ms) //逾時值為0,馬上返回 return 0;if (timeout_ms < 0) //不能為負數 {SET_SOCKERRNO(EINVAL);return -1;}pending_ms = timeout_ms;initial_tv = curlx_tvnow();do {pending_tv.tv_sec = pending_ms / 1000;pending_tv.tv_usec = (pending_ms % 1000) * 1000;r = select(0, NULL, NULL, NULL, &pending_tv);if (r != -1) //select()調用成功,則跳出迴圈 break;//select調用失敗。返回-1。通過errno能夠擷取到錯誤碼。 error = SOCKERRNO; //宏定義。

#define SOCKERRNO (errno) //以下的error_not_EINTR 是宏定義. #define error_not_EINTR (0 || error != EINTR) if (error && error_not_EINTR) ////檢測是否存在error,且不是EINTR錯誤 break;//elapsed_ms是宏定義: //#define elapsed_ms (int)curlx_tvdiff(curlx_tvnow(), initial_tv) pending_ms = timeout_ms - elapsed_ms;if (pending_ms <= 0){r = 0; //類比select逾時的情形 break;}} while (r == -1);//確保傳回值r僅僅能為-1(逾時失敗)或者0(逾時成功)。

//r不可能大於0,由於傳入到select()函數的3個fdset數組所有都是NULL。假設select的傳回值>0,則說明調用出問題了。 //故這裡會將r置為-1,即調用逾時失敗。 if (r)r = -1;return r;}


libcurl實現解析(3) - libcurl對select的使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.