oracle 监听程序当前无法识别连接描述符中请求的服务_最新版Web服务器项目详解 04 http连接处理(上)...
點(diǎn) 擊 關(guān) 注 上?方"兩猿社"
設(shè) 為"置 頂 或 星 標(biāo)",干 貨 第 一?時 間 送 達(dá)。
互 聯(lián) 網(wǎng) 猿 | 兩 猿 社
本文內(nèi)容
在服務(wù)器項目中,http請求的處理與響應(yīng)至關(guān)重要,關(guān)系到用戶界面的跳轉(zhuǎn)與反饋。這里,社長將其分為上、中、下三個部分來講解,具體的:
上篇,梳理基礎(chǔ)知識,結(jié)合代碼分析http類及請求接收
中篇,結(jié)合代碼分析請求報文解析
下篇,結(jié)合代碼分析請求報文響應(yīng)
基礎(chǔ)知識方面,包括epoll、HTTP報文格式、狀態(tài)碼和有限狀態(tài)機(jī)。
代碼分析方面,首先對服務(wù)器端處理http請求的全部流程進(jìn)行簡要介紹,然后結(jié)合代碼對http類及請求接收進(jìn)行詳細(xì)分析。
epoll
epoll涉及的知識較多,這里僅對API和基礎(chǔ)知識作介紹。更多資料請查閱資料,或查閱游雙的Linux高性能服務(wù)器編程 第9章 I/O復(fù)用
epoll_create函數(shù)
1#include?2int?epoll_create(int?size)3
創(chuàng)建一個指示epoll內(nèi)核事件表的文件描述符,該描述符將用作其他epoll系統(tǒng)調(diào)用的第一個參數(shù),size不起作用。
epoll_ctl函數(shù)
1#include?2int?epoll_ctl(int?epfd,?int?op,?int?fd,?struct?epoll_event?*event)3
該函數(shù)用于操作內(nèi)核事件表監(jiān)控的文件描述符上的事件:注冊、修改、刪除
epfd:為epoll_creat的句柄
op:表示動作,用3個宏來表示:
EPOLL_CTL_ADD (注冊新的fd到epfd),
EPOLL_CTL_MOD (修改已經(jīng)注冊的fd的監(jiān)聽事件),
EPOLL_CTL_DEL (從epfd刪除一個fd);
event:告訴內(nèi)核需要監(jiān)聽的事件
上述event是epoll_event結(jié)構(gòu)體指針類型,表示內(nèi)核所監(jiān)聽的事件,具體定義如下:
1struct?epoll_event?{2__uint32_t?events;?/*?Epoll?events?*/
3epoll_data_t?data;?/*?User?data?variable?*/
4};
events描述事件類型,其中epoll事件類型有以下幾種
EPOLLIN:表示對應(yīng)的文件描述符可以讀(包括對端SOCKET正常關(guān)閉)
EPOLLOUT:表示對應(yīng)的文件描述符可以寫
EPOLLPRI:表示對應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀(這里應(yīng)該表示有帶外數(shù)據(jù)到來)
EPOLLERR:表示對應(yīng)的文件描述符發(fā)生錯誤
EPOLLHUP:表示對應(yīng)的文件描述符被掛斷;
EPOLLET:將EPOLL設(shè)為邊緣觸發(fā)(Edge Triggered)模式,這是相對于水平觸發(fā)(Level Triggered)而言的
EPOLLONESHOT:只監(jiān)聽一次事件,當(dāng)監(jiān)聽完這次事件之后,如果還需要繼續(xù)監(jiān)聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里
epoll_wait函數(shù)
1#include?2int?epoll_wait(int?epfd,?struct?epoll_event?*events,?int?maxevents,?int?timeout)3
該函數(shù)用于等待所監(jiān)控文件描述符上有事件的產(chǎn)生,返回就緒的文件描述符個數(shù)
events:用來存內(nèi)核得到事件的集合,
maxevents:告之內(nèi)核這個events有多大,這個maxevents的值不能大于創(chuàng)建epoll_create()時的size,
timeout:是超時時間
-1:阻塞
0:立即返回,非阻塞
>0:指定毫秒
返回值:成功返回有多少文件描述符就緒,時間到時返回0,出錯返回-1
select/poll/epoll
調(diào)用函數(shù)
select和poll都是一個函數(shù),epoll是一組函數(shù)
文件描述符數(shù)量
select通過線性表描述文件描述符集合,文件描述符有上限,一般是1024,但可以修改源碼,重新編譯內(nèi)核,不推薦
poll是鏈表描述,突破了文件描述符上限,最大可以打開文件的數(shù)目
epoll通過紅黑樹描述,最大可以打開文件的數(shù)目,可以通過命令ulimit -n number修改,僅對當(dāng)前終端有效
將文件描述符從用戶傳給內(nèi)核
select和poll通過將所有文件描述符拷貝到內(nèi)核態(tài),每次調(diào)用都需要拷貝
epoll通過epoll_create建立一棵紅黑樹,通過epoll_ctl將要監(jiān)聽的文件描述符注冊到紅黑樹上
內(nèi)核判斷就緒的文件描述符
select和poll通過遍歷文件描述符集合,判斷哪個文件描述符上有事件發(fā)生
epoll_create時,內(nèi)核除了幫我們在epoll文件系統(tǒng)里建了個紅黑樹用于存儲以后epoll_ctl傳來的fd外,還會再建立一個list鏈表,用于存儲準(zhǔn)備就緒的事件,當(dāng)epoll_wait調(diào)用時,僅僅觀察這個list鏈表里有沒有數(shù)據(jù)即可。
epoll是根據(jù)每個fd上面的回調(diào)函數(shù)(中斷函數(shù))判斷,只有發(fā)生了事件的socket才會主動的去調(diào)用 callback函數(shù),其他空閑狀態(tài)socket則不會,若是就緒事件,插入list
應(yīng)用程序索引就緒文件描述符
select/poll只返回發(fā)生了事件的文件描述符的個數(shù),若知道是哪個發(fā)生了事件,同樣需要遍歷
epoll返回的發(fā)生了事件的個數(shù)和結(jié)構(gòu)體數(shù)組,結(jié)構(gòu)體包含socket的信息,因此直接處理返回的數(shù)組即可
工作模式
select和poll都只能工作在相對低效的LT模式下
epoll則可以工作在ET高效模式,并且epoll還支持EPOLLONESHOT事件,該事件能進(jìn)一步減少可讀、可寫和異常事件被觸發(fā)的次數(shù)。?
應(yīng)用場景
當(dāng)所有的fd都是活躍連接,使用epoll,需要建立文件系統(tǒng),紅黑書和鏈表對于此來說,效率反而不高,不如selece和poll
當(dāng)監(jiān)測的fd數(shù)目較小,且各個fd都比較活躍,建議使用select或者poll
當(dāng)監(jiān)測的fd數(shù)目非常大,成千上萬,且單位時間只有其中的一部分fd處于就緒狀態(tài),這個時候使用epoll能夠明顯提升性能
ET、LT、EPOLLONESHOT
LT水平觸發(fā)模式
epoll_wait檢測到文件描述符有事件發(fā)生,則將其通知給應(yīng)用程序,應(yīng)用程序可以不立即處理該事件。
當(dāng)下一次調(diào)用epoll_wait時,epoll_wait還會再次向應(yīng)用程序報告此事件,直至被處理
ET邊緣觸發(fā)模式
epoll_wait檢測到文件描述符有事件發(fā)生,則將其通知給應(yīng)用程序,應(yīng)用程序必須立即處理該事件
必須要一次性將數(shù)據(jù)讀取完,使用非阻塞I/O,讀取到出現(xiàn)eagain
EPOLLONESHOT
一個線程讀取某個socket上的數(shù)據(jù)后開始處理數(shù)據(jù),在處理過程中該socket上又有新數(shù)據(jù)可讀,此時另一個線程被喚醒讀取,此時出現(xiàn)兩個線程處理同一個socket
我們期望的是一個socket連接在任一時刻都只被一個線程處理,通過epoll_ctl對該文件描述符注冊epolloneshot事件,一個線程處理socket時,其他線程將無法處理,當(dāng)該線程處理完后,需要通過epoll_ctl重置epolloneshot事件
HTTP報文格式
HTTP報文分為請求報文和響應(yīng)報文兩種,每種報文必須按照特有格式生成,才能被瀏覽器端識別。
其中,瀏覽器端向服務(wù)器發(fā)送的為請求報文,服務(wù)器處理后返回給瀏覽器端的為響應(yīng)報文。
請求報文
HTTP請求報文由請求行(request line)、請求頭部(header)、空行和請求數(shù)據(jù)四個部分組成。
其中,請求分為兩種,GET和POST,具體的:
GET
2????Host:img.mukewang.com
3????User-Agent:Mozilla/5.0?(Windows?NT?10.0;?WOW64)
4????AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/51.0.2704.106?Safari/537.36
5????Accept:image/webp,image/*,*/*;q=0.8
6????Referer:http://www.imooc.com/
7????Accept-Encoding:gzip,?deflate,?sdch
8????Accept-Language:zh-CN,zh;q=0.8
9????空行
10????請求數(shù)據(jù)為空
POST
2????Host:www.wrox.com
3????User-Agent:Mozilla/4.0?(compatible;?MSIE?6.0;?Windows?NT?5.1;?SV1;?.NET?CLR?2.0.50727;?.NET?CLR?3.0.04506.648;?.NET?CLR?3.5.21022)
4????Content-Type:application/x-www-form-urlencoded
5????Content-Length:40
6????Connection:?Keep-Alive
7????空行
8????name=Professional%20Ajax&publisher=Wiley
請求行,用來說明請求類型,要訪問的資源以及所使用的HTTP版本。
GET說明請求類型為GET,/562f25980001b1b106000338.jpg(URL)為要訪問的資源,該行的最后一部分說明使用的是HTTP1.1版本。請求頭部,緊接著請求行(即第一行)之后的部分,用來說明服務(wù)器要使用的附加信息。
HOST,給出請求資源所在服務(wù)器的域名。
User-Agent,HTTP客戶端程序的信息,該信息由你發(fā)出請求使用的瀏覽器來定義,并且在每個請求中自動發(fā)送等。
Accept,說明用戶代理可處理的媒體類型。
Accept-Encoding,說明用戶代理支持的內(nèi)容編碼。
Accept-Language,說明用戶代理能夠處理的自然語言集。
Content-Type,說明實(shí)現(xiàn)主體的媒體類型。
Content-Length,說明實(shí)現(xiàn)主體的大小。
Connection,連接管理,可以是Keep-Alive或close。
空行,請求頭部后面的空行是必須的即使第四部分的請求數(shù)據(jù)為空,也必須有空行。
請求數(shù)據(jù)也叫主體,可以添加任意的其他數(shù)據(jù)。
響應(yīng)報文
HTTP響應(yīng)也由四個部分組成,分別是:狀態(tài)行、消息報頭、空行和響應(yīng)正文。
1HTTP/1.1?200?OK2Date:?Fri,?22?May?2009?06:07:21?GMT
3Content-Type:?text/html;?charset=UTF-8
4空行
5
6??????
7??????
8????????????
9??????
10
狀態(tài)行,由HTTP協(xié)議版本號, 狀態(tài)碼, 狀態(tài)消息 三部分組成。
第一行為狀態(tài)行,(HTTP/1.1)表明HTTP版本為1.1版本,狀態(tài)碼為200,狀態(tài)消息為OK。消息報頭,用來說明客戶端要使用的一些附加信息。
第二行和第三行為消息報頭,Date:生成響應(yīng)的日期和時間;Content-Type:指定了MIME類型的HTML(text/html),編碼類型是UTF-8。空行,消息報頭后面的空行是必須的。
響應(yīng)正文,服務(wù)器返回給客戶端的文本信息。空行后面的html部分為響應(yīng)正文。
HTTP狀態(tài)碼
HTTP有5種類型的狀態(tài)碼,具體的:
1xx:指示信息--表示請求已接收,繼續(xù)處理。
2xx:成功--表示請求正常處理完畢。
200 OK:客戶端請求被正常處理。
206 Partial content:客戶端進(jìn)行了范圍請求。
3xx:重定向--要完成請求必須進(jìn)行更進(jìn)一步的操作。
301 Moved Permanently:永久重定向,該資源已被永久移動到新位置,將來任何對該資源的訪問都要使用本響應(yīng)返回的若干個URI之一。
302 Found:臨時重定向,請求的資源現(xiàn)在臨時從不同的URI中獲得。
4xx:客戶端錯誤--請求有語法錯誤,服務(wù)器無法處理請求。
400 Bad Request:請求報文存在語法錯誤。
403 Forbidden:請求被服務(wù)器拒絕。
404 Not Found:請求不存在,服務(wù)器上找不到請求的資源。
5xx:服務(wù)器端錯誤--服務(wù)器處理請求出錯。
500 Internal Server Error:服務(wù)器在執(zhí)行請求時出現(xiàn)錯誤。
有限狀態(tài)機(jī)
有限狀態(tài)機(jī),是一種抽象的理論模型,它能夠把有限個變量描述的狀態(tài)變化過程,以可構(gòu)造可驗證的方式呈現(xiàn)出來。比如,封閉的有向圖。
有限狀態(tài)機(jī)可以通過if-else,switch-case和函數(shù)指針來實(shí)現(xiàn),從軟件工程的角度看,主要是為了封裝邏輯。
帶有狀態(tài)轉(zhuǎn)移的有限狀態(tài)機(jī)示例代碼。
1STATE_MACHINE(){2????State?cur_State?=?type_A;
3????while(cur_State?!=?type_C){
4????????Package?_pack?=?getNewPackage();
5????????switch(){
6????????????case?type_A:
7????????????????process_pkg_state_A(_pack);
8????????????????cur_State?=?type_B;
9????????????????break;
10????????????case?type_B:
11????????????????process_pkg_state_B(_pack);
12????????????????cur_State?=?type_C;
13????????????????break;
14????????}
15????}
16}
該狀態(tài)機(jī)包含三種狀態(tài):type_A,type_B和type_C。其中,type_A是初始狀態(tài),type_C是結(jié)束狀態(tài)。
狀態(tài)機(jī)的當(dāng)前狀態(tài)記錄在cur_State變量中,邏輯處理時,狀態(tài)機(jī)先通過getNewPackage獲取數(shù)據(jù)包,然后根據(jù)當(dāng)前狀態(tài)對數(shù)據(jù)進(jìn)行處理,處理完后,狀態(tài)機(jī)通過改變cur_State完成狀態(tài)轉(zhuǎn)移。
有限狀態(tài)機(jī)一種邏輯單元內(nèi)部的一種高效編程方法,在服務(wù)器編程中,服務(wù)器可以根據(jù)不同狀態(tài)或者消息類型進(jìn)行相應(yīng)的處理邏輯,使得程序邏輯清晰易懂。
http處理流程
首先對http報文處理的流程進(jìn)行簡要介紹,然后具體介紹http類的定義和服務(wù)器接收http請求的具體過程。
http報文處理流程
瀏覽器端發(fā)出http連接請求,主線程創(chuàng)建http對象接收請求并將所有數(shù)據(jù)讀入對應(yīng)buffer,將該對象插入任務(wù)隊列,工作線程從任務(wù)隊列中取出一個任務(wù)進(jìn)行處理。(本篇講)
工作線程取出任務(wù)后,調(diào)用process_read函數(shù),通過主、從狀態(tài)機(jī)對請求報文進(jìn)行解析。(中篇講)
解析完之后,跳轉(zhuǎn)do_request函數(shù)生成響應(yīng)報文,通過process_write寫入buffer,返回給瀏覽器端。(下篇講)
http類
這一部分代碼在TinyWebServer/http/http_conn.h中,主要是http類的定義。
1class?http_conn{2????public:
3????????//設(shè)置讀取文件的名稱m_real_file大小
4????????static?const?int?FILENAME_LEN=200;
5????????//設(shè)置讀緩沖區(qū)m_read_buf大小
6????????static?const?int?READ_BUFFER_SIZE=2048;
7????????//設(shè)置寫緩沖區(qū)m_write_buf大小
8????????static?const?int?WRITE_BUFFER_SIZE=1024;
9????????//報文的請求方法,本項目只用到GET和POST
10????????enum?METHOD{GET=0,POST,HEAD,PUT,DELETE,TRACE,OPTIONS,CONNECT,PATH};
11????????//主狀態(tài)機(jī)的狀態(tài)
12????????enum?CHECK_STATE{CHECK_STATE_REQUESTLINE=0,CHECK_STATE_HEADER,CHECK_STATE_CONTENT};
13????????//報文解析的結(jié)果
14????????enum?HTTP_CODE{NO_REQUEST,GET_REQUEST,BAD_REQUEST,NO_RESOURCE,FORBIDDEN_REQUEST,FILE_REQUEST,INTERNAL_ERROR,CLOSED_CONNECTION};
15????????//從狀態(tài)機(jī)的狀態(tài)
16????????enum?LINE_STATUS{LINE_OK=0,LINE_BAD,LINE_OPEN};
17
18????public:
19????????http_conn(){}
20????????~http_conn(){}
21
22????public:
23????????//初始化套接字地址,函數(shù)內(nèi)部會調(diào)用私有方法init
24????????void?init(int?sockfd,const?sockaddr_in?&addr);
25????????//關(guān)閉http連接
26????????void?close_conn(bool?real_close=true);
27????????void?process();
28????????//讀取瀏覽器端發(fā)來的全部數(shù)據(jù)
29????????bool?read_once();
30????????//響應(yīng)報文寫入函數(shù)
31????????bool?write();
32????????sockaddr_in?*get_address(){
33????????????return?&m_address;??
34????????}
35????????//同步線程初始化數(shù)據(jù)庫讀取表
36????????void?initmysql_result();
37????????//CGI使用線程池初始化數(shù)據(jù)庫表
38????????void?initresultFile(connection_pool?*connPool);
39
40????private:
41????????void?init();
42????????//從m_read_buf讀取,并處理請求報文
43????????HTTP_CODE?process_read();
44????????//向m_write_buf寫入響應(yīng)報文數(shù)據(jù)
45????????bool?process_write(HTTP_CODE?ret);
46????????//主狀態(tài)機(jī)解析報文中的請求行數(shù)據(jù)
47????????HTTP_CODE?parse_request_line(char?*text);
48????????//主狀態(tài)機(jī)解析報文中的請求頭數(shù)據(jù)
49????????HTTP_CODE?parse_headers(char?*text);
50????????//主狀態(tài)機(jī)解析報文中的請求內(nèi)容
51????????HTTP_CODE?parse_content(char?*text);
52????????//生成響應(yīng)報文
53????????HTTP_CODE?do_request();
54
55????????//m_start_line是已經(jīng)解析的字符
56????????//get_line用于將指針向后偏移,指向未處理的字符
57????????char*?get_line(){return?m_read_buf+m_start_line;};
58
59????????//從狀態(tài)機(jī)讀取一行,分析是請求報文的哪一部分
60????????LINE_STATUS?parse_line();
61
62????????void?unmap();
63
64????????//根據(jù)響應(yīng)報文格式,生成對應(yīng)8個部分,以下函數(shù)均由do_request調(diào)用
65????????bool?add_response(const?char*?format,...);
66????????bool?add_content(const?char*?content);
67????????bool?add_status_line(int?status,const?char*?title);
68????????bool?add_headers(int?content_length);
69????????bool?add_content_type();
70????????bool?add_content_length(int?content_length);
71????????bool?add_linger();
72????????bool?add_blank_line();
73
74????public:
75????????static?int?m_epollfd;
76????????static?int?m_user_count;
77????????MYSQL?*mysql;
78
79????private:
80????????int?m_sockfd;
81????????sockaddr_in?m_address;
82
83????????//存儲讀取的請求報文數(shù)據(jù)
84????????char?m_read_buf[READ_BUFFER_SIZE];
85????????//緩沖區(qū)中m_read_buf中數(shù)據(jù)的最后一個字節(jié)的下一個位置
86????????int?m_read_idx;
87????????//m_read_buf讀取的位置m_checked_idx
88????????int?m_checked_idx;
89????????//m_read_buf中已經(jīng)解析的字符個數(shù)
90????????int?m_start_line;
91
92????????//存儲發(fā)出的響應(yīng)報文數(shù)據(jù)
93????????char?m_write_buf[WRITE_BUFFER_SIZE];
94????????//指示buffer中的長度
95????????int?m_write_idx;
96
97????????//主狀態(tài)機(jī)的狀態(tài)
98????????CHECK_STATE?m_check_state;
99????????//請求方法
100????????METHOD?m_method;
101
102????????//以下為解析請求報文中對應(yīng)的6個變量
103????????//存儲讀取文件的名稱
104????????char?m_real_file[FILENAME_LEN];
105????????char?*m_url;
106????????char?*m_version;
107????????char?*m_host;
108????????int?m_content_length;
109????????bool?m_linger;
110
111????????char?*m_file_address;????????//讀取服務(wù)器上的文件地址
112????????struct?stat?m_file_stat;
113????????struct?iovec?m_iv[2];????????//io向量機(jī)制iovec
114????????int?m_iv_count;
115????????int?cgi;????????????????????//是否啟用的POST
116????????char?*m_string;????????????????//存儲請求頭數(shù)據(jù)
117????????int?bytes_to_send;??????????//剩余發(fā)送字節(jié)數(shù)
118????????int?bytes_have_send;????????//已發(fā)送字節(jié)數(shù)
119};
在http請求接收部分,會涉及到init和read_once函數(shù),但init僅僅是對私有成員變量進(jìn)行初始化,不用過多講解。
這里,對read_once進(jìn)行介紹。read_once讀取瀏覽器端發(fā)送來的請求報文,直到無數(shù)據(jù)可讀或?qū)Ψ疥P(guān)閉連接,讀取到m_read_buffer中,并更新m_read_idx。
1//循環(huán)讀取客戶數(shù)據(jù),直到無數(shù)據(jù)可讀或?qū)Ψ疥P(guān)閉連接2bool?http_conn::read_once()
3{
4????if(m_read_idx>=READ_BUFFER_SIZE)
5????{
6????????return?false;
7????}
8????int?bytes_read=0;
9????while(true)
10????{
11????????//從套接字接收數(shù)據(jù),存儲在m_read_buf緩沖區(qū)
12????????bytes_read=recv(m_sockfd,m_read_buf+m_read_idx,READ_BUFFER_SIZE-m_read_idx,0);
13????????if(bytes_read==-1)
14????????{????
15????????????//非阻塞ET模式下,需要一次性將數(shù)據(jù)讀完
16????????????if(errno==EAGAIN||errno==EWOULDBLOCK)
17????????????????break;
18????????????return?false;
19????????}
20????????else?if(bytes_read==0)
21????????{
22????????????return?false;
23????????}
24????????//修改m_read_idx的讀取字節(jié)數(shù)
25????????m_read_idx+=bytes_read;
26????}
27????return?true;
28}
epoll相關(guān)代碼
項目中epoll相關(guān)代碼部分包括非阻塞模式、內(nèi)核事件表注冊事件、刪除事件、重置EPOLLONESHOT事件四種。
非阻塞模式
2int?setnonblocking(int?fd)3{
4????int?old_option?=?fcntl(fd,?F_GETFL);
5????int?new_option?=?old_option?|?O_NONBLOCK;
6????fcntl(fd,?F_SETFL,?new_option);
7????return?old_option;
8}
內(nèi)核事件表注冊新事件,開啟EPOLLONESHOT,針對客戶端連接的描述符,listenfd不用開啟
3????epoll_event?event;
4????event.data.fd?=?fd;
5
6#ifdef?ET
7????event.events?=?EPOLLIN?|?EPOLLET?|?EPOLLRDHUP;
8#endif
9
10#ifdef?LT
11????event.events?=?EPOLLIN?|?EPOLLRDHUP;
12#endif
13
14????if?(one_shot)
15????????event.events?|=?EPOLLONESHOT;
16????epoll_ctl(epollfd,?EPOLL_CTL_ADD,?fd,?&event);
17????setnonblocking(fd);
18}
內(nèi)核事件表刪除事件
3????epoll_ctl(epollfd,?EPOLL_CTL_DEL,?fd,?0);
4????close(fd);
5}
重置EPOLLONESHOT事件
3????epoll_event?event;
4????event.data.fd?=?fd;
5
6#ifdef?ET
7????event.events?=?ev?|?EPOLLET?|?EPOLLONESHOT?|?EPOLLRDHUP;
8#endif
9
10#ifdef?LT
11????event.events?=?ev?|?EPOLLONESHOT?|?EPOLLRDHUP;
12#endif
13
14????epoll_ctl(epollfd,?EPOLL_CTL_MOD,?fd,?&event);
15}
服務(wù)器接收http請求
瀏覽器端發(fā)出http連接請求,主線程創(chuàng)建http對象接收請求并將所有數(shù)據(jù)讀入對應(yīng)buffer,將該對象插入任務(wù)隊列,工作線程從任務(wù)隊列中取出一個任務(wù)進(jìn)行處理。
1//創(chuàng)建MAX_FD個http類對象2http_conn*?users=new?http_conn[MAX_FD];
3
4//創(chuàng)建內(nèi)核事件表
5epoll_event?events[MAX_EVENT_NUMBER];
6epollfd?=?epoll_create(5);
7assert(epollfd?!=?-1);
8
9//將listenfd放在epoll樹上
10addfd(epollfd,?listenfd,?false);
11
12//將上述epollfd賦值給http類對象的m_epollfd屬性
13http_conn::m_epollfd?=?epollfd;
14
15while?(!stop_server)
16{
17????//等待所監(jiān)控文件描述符上有事件的產(chǎn)生
18????int?number?=?epoll_wait(epollfd,?events,?MAX_EVENT_NUMBER,?-1);
19????if?(number?0?&&?errno?!=?EINTR)
20????{
21????????break;
22????}
23????//對所有就緒事件進(jìn)行處理
24????for?(int?i?=?0;?i?25????{
26????????int?sockfd?=?events[i].data.fd;
27
28????????//處理新到的客戶連接
29????????if?(sockfd?==?listenfd)
30????????{
31????????????struct?sockaddr_in?client_address;
32????????????socklen_t?client_addrlength?=?sizeof(client_address);
33//LT水平觸發(fā)
34#ifdef?LT
35????????????int?connfd?=?accept(listenfd,?(struct?sockaddr?*)&client_address,?&client_addrlength);
36????????????if?(connfd?0)
37????????????{
38????????????????continue;
39????????????}
40????????????if?(http_conn::m_user_count?>=?MAX_FD)
41????????????{
42????????????????show_error(connfd,?"Internal?server?busy");
43????????????????continue;
44????????????}
45????????????users[connfd].init(connfd,?client_address);
46#endif
47
48//ET非阻塞邊緣觸發(fā)
49#ifdef?ET
50????????????//需要循環(huán)接收數(shù)據(jù)
51????????????while?(1)
52????????????{
53????????????????int?connfd?=?accept(listenfd,?(struct?sockaddr?*)&client_address,?&client_addrlength);
54????????????????if?(connfd?0)
55????????????????{
56????????????????????break;
57????????????????}
58????????????????if?(http_conn::m_user_count?>=?MAX_FD)
59????????????????{
60????????????????????show_error(connfd,?"Internal?server?busy");
61????????????????????break;
62????????????????}
63????????????????users[connfd].init(connfd,?client_address);
64????????????}
65????????????continue;
66#endif
67????????}
68
69????????//處理異常事件
70????????else?if?(events[i].events?&?(EPOLLRDHUP?|?EPOLLHUP?|?EPOLLERR))
71????????{
72????????????//服務(wù)器端關(guān)閉連接
73????????}
74
75????????//處理信號
76????????else?if?((sockfd?==?pipefd[0])?&&?(events[i].events?&?EPOLLIN))
77????????{
78????????}
79
80????????//處理客戶連接上接收到的數(shù)據(jù)
81????????else?if?(events[i].events?&?EPOLLIN)
82????????{
83????????????//讀入對應(yīng)緩沖區(qū)
84????????????if?(users[sockfd].read_once())
85????????????{
86????????????????//若監(jiān)測到讀事件,將該事件放入請求隊列
87????????????????pool->append(users?+?sockfd);
88????????????}
89????????????else
90????????????{
91???????????????//服務(wù)器關(guān)閉連接
92????????????}
93????????}
94
95????}
96}
如果本文對你有幫助,閱讀原文star一下服務(wù)器項目,我們需要你的星星^_^.
完。
總結(jié)
以上是生活随笔為你收集整理的oracle 监听程序当前无法识别连接描述符中请求的服务_最新版Web服务器项目详解 04 http连接处理(上)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么叫基于web的网站_什么叫响应式网站
- 下一篇: 两个服务器之间怎么传输大量数据速度快 j