青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

那誰的技術博客

感興趣領域:高性能服務器編程,存儲,算法,Linux內核
隨筆 - 210, 文章 - 0, 評論 - 1183, 引用 - 0
數(shù)據(jù)加載中……

lighttpd1.4.18代碼分析(八)--狀態(tài)機(2)CON_STATE_READ狀態(tài)

今天繼續(xù)沿著狀態(tài)轉換序列圖講解狀態(tài)機,這次到了CON_STATE_READ狀態(tài), 首先看看connection_state_machine函數(shù)部分的代碼:

        // 讀             
        case CON_STATE_READ_POST:
        
case CON_STATE_READ:
            
if (srv->srvconf.log_state_handling) {
                log_error_write(srv, __FILE__, __LINE__, 
"sds",
                        
"state for fd", con->fd, connection_get_state(con->state));
            }

            connection_handle_read_state(srv, con);
            
break;
可以看到,CON_STATE_READ_POST狀態(tài)和CON_STATE_READ狀態(tài)調用的同一段代碼,不過我們今天講解的CON_STATE_READ狀態(tài),CON_STATE_READ_POST狀態(tài)在后面講解.

上面的代碼調用了
connection_handle_read_state函數(shù),進入這個函數(shù)內部,分為幾個部分進行分析:
    if (con->is_readable) {
        con
->read_idle_ts = srv->cur_ts;

        
// -1:出錯 -2:對方關閉連接 0:成?
        switch(connection_handle_read(srv, con)) {
        
case -1:
            
return -1;
        
case -2:
            is_closed 
= 1;
            
break;
        
default:
            
break;
        }
    }
這里調用函數(shù)connection_handle_read, 來看看這個函數(shù)的實現(xiàn):
// -1:出錯 -2:對方關閉連接 0:成功
static int connection_handle_read(server *srv, connection *con) {
    
int len;
    buffer 
*b;
    
int toread;

    
if (con->conf.is_ssl) {
        
return connection_handle_read_ssl(srv, con);
    }

#if defined(__WIN32)
    b 
= chunkqueue_get_append_buffer(con->read_queue);
    buffer_prepare_copy(b, 
4 * 1024);
    len 
= recv(con->fd, b->ptr, b->size - 10);
#else
    
// 獲取有多少數(shù)據(jù)可讀
    if (ioctl(con->fd, FIONREAD, &toread)) {
        log_error_write(srv, __FILE__, __LINE__, 
"sd",
                
"unexpected end-of-file:",
                con
->fd);
        
return -1;
    }
    
// 根據(jù)數(shù)據(jù)量準備緩沖區(qū)
    b = chunkqueue_get_append_buffer(con->read_queue);
    buffer_prepare_copy(b, toread 
+ 1);
    
// 讀數(shù)據(jù)
    len = read(con->fd, b->ptr, b->size - 1);
#endif

    
if (len < 0) {
        con
->is_readable = 0;

        
// Non-blocking  I/O has been selected using O_NONBLOCK and no data
        
// was immediately available for reading.
        if (errno == EAGAIN) 
            
return 0;
        
if (errno == EINTR) {
            
/* we have been interrupted before we could read */
            con
->is_readable = 1;
            
return 0;
        }

        
if (errno != ECONNRESET) {
            
/* expected for keep-alive */
            log_error_write(srv, __FILE__, __LINE__, 
"ssd""connection closed - read failed: ", strerror(errno), errno);
        }

        connection_set_state(srv, con, CON_STATE_ERROR);

        
return -1;
    } 
else if (len == 0) {
        
// 當讀入數(shù)據(jù) = 0時 表示對端關閉了連接
        con->is_readable = 0;
        
/* the other end close the connection -> KEEP-ALIVE */

        
/* pipelining */

        
return -2;
    } 
else if ((size_t)len < b->size - 1) {
        
/* we got less then expected, wait for the next fd-event */

        con
->is_readable = 0;
    }

    
// 記錄讀入的數(shù)據(jù)量 未使用的數(shù)據(jù)第一個字節(jié)為0
    b->used = len;
    b
->ptr[b->used++= '\0';

    con
->bytes_read += len;
#if 0
    dump_packet(b
->ptr, len);
#endif

    
return 0;
}
簡單的說, 該函數(shù)首先調用ioctl獲取fd對應的緩沖區(qū)中有多少可讀的數(shù)據(jù), 然后調用chunkqueue_get_append_buffer和buffer_prepare_copy函數(shù)準備好所需的緩沖區(qū), 準備好緩沖區(qū)之后, 調用read函數(shù)讀取緩沖區(qū)中的數(shù)據(jù).對read函數(shù)的調用結果進行區(qū)分, 小于0表示出錯, 返回-1;等于0表示關閉了連接, 返回-2;假如讀取的數(shù)據(jù)長度比預期的小, 那么就等待下一次繼續(xù)讀數(shù)據(jù), 最后將已經(jīng)讀入的數(shù)據(jù)緩沖區(qū)最后一個字節(jié)置'\0',返回0.

繼續(xù)回到函數(shù)connection_handle_read_state中, 看接下來的代碼:
    // 這一段循環(huán)代碼用于更新read chunk隊列,沒有使用的chunk都歸入未使用chunk鏈中
    /* the last chunk might be empty */
    
for (c = cq->first; c;) {
        
if (cq->first == c && c->mem->used == 0) {
            
// 如果第一個chunk是空的并且沒有使用過
            /* the first node is empty */
            
/*  and it is empty, move it to unused */

            
// 則chunk隊列的第一個chunk為下一個chunk
            cq->first = c->next;
            
// 第一個chunk為NULL, 那么最后一個chunk為NULL
            if (cq->first == NULL) 
                cq
->last = NULL;

            
// 更新chunk隊列中對于未使用chunk的記錄
            c->next = cq->unused;
            cq
->unused = c;
            cq
->unused_chunks++;

            
// 重新指向第一個chunk
            c = cq->first;
        } 
else if (c->next && c->next->mem->used == 0) {
            chunk 
*fc;
            
// 如果下一個chunk存在而且未使用過
            /* next node is the last one */
            
/*  and it is empty, move it to unused */

            
// 將這個chunk從隊列中分離出去, 同時fc指向這個未使用的chunk
            fc = c->next;
            c
->next = fc->next;

            
// 將這個未使用的chunk(fc所指)保存到未使用chunk鏈中
            fc->next = cq->unused;
            cq
->unused = fc;
            cq
->unused_chunks++;

            
/* the last node was empty */
            
// 如果c的下一個chunk是空的, 那么chunk隊列的最后一個chunk就是c了
            if (c->next == NULL) {
                cq
->last = c;
            }

            
// 繼續(xù)往下走
            c = c->next;
        } 
else {
            
// 繼續(xù)往下走
            c = c->next;
        }
    }
每個connection結構體中, 有一個read_queue成員, 該成員是chunkqueue類型的, 一個connection讀入的數(shù)據(jù)都會保存在這個成員中, 由于一直沒有詳細介紹chunkqueue結構體及其使用, 這里不對上面的過程進行詳細的分析, 只需要知道chunkqueue結構體內部使用的是鏈表保存數(shù)據(jù), 上面這段代碼遍歷這個鏈表, 將未使用的部分抽取下來放到未使用chunkqueue中.

繼續(xù)看下面的代碼, 下面的代碼根據(jù)狀態(tài)是CON_STATE_READ還是CON_STATE_READ_POST進行了區(qū)分, 同樣的,目前僅關注CON_STATE_READ狀態(tài)部分:
    case CON_STATE_READ:    // 如果是可讀狀態(tài)
        /* if there is a \r\n\r\n in the chunkqueue
         *
         * scan the chunk-queue twice
         * 1. to find the \r\n\r\n
         * 2. to copy the header-packet
         *
         
*/

        last_chunk 
= NULL;
        last_offset 
= 0;

        
// 遍歷read chunk隊列
        for (c = cq->first; !last_chunk && c; c = c->next) {
            buffer b;
            size_t i;

            b.ptr 
= c->mem->ptr + c->offset;
            b.used 
= c->mem->used - c->offset;

            
// 遍歷當前chunk中的每一個字符
            for (i = 0!last_chunk && i < b.used; i++) {
                
char ch = b.ptr[i];
                size_t have_chars 
= 0;

                
// 判斷當前字符
                switch (ch) {
                
case '\r':        // 如果當前字符是'\r'
                    /* we have to do a 4 char lookup */
                    
// 該chunk還剩余多少個字符
                    have_chars = b.used - i - 1;
                    
                    
if (have_chars >= 4) {
                        
// 如果當前剩余字符大于等于4, 判斷緊跟著的4個字符是不是"\r\n\r\n", 如果是就退出循環(huán)
                        /* all chars are in this buffer */

                        
if (0 == strncmp(b.ptr + i, "\r\n\r\n"4)) {
                            
/* found */
                            last_chunk 
= c;
                            last_offset 
= i + 4;

                            
break;
                        }
                    } 
else {
                        
// 否則就去查看下一個chunk, 看看是不是和這個chunk一起形成了"\r\n\r\n"
                        chunk *lookahead_chunk = c->next;
                        size_t missing_chars;
                        
/* looks like the following chars are not in the same chunk */

                        missing_chars 
= 4 - have_chars;

                        
if (lookahead_chunk && lookahead_chunk->type == MEM_CHUNK) {
                            
/* is the chunk long enough to contain the other chars ? */

                            
if (lookahead_chunk->mem->used > missing_chars) {
                                
if (0 == strncmp(b.ptr + i, "\r\n\r\n", have_chars) &&
                                    
0 == strncmp(lookahead_chunk->mem->ptr, "\r\n\r\n" + have_chars, missing_chars)) {

                                    last_chunk 
= lookahead_chunk;
                                    last_offset 
= missing_chars;

                                    
break;
                                }
                            } 
else {
                                
/* a splited \r \n */
                                
break;
                            }
                        }
                    }

                    
break;
                }
            }
        }
這段代碼用于在讀入數(shù)據(jù)中查找"\r\n\r\n",熟悉http協(xié)議的人知道, 這代表著一個http請求的結束,也就是說, 上面的代碼用于判斷是否已經(jīng)接受了一個完整的http請求.但是有一個細節(jié)部分需要注意, 前面說過chunkqueue內部是使用一個鏈表來存放數(shù)據(jù),比方說這個鏈表中有兩個節(jié)點, 一個節(jié)點存放一字節(jié)的數(shù)據(jù), 一個節(jié)點存放了十字節(jié)的數(shù)據(jù),這時候可能會出現(xiàn)這樣的情況:假如在一個節(jié)點存放的數(shù)據(jù)中找到了字符'\r',而該節(jié)點剩下的數(shù)據(jù)不足以存放"\r\n\r\n"字符串剩余的字符, 也就是說, 不足4個字節(jié), 那么查找"\r\n\r\n"的過程就要延續(xù)到下一個節(jié)點繼續(xù)進行查找.比如在一個節(jié)點中最后部分找到了"\r", 那么就要在下一個節(jié)點的數(shù)據(jù)起始位置中查找"\n\r\n".

繼續(xù)看下面的代碼:
        /* found */
        
// 讀取到了請求的結尾, 現(xiàn)在將請求字符串放到request字段中
        if (last_chunk) {
            buffer_reset(con
->request.request);

            
for (c = cq->first; c; c = c->next) {
                buffer b;

                b.ptr 
= c->mem->ptr + c->offset;
                b.used 
= c->mem->used - c->offset;

                
if (c == last_chunk) {
                    b.used 
= last_offset + 1;
                }

                buffer_append_string_buffer(con
->request.request, &b);

                
if (c == last_chunk) {
                    c
->offset += last_offset;

                    
break;
                } 
else {
                    
/* the whole packet was copied */
                    c
->offset = c->mem->used - 1;
                }
            }

            
// 設置狀態(tài)為讀取請求結束
            connection_set_state(srv, con, CON_STATE_REQUEST_END);
        } 
else if (chunkqueue_length(cq) > 64 * 1024) {
            
// 讀入的數(shù)據(jù)太多, 出錯
            log_error_write(srv, __FILE__, __LINE__, "s""oversized request-header -> sending Status 414");

            con
->http_status = 414/* Request-URI too large */
            con
->keep_alive = 0;
            connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
        }
        
break;
如果前面查找到了"\r\n\r\n", 那么函數(shù)就進入這個部分.這部分代碼做的事情就是復制http請求頭到connection結構體中request成員中.需要注意的是如果沒有查找到"\r\n\r\n",并且緩沖區(qū)數(shù)據(jù)長度大于64*1024, 也就是64K字節(jié), 那么就返回414錯誤, 也就是說, 對于lighttpd而言, 一般的http請求不能超過64K字節(jié).

這個過程就分析到這里,簡單的總結一下:首先從緩沖區(qū)中讀取數(shù)據(jù), 然后查找"\r\n\r\n"字符串, 判斷是否已經(jīng)讀取了完整的http請求, 如果是的話就復制下來, 最后進入CON_STATE_REQUEST_END狀態(tài), 這是下一節(jié)分析的內容.



posted on 2008-09-24 10:50 那誰 閱讀(3479) 評論(1)  編輯 收藏 引用 所屬分類: 網(wǎng)絡編程服務器設計Linux/Unixlighttpd

評論

# re: lighttpd1.4.18代碼分析(八)--狀態(tài)機(2)CON_STATE_READ狀態(tài)  回復  更多評論   

mark
2008-09-27 16:05 | 908971
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩综合在线| 亚洲精选在线| 一本高清dvd不卡在线观看| 在线免费日韩片| 在线观看视频欧美| 亚洲第一在线| 亚洲人成亚洲人成在线观看| 亚洲日韩欧美一区二区在线| 亚洲欧洲日本国产| av不卡在线| 香蕉国产精品偷在线观看不卡| 午夜精品在线视频| 六月婷婷久久| 亚洲伦理久久| 销魂美女一区二区三区视频在线| 欧美精品日韩综合在线| 欧美日韩免费一区| 国产精品五区| 在线精品视频一区二区三四| 日韩五码在线| 欧美在线不卡视频| 亚洲成色www8888| 亚洲精品小视频| 欧美一区二区三区免费视频| 欧美二区不卡| 国产热re99久久6国产精品| 最新国产乱人伦偷精品免费网站| 亚洲字幕一区二区| 免费日韩视频| 亚洲伊人一本大道中文字幕| 蜜臀av性久久久久蜜臀aⅴ四虎| 欧美视频一区在线观看| 亚洲丁香婷深爱综合| 亚洲欧美国产精品va在线观看| 麻豆freexxxx性91精品| 亚洲一区欧美激情| 欧美久久久久久久| 在线电影国产精品| 久久精彩免费视频| 一区二区三区免费网站| 免费在线成人| 好看不卡的中文字幕| 亚洲尤物在线视频观看| 亚洲激情第一页| 久久久久成人网| 国产色视频一区| 亚洲自拍偷拍网址| 在线一区视频| 欧美日韩直播| 一本久道久久综合婷婷鲸鱼| 美女被久久久| 欧美在线视频一区| 国产欧美在线| 欧美一级精品大片| 亚洲视频大全| 国产精品www色诱视频| 99riav国产精品| 亚洲丰满少妇videoshd| 久久人91精品久久久久久不卡| 国产精品日本| 欧美在线观看视频一区二区| 亚洲专区在线| 国产丝袜一区二区| 久久成人免费网| 欧美一站二站| 一区二区三区在线看| 久久综合久色欧美综合狠狠 | 国产婷婷色综合av蜜臀av| 亚洲视频自拍偷拍| 亚洲视频视频在线| 国产欧美日韩一区二区三区在线| 亚洲视频在线看| 国产精品一区二区在线观看网站 | 精品9999| 欧美国产日韩一区| 欧美精品在线观看| 欧美一区二区| 久久国内精品视频| 亚洲国产精品尤物yw在线观看| 欧美韩国一区| 欧美日韩免费在线| 久久久精品一区| 欧美成人精品激情在线观看| 宅男精品视频| 欧美在线看片a免费观看| 1024欧美极品| 日韩网站在线| 国内一区二区三区在线视频| 欧美激情女人20p| 欧美午夜美女看片| 久久久在线视频| 欧美黄色日本| 久久爱91午夜羞羞| 欧美日韩国产美| 久久精品视频播放| 欧美精品在线观看91| 久久久久久久久久久久久9999| 欧美77777| 久久精品亚洲一区二区| 欧美激情在线免费观看| 久久精品国产久精国产一老狼| 欧美成人一品| 久久午夜精品一区二区| 国产精品久久久久77777| 亚洲成在人线av| 国产欧美视频一区二区三区| 亚洲精品美女在线观看播放| 伊大人香蕉综合8在线视| 亚洲免费在线精品一区| 亚洲一卡二卡三卡四卡五卡| 欧美成熟视频| 蜜臀久久99精品久久久久久9 | 麻豆精品视频在线| 欧美日韩一区二区三区高清| 欧美成年人网| 国产视频欧美视频| 一区二区三区av| 99精品国产在热久久婷婷| 久久久久久久波多野高潮日日| 亚洲欧美中日韩| 欧美日韩日本国产亚洲在线| 欧美成年人网站| 有坂深雪在线一区| 久久福利一区| 久久夜色精品国产欧美乱| 国产乱码精品| 亚洲免费综合| 欧美国产第一页| 国产亚洲一区二区三区在线播放| 欧美视频一区二区三区| 久久高清国产| 欧美va亚洲va日韩∨a综合色| 欧美一区亚洲二区| 欧美剧在线免费观看网站| 久久天天躁狠狠躁夜夜av| 国产精品一区二区久激情瑜伽| 亚洲精品视频在线观看免费| 最新国产拍偷乱拍精品| 久久精品亚洲| 久久偷看各类wc女厕嘘嘘偷窃| 国产亚洲欧美日韩一区二区| 亚洲欧美日韩爽爽影院| 性视频1819p久久| 国产精品夜夜夜| 午夜久久久久久| 久久久久久久久综合| 国模精品一区二区三区| 欧美在线影院| 欧美xart系列高清| 亚洲国产另类 国产精品国产免费| 久久视频在线看| 欧美高清在线一区| 日韩午夜电影av| 欧美性大战久久久久| 日韩亚洲欧美精品| 欧美在线啊v| 激情成人综合网| 猛干欧美女孩| 一本色道久久加勒比88综合| 欧美一级夜夜爽| 在线观看国产精品网站| 欧美大片一区二区| 一区二区欧美日韩视频| 欧美在线观看视频一区二区三区| 国产专区一区| 欧美精品在线视频| 午夜精品视频在线观看| 美日韩精品免费| 亚洲无线一线二线三线区别av| 国产免费亚洲高清| 久久综合一区二区三区| 99国产精品99久久久久久粉嫩| 久久av一区二区三区| 亚洲激情视频在线| 国产精品三级视频| 欧美成人视屏| 欧美在线国产精品| 日韩亚洲精品电影| 欧美a级片网| 欧美影院在线| 99在线精品观看| 国内精品久久久久影院色 | 在线综合亚洲| 国产视频在线观看一区 | 国产一区二区精品| 欧美gay视频| 欧美影院在线播放| 亚洲午夜一区| 久久久精品tv| 9l视频自拍蝌蚪9l视频成人| 国产女主播一区| 欧美精品久久久久久久| 性欧美激情精品| 99热这里只有精品8| 欧美成人精品一区二区三区| 午夜在线成人av| 亚洲精品久久久久久久久久久久| 国产偷国产偷亚洲高清97cao| 欧美日韩美女在线| 牛牛国产精品|