程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Lighttpd1.4.20源碼分析之狀態機(4) 錯誤處理和連接關閉

Lighttpd1.4.20源碼分析之狀態機(4) 錯誤處理和連接關閉

編輯:關於.NET

Lighttpd所要處理的錯誤分為兩種。一種是http協議規定的錯誤,如404錯誤。另一種就是服務器運行過程中的錯誤,如write錯誤。

對於http協議規定的錯誤,lighttpd返回相應的錯誤提示文件。其實對於lighttpd而言,這不算錯誤。在返回錯誤提示文件後,相當於順利的完成了一次請求,只是結果和客戶端想要的不一樣而已。

對於服務器運行中的錯誤,狀態機會直接進入CON_STATE_ERROR狀態。大部分的情況下,這種錯誤都是由客戶端提前斷開連接所造成的。比如你不停的刷新頁面,在你刷新的時候,前一次的連接沒有完成,但被浏覽器強行斷開,這時,服務器就會出現連接錯誤。對於服務器而言,刷新前後的兩個連接是不相干的。因此,服務器在接收後一個連接的時候仍然會繼續處理前一次的連接。而這時前一次的連接已經斷開,這就產生了連接錯誤。

進入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連接。關閉ssl連接的代碼很長,但大部分都是錯誤處理。再朝後,如果連接模式不是DIRECT,調用plugins_call_handle_connection_close告訴插件連接已經關閉。到此,如果連接沒有設置keep_alive,那麼關閉連接並做一些清理工作之後就直接結束狀態機的運行了。

如果設置了keep_alive,此時可能是服務器首先關閉連接的。調用shutdown關閉連接的讀和寫。如果關閉沒有出錯,狀態機進入CON_STATE_CLOSE狀態。

 1     /*
2 * close the connection
3 */
4 if ((con->keep_alive == 1) && (0 == shutdown(con->fd, SHUT_WR)))
5 {
6 con->close_timeout_ts = srv->cur_ts;
7 connection_set_state(srv, con, CON_STATE_CLOSE);、
8 if (srv->srvconf.log_state_handling)
9 {
10 log_error_write(srv, __FILE__, __LINE__, "sd",
11 "shutdown for fd", con->fd);
12 }
13 }

上面的代碼中有這麼一句:con->close_timeout_ts = srv->cur_ts;將close_timeout_ts的值設置為當前時間。這時干嗎用的?不著急,繼續完後走。

進入CON_STATE_ERROR狀態之後,如果是keep_alive,且緩沖區中有數據還沒讀,那麼把這些數據讀出來然後直接丟棄。如果沒有數據可讀,直接設置close_time_ts為0。接著執行下面的代碼:

1 if (srv->cur_ts - con->close_timeout_ts > 1)
2 {
3 connection_close(srv, con);
4 if (srv->srvconf.log_state_handling)
5 {
6 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed for fd", con->fd);
7 }
8 }

除了輸出日志,這段代碼就是調用了connection_close。connection_close做最後的清理工作,包括調用close,將對應的connection放回緩沖池中等。前面剛剛說過,在CON_STATE_ERROR中設置了close_time_ts為cur_ts。在出了CON_STATE_ERROR後,進入CON_STATE_CLOSE,這段時間cur_ts是沒有改變的。如果前面的代碼中測試緩沖區中沒有數據可讀,此時const_time_ts是等於cur_ts。也就是說狀態機還在CON_CLOSE_STATE這個狀態,然後就退出了狀態機的大while循環。服務器進入了connection_state_mechine最後面的那個switch語句。在這個switch中,連接對應的fd被加入到fdevent系統中並監聽讀入事件。

這個時候連接都已經關閉了還有什麼能讀的啊?別急!細心的讀者應該注意到了,到這個時候,服務器僅僅是對連接調用了shutdown函數,沒有調用close函數。首先說一下shutdown和close的區別。close作用在socket fd上和作用在其他fd上的效果差不多,都是減少引用計數,當計數為0的時候釋放資源,關閉連接。shutdown函數則是針對socket fd的專門函數。shutdown可以關閉socket連接的一個方向,也就是可以只關閉寫或者只關閉讀(socket連接是全雙工的)。另外一個區別就是,如果連接是多個進程共享的,那麼在一個進程中調用close不影響其他進程使用連接,因為僅僅是引用計數減少1。而如果一個進程調用了shutdown ,那麼,不管三七二十一,系統直接咔嚓掉這個連接,其他進程看到的也是關閉的連接。還有一個區別,調用了close的socket fd,read,write函數不能再從這個fd上讀取或發送數據。而調用了shutdown的socket fd,如果緩沖區中還有數據可讀,由於此時對於進程而言,socket fd沒有關閉,read依然可以從這個fd讀數據。由於shutdown僅僅是關閉了連接,不會進行資源的釋放,要想釋放連接占用的資源,還必須調用close函數。

對於keep_alive的連接,關閉是服務器主動發起的。根據TCP協議,主動發起關閉的一方,其連接將進入TIME_WAIT狀態,此時連接所占用的資源沒有釋放。更悲劇的是要在這個狀態待很長時間。。。(默認4分鐘)對於高並發的服務器,如果服務器主動關閉大量連接,那麼服務器的資源很快就會被用光(端口,內存等),新的連接將無法建立。關於這個TIME_WAIT狀態,讀者可自行google之,網上有很多介紹的文章以及處理辦法。雖然這個TIME_WAIT狀態看似會給我們帶來很多麻煩,但是,如果沒有這個狀態我們會更麻煩。。。《UNIX Network Programming Volum1》這本神作中有詳細的介紹,有興趣的讀者可以看看。既然麻煩不可避免,那麼就盡量將麻煩的影響見到最低。連接占用的資源主要就是端口和內存。端口是沒辦法的,占用就占用了。但是,內存還是可以節省一點的。前面說了,shutdown之後,我們依然可以把緩沖區的數據讀出來。那麼,我們把這些數據讀出來以後,緩沖區所占用的內存就可以釋放了。從而降低了內存的使用。

在CON_STATE_CLOSE中,下面的代碼把緩沖區的數據讀了出來:

 1 if (ioctl(con->fd, FIONREAD, &b))
2 {
3 log_error_write(srv, __FILE__, __LINE__, "ss", "ioctl() failed", strerror(errno));
4 }
5 if (b > 0)
6 {
7 char buf[1024];
8 log_error_write(srv, __FILE__, __LINE__, "sdd", "CLOSE-read()", con->fd, b);
9
10 read(con->fd, buf, sizeof(buf));
11 }
12 else
13 {
14 /*
15 * nothing to read
16 */
17
18 con->close_timeout_ts = 0;
19 }

如果沒有數據可讀,那麼設置close_timeout_ts會使連接在後面的代碼中被關閉。如果有數據可讀,讀取數據之後,連接依然處在CON_STATE_CLOSE狀態中,連接對應的fd被加入到fdevent系統中監聽讀事件。如果緩沖區中還有數據,那麼在connection_handle_fdevent 函數中,也有上面這段代碼,再次運行之。如果數據沒有讀完,服務器將會重復的在connection_handle_fdevent函數中運行上述代碼直到數據讀完。隨著close_timeout_ts被設置為0,在下次joblist的調度中,狀態機將會關閉連接,清理所有資源。

至此,連接正式關閉。之所以這麼麻煩,就是由於連接是服務器主動關閉的,會有TIME_WAIT狀態的問題。這樣處理僅僅在內存上有了一些節省。但是具體效果怎樣,這要看系統內核是如何處理連接緩沖區的。如果各位讀者了解這方面的內容,還請不吝賜教!

另外,shutdown之後內核中的緩沖區到底會怎樣筆者並不是很確定。上面的內容只是根據代碼的推斷。還是那句話,如果給為讀者有什麼高見,還行不吝賜教啊。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved