Thttpd源程序解析20 請求回送過程詳解

此文章之前講的請求處理過程說明請求處理過程校驗請求格式、分析請求內容、處理請求將數據存儲在緩存數組中並將此連接的文件描述符的狀態設置為發送狀態添加在管理數組中,當程序循環執行到檢測此文件的描述符時檢測到此文件的狀態為發送狀態將會執行handle_send函數,此函數的作用是確定發送數據的數據量併發送給客戶端,根據發送的狀態做出相應的處理。

handle_send函數

(1)根據是否設置限制最大發送數據的數據量進行設置最大發送數據的數據量,沒有設置發送數據限制設置最大發送數據字節數為1000000000字節,反之設置最大發送字節數為250000000字節。

(2)根據hc->responselen的值是否為0判斷是隻發送響應頭文件還是發送響應頭文件和內容文件。

(3)根據寫數據的結果進行處理如果寫數據失敗但是錯誤值為EINTR退出函數,如果寫數據失敗但是錯誤值為EWOULDBLOCK或者是EAGAIN在文件數組中刪除此文件描述符,然後調用定時函數進行重新處理發送函數退出函數。如果為其他錯誤關閉連接退出函數。

(4)對於hc->responselen的值不為0的,但是發送的數據小於hc->responselen的值的更新發送數據的長度和下一次發送數據的位置。

(5)更新需要發送數據的開始位置和截止位置。

(6)對於數據完全被髮送完的關閉連接退出函數。

(7)對於數據沒有發送完的如果延遲時間大於最小延遲時間的設置延遲時間為延遲時間減去最小延遲時間。

(8)如果限制最大發送數據但是每秒發送的數據大於設置的最大發送限制的進行發送數據的網絡速度限制。

流程圖

源程序

static void handle_send( connecttab* c, struct timeval* tvP ) { size_t max_bytes; int sz, coast; ClientData client_data; time_t elapsed; httpd_conn* hc = c->hc; int tind; /**對於沒有限制的處理*/ if ( c->max_limit == THROTTLE_NOLIMIT ) { max_bytes = 1000000000L; } /**對於有限制的處理*/ else { max_bytes = c->max_limit / 4; /* send at most 1/4 seconds worth */ } /* Do we need to write the headers first? */ /**對於hc->responselen的值為0的處理,即沒有數據的處理*/ if ( hc->responselen == 0 ) { /* No, just write the file. */ sz = write(hc->conn_fd, &(hc->file_address[c->next_byte_index]),MIN( c->end_byte_index - c->next_byte_index, max_bytes ) ); } /**對於hc->responselen的值不為0的處理*/ else { /* Yes. We'll combine headers and file into a single writev(), ** hoping that this generates a single packet. */ /**一次函數對象*/ struct iovec iv[2]; iv[0].iov_base = hc->response; iv[0].iov_len = hc->responselen; iv[1].iov_base = &(hc->file_address[c->next_byte_index]); iv[1].iov_len = MIN( c->end_byte_index - c->next_byte_index, max_bytes ); /**一次寫兩個數據塊的數據*/ sz = writev( hc->conn_fd, iv, 2 ); } /**對於寫數據錯誤的處理但是錯誤的值為eintr的處理*/ if ( sz < 0 && errno == EINTR ) { return; } /***/ if ( sz == 0 ||( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) ) { /* This shouldn't happen, but some kernels, e.g. ** SunOS 4.1.x, are broken and select() says that ** O_NDELAY sockets are always writable even when ** they're actually not. ** ** Current workaround is to block sending on this ** socket for a brief adaptively-tuned period. ** Fortunately we already have all the necessary ** blocking code, for use with throttling. */ c->wouldblock_delay += MIN_WOULDBLOCK_DELAY; c->conn_state = CNST_PAUSING; fdwatch_del_fd( hc->conn_fd ); client_data.p = c; if ( c->wakeup_timer != (Timer*) 0 ) { syslog( LOG_ERR, "replacing non-null wakeup_timer!" ); } c->wakeup_timer = tmr_create(tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 ); if ( c->wakeup_timer == (Timer*) 0 ) { syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" ); exit( 1 ); } return; } /**對於寫失敗的處理*/ if ( sz < 0 ) { /* Something went wrong, close this connection. ** ** If it's just an EPIPE, don't bother logging, that ** just means the client hung up on us. ** ** On some systems, write() occasionally gives an EINVAL. ** Dunno why, something to do with the socket going ** bad. Anyway, we don't log those either. ** ** And ECONNRESET isn't interesting either. */ if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET ) { syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl ); } clear_connection( c, tvP ); return; } /* Ok, we wrote something. */ c->active_at = tvP->tv_sec; /* Was this a headers + file writev()? */ /**對於調用寫的方式不同的處理*/ if ( hc->responselen > 0 ) { /* Yes; did we write only part of the headers? */ if ( sz < hc->responselen ) { /* Yes; move the unwritten part to the front of the buffer. */ int newlen = hc->responselen - sz; (void) memmove( hc->response, &(hc->response[sz]), newlen ); hc->responselen = newlen; sz = 0; } else { /* Nope, we wrote the full headers, so adjust accordingly. */ sz -= hc->responselen; hc->responselen = 0; } } /* And update how much of the file we wrote. */ /**更新當前已經寫的數據的數量*/ c->next_byte_index += sz; c->hc->bytes_sent += sz; for ( tind = 0; tind < c->numtnums; ++tind ) { throttles[c->tnums[tind]].bytes_since_avg += sz; } /* Are we done? */ /**對於已經寫完數據的處理*/ if ( c->next_byte_index >= c->end_byte_index ) { /* This connection is finished! */ finish_connection( c, tvP ); return; } /* Tune the (blockheaded) wouldblock delay. */ /**對於沒有寫完數據的處理*/ if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY ) { c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY; } /* If we're throttling, check if we're sending too fast. */ if ( c->max_limit != THROTTLE_NOLIMIT ) { elapsed = tvP->tv_sec - c->started_at; if ( elapsed == 0 ) { elapsed = 1; /* count at least one second */ } if ( c->hc->bytes_sent / elapsed > c->max_limit ) { c->conn_state = CNST_PAUSING; fdwatch_del_fd( hc->conn_fd ); /* How long should we wait to get back on schedule? If less ** than a second (integer math rounding), use 1/2 second. */ coast = c->hc->bytes_sent / c->max_limit - elapsed; client_data.p = c; if ( c->wakeup_timer != (Timer*) 0 ) { syslog( LOG_ERR, "replacing non-null wakeup_timer!" ); } c->wakeup_timer = tmr_create(tvP, wakeup_connection, client_data,coast > 0 ? ( coast * 1000L ) : 500L, 0 ); if ( c->wakeup_timer == (Timer*) 0 ) { syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" ); exit( 1 ); } } } /* (No check on min_limit here, that only controls connection startups.) */ }