標籤:原始碼分析 ioctl 系統 oct proc 階段 資源 eset 時間
這裡所說的錯誤有兩種:
1.http協議規定的錯誤,如404錯誤。
2.server執行過程中的錯誤。如write錯誤。
對於http協議規定的錯誤,這裡的“錯誤”是針對client的。
lighttpd返回相應的錯誤提示檔案之後,相當於順利的完畢了一次請求,僅僅是結果和client想要的不一樣而已。
對於server執行中的錯誤,狀態機器進入CON_STATE_ERROR狀態。常見的錯誤原因:client提前中斷連線。
比方你不停的重新整理頁面。在你重新整理的時候,前一次的串連沒有完畢,但被瀏覽器強行斷開。對於server而言,重新整理前後的兩個串連是不相干的,server在接收後一個串連的時候仍然會繼續處理前一次的串連。而前一次的串連已斷開,這就產生了串連錯誤。
進入CON_STATE_ERROR狀態後。假設前面的請求處理已經得到了結果。也就是http_status不為空白。那麼調用plugins_call_handle_request_done告訴外掛程式請求處理結束:
/* even if the connection was drop we still have to write it to the access log */ if (con->http_status) { plugins_call_handle_request_done(srv, con); }
假設使用了ssl,關閉ssl串連:
#ifdef USE_OPENSSL if (srv_sock->is_ssl) { /* 關閉ssl串連 */ } ERR_clear_error();#endif
接著:
switch(con->mode) { case DIRECT:#if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "emergency exit: direct", con->fd);#endif break; default: switch(r = plugins_call_handle_connection_close(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } break; } connection_reset(srv, con);
假設串連模式不是DIRECT,調用plugins_call_handle_connection_close告訴外掛程式串連已經關閉。
假設設定了keep_alive。此時可能是server首先關閉串連的。調用shutdown關閉串連的讀和寫。假設關閉沒有出錯,狀態機器進入CON_STATE_CLOSE狀態。
假設沒有設定keep_alive或者shutdown調用失敗,那麼直接關閉串連。結束狀態機器的執行。
/* close the connection */ if ((con->keep_alive == 1) && (0 == shutdown(con->fd, SHUT_WR))) { con->close_timeout_ts = srv->cur_ts; connection_set_state(srv, con, CON_STATE_CLOSE); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "shutdown for fd", con->fd); } } else { connection_close(srv, con); } con->keep_alive = 0; srv->con_closed++;
注意到。這裡server主動關閉串連的時候用的是shutdown而不是close:
1.close使用引用計數,在計數為0時才關閉通訊端;shutdown無論引用計數,直接激發TCP的正常串連終止序列。
2.close終止讀和寫兩個方向的資料傳送。shutdown能夠指定僅僅關閉串連的讀,或僅僅關閉串連的寫。或兩者均關閉。
以上lighttpd是關閉了串連的寫這一半,對於TCP通訊端來說。這叫做半關閉:當前留在通訊端發送緩衝區的資料仍然能夠發送。可是進程不能再對其調用寫函數(因為讀端沒有關閉,所以server仍然能夠讀資料),當資料發送完畢之後,TCP串連終止。
另外。注意一下:con->close_timeout_ts = srv->cur_ts;將close_timeout_ts的值設定為目前時間,在以下會用到。
在CON_STATE_CLOSE階段:
case CON_STATE_CLOSE: if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } if (con->keep_alive) { if (ioctl(con->fd, FIONREAD, &b)) { log_error_write(srv, __FILE__, __LINE__, "ss", "ioctl() failed", strerror(errno)); } if (b > 0) { char buf[1024]; log_error_write(srv, __FILE__, __LINE__, "sdd", "CLOSE-read()", con->fd, b); /* */ read(con->fd, buf, sizeof(buf)); } else { /* nothing to read */ con->close_timeout_ts = 0; } } else { con->close_timeout_ts = 0; } if (srv->cur_ts - con->close_timeout_ts > 1) { connection_close(srv, con); if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed for fd", con->fd); } } break;
假設緩衝區中還有資料,server會把資料讀出來(然後丟棄),以騰出記憶體空間。
假設沒有資料可讀,那麼設定close_timeout_ts=0,關閉串連。
假設有資料可讀,讀取資料之後,串連依舊處在CON_STATE_CLOSE狀態中(在出了CON_STATE_ERROR後。進入CON_STATE_CLOSE,這段時間cur_ts是沒有改變的。假設有資料可讀,此時const_time_ts是等於cur_ts的,因此串連並未被關閉),串連相應的fd被增加到fdevent系統中監聽讀事件。
假設緩衝區中還有資料,那麼在connection_handle_fdevent 函數中,也有上面這段代碼,再次執行之,直到資料讀完。
隨著close_timeout_ts被設定為0,在下次joblist的調度中,狀態機器將會關閉串連,清理全部資源。
至此,串連正式關閉。
關於狀態機器的簡單解析就到此為止~
Lighttpd1.4.20原始碼分析 筆記 狀態機器之錯誤處理和串連關閉