實(shí)驗(yàn)中心網(wǎng)站建設(shè)媒體軟文發(fā)稿
一、TCP 狀態(tài)轉(zhuǎn)換

二、TCP協(xié)議的通信過(guò)程?
三、TCP 通信流程?
// TCP 通信的流程
// 服務(wù)器端 (被動(dòng)接受連接的角色)
1. 創(chuàng)建一個(gè)用于監(jiān)聽(tīng)的套接字- 監(jiān)聽(tīng):監(jiān)聽(tīng)有客戶(hù)端的連接- 套接字:這個(gè)套接字其實(shí)就是一個(gè)文件描述符
2. 將這個(gè)監(jiān)聽(tīng)文件描述符和本地的IP和端口綁定(IP和端口就是服務(wù)器的地址信息)- 客戶(hù)端連接服務(wù)器的時(shí)候使用的就是這個(gè)IP和端口
3. 設(shè)置監(jiān)聽(tīng),監(jiān)聽(tīng)的fd開(kāi)始工作
4. 阻塞等待,當(dāng)有客戶(hù)端發(fā)起連接,解除阻塞,接受客戶(hù)端的連接,會(huì)得到一個(gè)和客戶(hù)端通信的套接字
(fd)
5. 通信- 接收數(shù)據(jù)- 發(fā)送數(shù)據(jù)
6. 通信結(jié)束,斷開(kāi)連接
// 客戶(hù)端
1. 創(chuàng)建一個(gè)用于通信的套接字(fd)
2. 連接服務(wù)器,需要指定連接的服務(wù)器的 IP 和 端口
3. 連接成功了,客戶(hù)端可以直接和服務(wù)器通信- 接收數(shù)據(jù)- 發(fā)送數(shù)據(jù)
4. 通信結(jié)束,斷開(kāi)連接
套接字通信的客戶(hù)端和服務(wù)端的實(shí)現(xiàn)_呵呵噠( ̄▽ ̄)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132046488?spm=1001.2014.3001.5501
四 、解析HTTP請(qǐng)求報(bào)文
4.1 讀取數(shù)據(jù)到緩沖區(qū) bool http_conn::read()
// 循環(huán)讀取客戶(hù)數(shù)據(jù),直到無(wú)數(shù)據(jù)可讀或者對(duì)方關(guān)閉連接
bool http_conn::read() {// printf("一次性讀完數(shù)據(jù)\n");if( m_read_idx >= READ_BUFFER_SIZE ) {return false;}int bytes_read = 0;//讀取到的字節(jié)while(true) {//開(kāi)始保存數(shù)據(jù)// 數(shù)組起始位置(0) + 已經(jīng)讀的數(shù)據(jù)位置(100) = 下次開(kāi)始寫(xiě)的位置(101)// 從m_read_buf + m_read_idx索引出開(kāi)始保存數(shù)據(jù),大小是READ_BUFFER_SIZE - m_read_idxbytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0 );if (bytes_read == -1) {if( errno == EAGAIN || errno == EWOULDBLOCK ) {// 沒(méi)有數(shù)據(jù)break;}return false; } else if (bytes_read == 0) { // 對(duì)方關(guān)閉連接return false;}m_read_idx += bytes_read;//讀到數(shù)據(jù),更新索引}// printf("讀到了數(shù)據(jù): %s\n",m_read_buf);return true;
}
4.2?有限狀態(tài)機(jī)
邏輯單元內(nèi)部的一種高效編程方法:有限狀態(tài)機(jī)(finite state machine)。有的應(yīng)用層協(xié)議頭部包含數(shù)據(jù)包類(lèi)型字段,每種類(lèi)型可以映射為邏輯單元的一種執(zhí)行狀態(tài),服務(wù)器可以根據(jù)它來(lái)編寫(xiě)相應(yīng)的處理邏輯。如下是一種狀態(tài)獨(dú)立的有限狀態(tài)機(jī):
STATE_MACHINE( Package _pack )
{PackageType _type = _pack.GetType();switch( _type ){case type_A:process_package_A( _pack );break;case type_B:process_package_B( _pack );break;}
}
這是一個(gè)簡(jiǎn)單的有限狀態(tài)機(jī),只不過(guò)該狀態(tài)機(jī)的每個(gè)狀態(tài)都是相互獨(dú)立的,即狀態(tài)之間沒(méi)有相互轉(zhuǎn)移。狀態(tài)之間的轉(zhuǎn)移是需要狀態(tài)機(jī)內(nèi)部驅(qū)動(dòng),如下代碼:
STATE_MACHINE()
{State cur_State = type_A;while( cur_State != type_C ){Package _pack = getNewPackage();switch( cur_State ){case type_A:process_package_state_A( _pack );cur_State = type_B;break;case type_B:process_package_state_B( _pack );cur_State = type_C;break;}}
}
該狀態(tài)機(jī)包含三種狀態(tài):type_A、type_B 和 type_C,其中 type_A 是狀態(tài)機(jī)的開(kāi)始狀態(tài),type_C 是狀態(tài)機(jī)的結(jié)束狀態(tài)。狀態(tài)機(jī)的當(dāng)前狀態(tài)記錄在 cur_State 變量中。在一趟循環(huán)過(guò)程中,狀態(tài)機(jī)先通過(guò) getNewPackage 方法獲得一個(gè)新的數(shù)據(jù)包,然后根據(jù) cur_State 變量的值判斷如何處理該數(shù)據(jù)包。數(shù)據(jù)包處理完之后,狀態(tài)機(jī)通過(guò)給 cur_State 變量傳遞目標(biāo)狀態(tài)值來(lái)實(shí)現(xiàn)狀態(tài)轉(zhuǎn)移。那么當(dāng)狀態(tài)機(jī)進(jìn)入下一趟循環(huán)時(shí),它將執(zhí)行新的狀態(tài)對(duì)應(yīng)的邏輯。
4.3 HTTP 請(qǐng)求格式
>>http get請(qǐng)求報(bào)文的格式
請(qǐng)求行\(zhòng)r\n
請(qǐng)求頭\r\n
空行(\r\n)提示: 每項(xiàng)信息之間都需要一個(gè)\r\n,是由http協(xié)議規(guī)定************************************************
************************************************
>>http post請(qǐng)求報(bào)文的格式
請(qǐng)求行\(zhòng)r\n
請(qǐng)求頭\r\n
空行(\r\n)
請(qǐng)求體提示: 請(qǐng)求體就是瀏覽器發(fā)送給服務(wù)器的數(shù)據(jù)
在HTTP報(bào)文中,每一行的數(shù)據(jù)有\r\n作為結(jié)束字符,空行則是僅僅是字符\r\n。因此,可以通過(guò)查找\r\n將報(bào)文拆解成單獨(dú)的行進(jìn)行解析,項(xiàng)目中便是利用了這一點(diǎn)。?
推薦文章:?
http請(qǐng)求報(bào)文與響應(yīng)報(bào)文_構(gòu)造http請(qǐng)求報(bào)文,發(fā)送到天氣預(yù)報(bào)的服務(wù)器,并獲取http響應(yīng)報(bào)文,將報(bào)文進(jìn)行解析獲_JSon liu的博客-CSDN博客https://blog.csdn.net/weixin_45912307/article/details/109454522
Http請(qǐng)求報(bào)文格式和響應(yīng)報(bào)文格式-騰訊云開(kāi)發(fā)者社區(qū)-騰訊云 (tencent.com)https://cloud.tencent.com/developer/article/1953222我的往期文章:Web服務(wù)器簡(jiǎn)介及HTTP協(xié)議_呵呵噠( ̄▽ ̄)"的博客-CSDN博客
https://blog.csdn.net/weixin_41987016/article/details/132610837?spm=1001.2014.3001.5501
4.4?狀態(tài)轉(zhuǎn)換圖

4.5 主狀態(tài)機(jī)狀態(tài)和從狀態(tài)機(jī)狀態(tài)
(1)?從狀態(tài)機(jī)狀態(tài)
>>從狀態(tài)機(jī)邏輯
負(fù)責(zé)讀取buffer中的數(shù)據(jù),將每行數(shù)據(jù)末尾的\r\n置為\0\0,并更新從狀態(tài)機(jī)在buffer中讀取的位置m_checked_idx,從此來(lái)驅(qū)動(dòng)主狀態(tài)機(jī)解析
① 從狀態(tài)機(jī)從m_read_buf中逐字節(jié)讀取,判斷當(dāng)前字節(jié)是否為\r
- 接下來(lái)的字符是\n,將\r\n修改成\0\0,將m_checked_idx指向下一行的開(kāi)頭,則返回LINE_OK
- 然后達(dá)到了buffer末尾,表示buffer還需要繼續(xù)接收,返回LINE_OPEN
- 否則,表示語(yǔ)法錯(cuò)誤,返回LINE_BAD
② 當(dāng)前字節(jié)不是\r,判斷是否是\n
- 【注意】什么時(shí)候會(huì)是②這種情況?
一般是上次讀取到\r就到了buffer末尾,沒(méi)有接收完整,再次接收時(shí)會(huì)出現(xiàn)這種情況。
- 【判斷】如果前一個(gè)字符是\r,則將\r\n修改成\0\0,將m_checked_idx指向下一行的開(kāi)頭,則返回LINE_OK
③ 當(dāng)前字節(jié)既不是\r,也不是\n
- 表示接受不完整,需要繼續(xù)接收,返回LINE_OPEN
/*從狀態(tài)機(jī),用于分析出一行內(nèi)容返回值為行的讀取狀態(tài),由LINE_OK,LINE_BAD,LINE_OPENm_read_idx:指向緩沖區(qū)m_read_buf的數(shù)據(jù)末尾的下一個(gè)字節(jié)m_checked_idx:指向從狀態(tài)機(jī)當(dāng)前正在分析的字節(jié)
*/
// 解析一行,判斷依據(jù)\r\n
http_conn::LINE_STATUS http_conn::parse_line() {char temp;for ( ; m_checked_idx < m_read_idx; ++m_checked_idx ) {// temp 為將要分析的字節(jié)temp = m_read_buf[ m_checked_idx ];// 如果當(dāng)前是\r字符,則有可能會(huì)讀取到完整行if ( temp == '\r' ) {// 下一個(gè)字符達(dá)到了buffer結(jié)尾,則接收不完整,需要繼續(xù)接收if ( ( m_checked_idx + 1 ) == m_read_idx ) {return LINE_OPEN;} // 下一個(gè)字符是\n,將\r\n改為\0\0else if ( m_read_buf[ m_checked_idx + 1 ] == '\n' ) {m_read_buf[ m_checked_idx++ ] = '\0';m_read_buf[ m_checked_idx++ ] = '\0';return LINE_OK;}// 如果都不符合,則返回語(yǔ)法錯(cuò)誤return LINE_BAD;} // 如果當(dāng)前字符是\n,也有可能讀取到完整行// 一般是上次讀取到\r就到 buffer 末尾了,沒(méi)有接收完整,再次接收時(shí)會(huì)出現(xiàn)這種情況else if( temp == '\n' ) {// 前一個(gè)字符是\r,則接收完整if( ( m_checked_idx > 1) && ( m_read_buf[ m_checked_idx - 1 ] == '\r' ) ) {m_read_buf[ m_checked_idx-1 ] = '\0';m_read_buf[ m_checked_idx++ ] = '\0';return LINE_OK;}return LINE_BAD;}}// 并沒(méi)有找到\r\n,需要繼續(xù)接收return LINE_OPEN;
}
例如:
>>http get請(qǐng)求報(bào)文的格式
請(qǐng)求行\(zhòng)r\n
請(qǐng)求頭\r\n
空行(\r\n)提示: 每項(xiàng)信息之間都需要一個(gè)\r\n,是由http協(xié)議規(guī)定>>http get請(qǐng)求報(bào)文的格式
請(qǐng)求行\(zhòng)0\0
請(qǐng)求頭\0\0
空行(\0\0)
(2)主從狀態(tài)機(jī),http_conn::process_read()
>>解析報(bào)文整體流程
process_read 通過(guò) while 循環(huán),將主狀態(tài)機(jī)進(jìn)行封裝,對(duì)報(bào)文的每一行進(jìn)行循環(huán)處理
http_conn::process_read() 是一個(gè)HTTP請(qǐng)求處理函數(shù),用于解析HTTP請(qǐng)求報(bào)文
① 解析一行數(shù)據(jù),得到不同狀態(tài)(包括請(qǐng)求行、請(qǐng)求頭、請(qǐng)求體)
- LINE_OK
- LINE_BAD
- LINE_OPEN
② 根據(jù)不同的狀態(tài),解析請(qǐng)求行、請(qǐng)求頭或請(qǐng)求體,并進(jìn)行相應(yīng)的處理
- CHECK_STATE_REQUESTLINE ---- parse_request_line()
- CHECK_STATE_HEADER ---- parse_headers()
- CHECK_STATE_CONTENT ---- parse_content()
?③ 對(duì)于GET請(qǐng)求,根據(jù)具體的請(qǐng)求信息進(jìn)行預(yù)處理,分析目標(biāo)文件屬性,并將其映射到內(nèi)存地址m_file_address處
- do_request()
④ 返回處理結(jié)果,包括成功、失敗或請(qǐng)求不完整等狀態(tài)碼???????
>>主從狀態(tài)機(jī)模式
主從狀態(tài)機(jī)模式:http_conn::process_read()用以應(yīng)對(duì)不同的狀態(tài)處理。
- 一個(gè)狀態(tài)機(jī)作為主要的控制器,而其他狀態(tài)機(jī)則被設(shè)計(jì)為從狀態(tài)機(jī)
- 主狀態(tài)機(jī)將它們之間的通信和事件處理進(jìn)行協(xié)調(diào)
- 主狀態(tài)機(jī)是在系統(tǒng)啟動(dòng)時(shí)創(chuàng)建的
- 從狀態(tài)機(jī)可以在系統(tǒng)運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建、注冊(cè)或注銷(xiāo)
主狀態(tài)機(jī) process_read() 中進(jìn)行循環(huán),直到解析完整個(gè) HTTP 請(qǐng)求數(shù)據(jù)
當(dāng)且僅當(dāng)解析到請(qǐng)求體時(shí),才需要進(jìn)一步判斷是否已經(jīng)解析完整個(gè) HTTP 請(qǐng)求數(shù)據(jù)
① 表示若當(dāng)前正在解析請(qǐng)求體,并且從狀態(tài)機(jī)已經(jīng)成功解析了一行請(qǐng)求數(shù)據(jù),則需要繼續(xù)解析下一行請(qǐng)求數(shù)據(jù)?
(m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK)
② 或者從狀態(tài)機(jī)還未成功解析出一行完整的請(qǐng)求數(shù)據(jù),就需要不斷循環(huán)調(diào)用從狀態(tài)機(jī)、解析請(qǐng)求數(shù)據(jù)
((line_status = parse_line()) == LINE_OK))
?(line_status = parse_line()) 表示調(diào)用從狀態(tài)機(jī) parse_line() ,并將返回值賦給變量 line_status
- 解析成功,line_status = LINE_OK
- 否則,line_status 為 LINE_OPEN、LINE_BAD等
③ 直到解析完成整個(gè) HTTP 請(qǐng)求數(shù)據(jù)
// 主狀態(tài)機(jī),解析請(qǐng)求
http_conn::HTTP_CODE http_conn::process_read() {// 定義初始狀態(tài)LINE_STATUS line_status = LINE_OK;HTTP_CODE ret = NO_REQUEST;char* text = 0;// 解析一行數(shù)據(jù),得到不同狀態(tài)// OK表示正常// 主狀態(tài)機(jī) && 從狀態(tài)機(jī) || 解析到一行完整數(shù)據(jù)(或者請(qǐng)求體)// 這里主狀態(tài)機(jī)指的是process_read()函數(shù),從狀態(tài)機(jī)指的是parse_line()while (((m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK))|| ((line_status = parse_line()) == LINE_OK)) {// 獲取一行數(shù)據(jù)text = get_line();m_start_line = m_checked_idx;printf( "got 1 http line: %s\n", text );switch ( m_check_state ) {case CHECK_STATE_REQUESTLINE: {// 解析請(qǐng)求行,也就是 GET 中的 GET/*通過(guò)請(qǐng)求行的解析我們可以判斷該HTTP請(qǐng)求的類(lèi)型(GET/POST),而請(qǐng)求行中最重要的部分就是URL部分將這部分保存下來(lái)用于后面的生成HTTP響應(yīng)*/ret = parse_request_line( text );if ( ret == BAD_REQUEST ) {return BAD_REQUEST;}break;// 正常解析就break}case CHECK_STATE_HEADER: {ret = parse_headers( text );//解析請(qǐng)求頭,GET 和 POST 中空行以上,請(qǐng)求行以下的部分if ( ret == BAD_REQUEST ) {return BAD_REQUEST;} else if ( ret == GET_REQUEST ) {//遇到換行符就默認(rèn)你解析完請(qǐng)求頭,不管后面還有沒(méi)有內(nèi)容return do_request();//解析具體的請(qǐng)求信息}break;}case CHECK_STATE_CONTENT: {/*解析請(qǐng)求數(shù)據(jù),對(duì)于GET來(lái)說(shuō)這部分是空的,因?yàn)檫@部分內(nèi)容已經(jīng)以明文的方式包含在了請(qǐng)求行中的URL部分了;只有POST的這部分是有數(shù)據(jù)的*/ret = parse_content( text );if ( ret == GET_REQUEST ) {/*do_request() 需要做:需要首先對(duì) GET請(qǐng)求 和不同的 POST請(qǐng)求(登錄、注冊(cè)、請(qǐng)求圖片、視頻等等)做不同的預(yù)處理,然后分析目標(biāo)文件的屬性,若目標(biāo)文件存在、對(duì)所有用戶(hù)可讀且不是目錄時(shí),則使用mmap將其映射到內(nèi)存地址m_file_address處,并告訴調(diào)用者獲取文件成功*/return do_request();}line_status = LINE_OPEN;break;}default: {return INTERNAL_ERROR;}}}return NO_REQUEST;//主狀態(tài)機(jī)請(qǐng)求不完整
}
注意在http_conn.h 編寫(xiě) get_line()函數(shù)
// m_start_line是已經(jīng)解析的字符
// get_line用于將指針向后偏移,指向未處理的字符
/*m_start_line是行在bufffer中的起始位置,將該位置后面的數(shù)據(jù)賦給text此時(shí)從狀態(tài)機(jī)已提前將一行的末尾字符\r\n變?yōu)閈0\0,所以text可以直接取出完整的行進(jìn)行解析
*/
char* get_line() { return m_read_buf + m_start_line;
}
??(3)主狀態(tài)機(jī)狀態(tài)?
>>主狀態(tài)機(jī)邏輯?
主狀態(tài)機(jī)初始狀態(tài)是CHECK_STATE_REQUESTLINE,通過(guò)調(diào)用從狀態(tài)機(jī)來(lái)驅(qū)動(dòng)主狀態(tài)機(jī),在主狀態(tài)機(jī)進(jìn)行解析前,從狀態(tài)機(jī)已經(jīng)將每一行的末尾\r\n符號(hào)改為\0\0,以便于主狀態(tài)機(jī)直接取出對(duì)應(yīng)字符串進(jìn)行處理。
CHECK_STATE_REQUESTLINE
- 主狀態(tài)機(jī)的初始狀態(tài),調(diào)用parse_request_line函數(shù)解析請(qǐng)求行
- 解析函數(shù)從m_read_buf中解析HTTP請(qǐng)求行,獲得請(qǐng)求方法,目標(biāo)URL及HTTP版本號(hào)
- 解析完成后主狀態(tài)機(jī)的狀態(tài)變?yōu)?strong>CHECK_STATE_HEADER
// 解析HTTP請(qǐng)求行,獲得請(qǐng)求方法,目標(biāo)URL,以及HTTP版本號(hào)
http_conn::HTTP_CODE http_conn::parse_request_line(char* text) {// 在HTTP報(bào)文中,請(qǐng)求行用來(lái)說(shuō)明請(qǐng)求類(lèi)型,要訪(fǎng)問(wèn)的資源以及所使用的HTTP版本,// 其中各個(gè)部分之間通過(guò)\t或空格分隔// 請(qǐng)求行中最先含有空格和\t任一字符的位置并返回// GET /index.html HTTP/1.1m_url = strpbrk(text, " \t"); // 判斷第二個(gè)參數(shù)中的字符哪個(gè)在text中最先出現(xiàn)// 如果沒(méi)有空格或\t,則報(bào)文格式有誤if (! m_url) { return BAD_REQUEST;}// 將該位置改為\0,用于將前面數(shù)據(jù)取出// GET\0/index.html HTTP/1.1*m_url++ = '\0'; // 置位空字符,字符串結(jié)束符// 取出數(shù)據(jù),并通過(guò)與GET和POST比較,以確定請(qǐng)求方式char* method = text;if ( strcasecmp(method, "GET") == 0 ) { // 忽略大小寫(xiě)比較m_method = GET;} else {return BAD_REQUEST;}// /index.html HTTP/1.1// 檢索字符串 str1 中第一個(gè)不在字符串 str2 中出現(xiàn)的字符下標(biāo)。m_version = strpbrk( m_url, " \t" );if (!m_version) {return BAD_REQUEST;}*m_version++ = '\0';// 僅支持HTTP/1.1if (strcasecmp( m_version, "HTTP/1.1") != 0 ) {return BAD_REQUEST;}/*** http://192.168.110.129:10000/index.html* 對(duì)請(qǐng)求資源前7個(gè)字符進(jìn)行判斷* 這里主要是有些報(bào)文的請(qǐng)求資源中會(huì)帶有http://,這里需要對(duì)這種情況進(jìn)行單獨(dú)處理*/if (strncasecmp(m_url, "http://", 7) == 0 ) { m_url += 7;// 在參數(shù) str 所指向的字符串中搜索第一次出現(xiàn)字符 c(一個(gè)無(wú)符號(hào)字符)的位置。m_url = strchr( m_url, '/' );}// 同樣增加https情況if (strncasecmp(m_url, "https://", 8) == 0 ) { m_url += 8;m_url = strchr( m_url, '/' );}// 一般的不會(huì)帶有上述兩種符號(hào),直接是單獨(dú)的/或/后面帶訪(fǎng)問(wèn)資源if ( !m_url || m_url[0] != '/' ) {return BAD_REQUEST;}// 請(qǐng)求行處理完畢,將主狀態(tài)機(jī)轉(zhuǎn)移處理請(qǐng)求頭m_check_state = CHECK_STATE_HEADER; // 檢查狀態(tài)變成檢查頭return NO_REQUEST;
}
解析完請(qǐng)求行后,主狀態(tài)機(jī)繼續(xù)分析請(qǐng)求頭。在報(bào)文中,請(qǐng)求頭和空行的處理使用的同一個(gè)函數(shù),這里通過(guò)判斷當(dāng)前的text首位是不是\0字符。
- 若是,則表示當(dāng)前處理的是空行
- 若不是,則表示當(dāng)前處理的是請(qǐng)求頭?
CHECK_STATE_HEADER
- 調(diào)用parse_headers函數(shù)解析請(qǐng)求頭部信息
- 判斷是空行還是請(qǐng)求頭。若是空行,進(jìn)而判斷Content-length是否為0,如果不是0,表明是POST請(qǐng)求,則狀態(tài)轉(zhuǎn)移到CHECK_STATE_CONTENT;否則說(shuō)明是GET,則報(bào)文解析結(jié)束
- 若解析的是請(qǐng)求頭部字段,則主要分析Connection字段,Content-length字段等
- Connection字段判斷是keep-alive 還是 close,決定是長(zhǎng)連接還是短連接;如果是長(zhǎng)連接,則將linger標(biāo)志設(shè)置為true
- Content-length字段,這里用于讀取post請(qǐng)求的消息體長(zhǎng)度
// 解析HTTP請(qǐng)求的一個(gè)頭部信息
http_conn::HTTP_CODE http_conn::parse_headers(char* text) { // 遇到空行,表示頭部字段解析完畢if( text[0] == '\0' ) {// 判斷是GET 還是 POST 請(qǐng)求/*如果HTTP請(qǐng)求有消息體,則還需要讀取m_content_length字節(jié)的消息體,狀態(tài)機(jī)轉(zhuǎn)移到CHECK_STATE_CONTENT狀態(tài)*/if ( m_content_length != 0 ) {// POST需要跳轉(zhuǎn)到消息體處理狀態(tài)m_check_state = CHECK_STATE_CONTENT;return NO_REQUEST;}// 否則說(shuō)明我們已經(jīng)得到了一個(gè)完整的HTTP請(qǐng)求return GET_REQUEST;} // 解析請(qǐng)求頭部連接字段else if ( strncasecmp( text, "Connection:", 11 ) == 0 ) {// 處理Connection 頭部字段 Connection: keep-alivetext += 11;// 跳過(guò)空格和\t字符text += strspn( text, " \t" );if ( strcasecmp( text, "keep-alive" ) == 0 ) {// 如果是長(zhǎng)連接,則將linger標(biāo)志設(shè)置為truem_linger = true;}} // 解析請(qǐng)求頭部?jī)?nèi)容長(zhǎng)度字段else if ( strncasecmp( text, "Content-Length:", 15 ) == 0 ) {// 處理Content-Length頭部字段text += 15;text += strspn( text, " \t" );m_content_length = atol(text);} // 解析請(qǐng)求頭部HOST字段else if ( strncasecmp( text, "Host:", 5 ) == 0 ) {// 處理Host頭部字段text += 5;text += strspn( text, " \t" );m_host = text;} else {printf( "oop! unknow header %s\n", text );}return NO_REQUEST;
}
GET和POST請(qǐng)求報(bào)文的區(qū)別之一是有無(wú)消息體部分。GET請(qǐng)求沒(méi)有消息體,當(dāng)解析完空行之后,便完成了報(bào)文的解析。僅用從狀態(tài)機(jī)的狀態(tài)(line_status = parse_line()) == LINE_OK語(yǔ)句即可。
在POST請(qǐng)求報(bào)文中,消息體的末尾沒(méi)有任何字符
>>http post請(qǐng)求報(bào)文的格式
請(qǐng)求行\(zhòng)r\n
請(qǐng)求頭\r\n
空行(\r\n)
請(qǐng)求體
所以無(wú)法使用從狀態(tài)機(jī)的狀態(tài),轉(zhuǎn)而使用主狀態(tài)機(jī)的狀態(tài)作為循環(huán)入口條件
((m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK))
解析完消息體之后,報(bào)文的完整解析也就完成了,此時(shí)主狀態(tài)機(jī)的狀態(tài)還是CHECK_STATE_CONTENT。意味著符合循環(huán)入口條件,還會(huì)再次進(jìn)入循環(huán)。為此,需要在完成請(qǐng)求體解析后,將line_status變量更改為LINE_OPEN,便可跳出循環(huán),完成報(bào)文解析任務(wù)
while (((m_check_state == CHECK_STATE_CONTENT) && (line_status == LINE_OK))|| ((line_status = parse_line()) == LINE_OK)) {// 獲取一行數(shù)據(jù)text = get_line();m_start_line = m_checked_idx;printf( "got 1 http line: %s\n", text );switch ( m_check_state ) {......case CHECK_STATE_CONTENT: {ret = parse_content( text );if ( ret == GET_REQUEST ) {return do_request();}// 在完成請(qǐng)求體解析后,將line_status變量更改為L(zhǎng)INE_OPEN,跳出循環(huán),完成報(bào)文解析任務(wù)line_status = LINE_OPEN;break;}......}
}
CHECK_STATE_HEADER
- 僅用于解析POST請(qǐng)求,調(diào)用parse_content函數(shù)解析請(qǐng)求體
- 用于保存post請(qǐng)求消息體,為后面的登錄和注冊(cè)做準(zhǔn)備
// 我們沒(méi)有真正解析HTTP請(qǐng)求的消息體,只是判斷它是否被完整的讀入了
http_conn::HTTP_CODE http_conn::parse_content( char* text ) {// 判斷buffer中是否讀取了消息體if ( m_read_idx >= ( m_content_length + m_checked_idx ) ){text[ m_content_length ] = '\0';return GET_REQUEST;}return NO_REQUEST;
}
參考和推薦文章:
最新版Web服務(wù)器項(xiàng)目詳解 - 05 http連接處理(中) (qq.com)https://mp.weixin.qq.com/s?__biz=MzAxNzU2MzcwMw==&mid=2649274278&idx=7&sn=d1ab62872c3ddac765d2d80bbebfb0dd&chksm=83ffbefeb48837e808caad089f23c340e1348efb94bef88be355f4d9aedb0f9784e1f9e072b1&scene=178&cur_album_id=1339230165934882817#rd【從0開(kāi)始編寫(xiě)webserver·基礎(chǔ)篇#02】服務(wù)器的核心---I/O處理單元和任務(wù)類(lèi) - dayceng - 博客園 (cnblogs.com)
https://www.cnblogs.com/DAYceng/p/17418584.html#%E5%A4%84%E7%90%86%E6%96%B0%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E8%BF%9E%E6%8E%A5%E8%AF%B7%E6%B1%82