任振興
摘要:HTTP(超文本傳輸協(xié)議)是一個(gè)基于請(qǐng)求與響應(yīng)模式的、無(wú)狀態(tài)的、應(yīng)用層的協(xié)議,常基于TCP的連接方式。絕大多數(shù)的Web開(kāi)發(fā),都是構(gòu)建在HTTP協(xié)議之上的Web應(yīng)用。該文所給的程序一個(gè)簡(jiǎn)單的基于HTTP協(xié)議的服務(wù)器程序,實(shí)現(xiàn)所有基于HTTP協(xié)議的服務(wù)器程序的核心部分。該文對(duì)所給的具有圖形界面HTTP服務(wù)器程序源代碼進(jìn)行分析和調(diào)試,介紹程序?qū)崿F(xiàn)的主要功能,運(yùn)行過(guò)程以及程序中各類實(shí)現(xiàn)的功能。
關(guān)鍵詞:HTTP;HTTP協(xié)議;服務(wù)器
中圖分類號(hào):TP393文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1009-3044(2012)02-0282-03
1 HTTP協(xié)議介紹
1.1 HTTP協(xié)議
HTTP(超文本傳輸協(xié)議)是一個(gè)基于請(qǐng)求與響應(yīng)模式的、無(wú)狀態(tài)的、應(yīng)用層的協(xié)議,?;赥CP的連接方式。絕大多數(shù)的Web開(kāi)發(fā),都是構(gòu)建在HTTP協(xié)議之上的Web應(yīng)用。本程序?qū)崿F(xiàn)的是一個(gè)輕量級(jí)的web服務(wù)器?!?】
1.2 HTTP組成
HTTP請(qǐng)求由三部分組成,分別是:請(qǐng)求行、消息報(bào)頭、請(qǐng)求正文。
1)請(qǐng)求行以一個(gè)方法符號(hào)開(kāi)頭,以空格分開(kāi),后面跟著請(qǐng)求的URI和協(xié)議的版本,格式如下:Method Request-URI HTTP-Version CRLF。其中Method表示請(qǐng)求方法;Request-URI是一個(gè)統(tǒng)一資源標(biāo)識(shí)符;HTTP-Version表示請(qǐng)求的HTTP協(xié)議版本;CRLF表示回車和換行(除了作為結(jié)尾的CRLF外,不允許出現(xiàn)單獨(dú)的CR或LF字符)。
2)HTTP響應(yīng)由三個(gè)部分組成,分別是:狀態(tài)行、消息報(bào)頭、響應(yīng)正文。
狀態(tài)行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF,其中,HTTP-Version表示服務(wù)器HTTP協(xié)議的版本;Status-Code表示服務(wù)器發(fā)回的響應(yīng)狀態(tài)代碼;Reason-Phrase表示狀態(tài)代碼的文本描述。狀態(tài)代碼有三位數(shù)字組成,第一個(gè)數(shù)字定義了響應(yīng)的類別,且有五種
可能取值:【2】
1xx:指示信息--表示請(qǐng)求已接收,繼續(xù)處理。
2xx:成功--表示請(qǐng)求已被成功接收、理解、接受。
3xx:重定向--要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的操作。
4xx:客戶端錯(cuò)誤--請(qǐng)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無(wú)法實(shí)現(xiàn)。
5xx:服務(wù)器端錯(cuò)誤--服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求。
常見(jiàn)狀態(tài)代碼、狀態(tài)描述、說(shuō)明:
200 OK//客戶端請(qǐng)求成功
400 Bad Request//客戶端請(qǐng)求有語(yǔ)法錯(cuò)誤,不能被服務(wù)器所理解
401 Unauthorized //請(qǐng)求未經(jīng)授權(quán),這個(gè)狀態(tài)代碼必須和WWW-Authenticate報(bào)頭域一起使用
403 Forbidden//服務(wù)器收到請(qǐng)求,但是拒絕提供服務(wù)
404 Not Found//請(qǐng)求資源不存在,eg:輸入了錯(cuò)誤的URL
500 Internal Server Error //服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤
503 Server Unavailable//服務(wù)器當(dāng)前不能處理客戶端的請(qǐng)求,一段時(shí)間后,可能恢復(fù)正常
HTTP消息由客戶端到服務(wù)器的請(qǐng)求和服務(wù)器到客戶端的響應(yīng)組成。請(qǐng)求消息和響應(yīng)消息都是由開(kāi)始行(對(duì)于請(qǐng)求消息,開(kāi)始行就是請(qǐng)求行,對(duì)于響應(yīng)消息,開(kāi)始行就是狀態(tài)行),消息報(bào)頭(可選),空行(只有CRLF的行),消息正文(可選)組成。
3)請(qǐng)求正文。
2 HTTPSVR程序功能
2.1工作原理
建立在請(qǐng)求/響應(yīng)模式(Request/Response)上:一個(gè)客戶端與服務(wù)器建立連接后,客戶端向Web服務(wù)器發(fā)出一個(gè)HTTP請(qǐng)求行;Web服務(wù)器在收到有效的請(qǐng)求后,返回一個(gè)狀態(tài)行或多個(gè)響應(yīng)標(biāo)題、一個(gè)空白行和相關(guān)文檔。HTTP協(xié)議使用的端口號(hào),通常為80。
2.2工作流程圖
HTTPSVR的信息交換過(guò)程,它分四個(gè)過(guò)程:建立連接、發(fā)送請(qǐng)求信息、獲取服務(wù)器響應(yīng)狀態(tài)、關(guān)閉連接。其中較為復(fù)雜的過(guò)程是:發(fā)送請(qǐng)求信息、獲取服務(wù)器響應(yīng)狀態(tài)?!?】如圖1所示:
圖1工作流程圖
1)建立連接:連接的建立是通過(guò)申請(qǐng)?zhí)捉幼?Socket)實(shí)現(xiàn)的??蛻舸蜷_(kāi)一個(gè)套接字并把它約束在一個(gè)端口上,如果成功,就相當(dāng)于建立了一個(gè)虛擬文件。以后就可以在該虛擬文件上寫(xiě)數(shù)據(jù)并通過(guò)網(wǎng)絡(luò)向外傳送。
2)發(fā)送請(qǐng)求:打開(kāi)一個(gè)連接后,客戶機(jī)把請(qǐng)求消息送到服務(wù)器的停留端口上,完成提出請(qǐng)求動(dòng)作。
3)發(fā)送響應(yīng):服務(wù)器在處理完客戶的請(qǐng)求之后,要向客戶機(jī)發(fā)送響應(yīng)消息。
4)關(guān)閉連接:客戶和服務(wù)器雙方都可以通過(guò)關(guān)閉套接字來(lái)結(jié)束TCP/IP對(duì)話。
3 HTTPSVR程序分析
基于HTTPSVR所用到的類進(jìn)行分析:
1)CGenPage類(genpage.h/.cpp):這是通用設(shè)置的屬性對(duì)話框,是UI的一部分,設(shè)置屬性中的一般屬性,其中包括:服務(wù)器視圖界面的列表顯示、訪問(wèn)日志、圖標(biāo)顯示三個(gè)屬性。
2)CHitDoc類(http.h/.cpp):這是http響應(yīng)類,處理客戶端請(qǐng)求的URL地址,并且進(jìn)行本地地址轉(zhuǎn)化,指向服務(wù)器資源目錄。
3)CHitDoc(httpDoc.h/.cpp):這是mfc的doc視圖主類。
4)CHttpSvrApp(httpsvr.h/.cpp):這是處理web服務(wù)器的初始化,包括訪問(wèn)目錄、登錄日志、HTTP端口的設(shè)置、文件的保存等。
5)CHttpSvrView(httpview.h/.cpp):這是UI中的日志列表控件,主要是客戶登錄服務(wù)器的訪問(wèn)的記錄和錯(cuò)誤記錄的顯示,包括文件的路徑,用戶登錄時(shí)間,用戶訪問(wèn)次數(shù)等。
6)CListenSocket(listen.h/.cpp):這是監(jiān)聽(tīng)套接字的拓展類,用于創(chuàng)建服務(wù)器在端口(默認(rèn)80端口)上的監(jiān)聽(tīng)套接字。
7)CNamePage(namepage.h/.cpp):這是一般屬性中的Server Name屬性頁(yè),是UI的一部分,其中包括服務(wù)器名稱的設(shè)置和端口的設(shè)置,而服務(wù)器名稱有默認(rèn)和指定的兩種情況。
8)CNoRootDlg(NoRoot.h/.cpp):這是服務(wù)器地址屬性設(shè)置對(duì)話框,是UI的一部分。在此部分中,如果URL不存在則會(huì)彈出此對(duì)話框,讓我們輸入正確的URL地址。
9)CRequestSocket(reqsock.h/.cpp):這是服務(wù)器的主要功能實(shí)現(xiàn)部分,包括接受、發(fā)送數(shù)據(jù),并且支持cgi的動(dòng)態(tài)生成頁(yè)面,詳細(xì)的功能有:接收請(qǐng)求,對(duì)http請(qǐng)求報(bào)文進(jìn)行解析,根據(jù)請(qǐng)求內(nèi)容構(gòu)造響應(yīng)報(bào)文。
10)CRequest(Request.h/.cpp):實(shí)現(xiàn)保存上一次web請(qǐng)求的內(nèi)容。
11)CRootPage(RootPage.h/.cpp):這是一般屬性中Root Dir對(duì)話框,是UI的一部分。在此部分我們可以對(duì)服務(wù)器的資源根目錄進(jìn)行設(shè)置,同時(shí)也可以重置為默認(rèn)的根目錄G:WebPages。
12)CasyncSocket(AsyncSock.h /.cpp):這是異步非阻塞類,它的Create()函數(shù),除了創(chuàng)建了一個(gè)SOCKET以外,使用WSAAsyncSe? lect()將這個(gè)SOCKET與該窗口對(duì)象關(guān)聯(lián),以讓該窗口對(duì)象處理來(lái)自Socket的事件(消息),然而CSocketWnd收到Socket事件之后,只是簡(jiǎn)單地回調(diào)CAsyncSocket::OnReceive() CAsyncSocket::OnSend(),CAsyncSocket::OnAccept(),CAsyncSocket::OnConnect()等虛函數(shù)。所以CAsyncSocket的派生類,只需要在這些虛函數(shù)里添加發(fā)送和接收的代碼。
使用CAsyncSocket時(shí),如果使用Create缺省創(chuàng)建socket,則所有網(wǎng)絡(luò)I/O都是異步操作,進(jìn)行有關(guān)網(wǎng)絡(luò)數(shù)據(jù)傳輸時(shí)需要用到以下函數(shù):OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend?!?】
4 HTTPSRV服務(wù)器程序分析流程
4.1執(zhí)行函數(shù)
服務(wù)器程序分析首先在MFC中的文件APPMODULE.CPP的_tWinMain函數(shù)處開(kāi)始執(zhí)行,執(zhí)行該文件的return AfxWinMain函數(shù)。
4.2客戶端鏈接服務(wù)器
在服務(wù)器程序中由AfxWinMain函數(shù)負(fù)責(zé)建立工作線程pWinThread,此線程對(duì)HttpSvr進(jìn)行初始化工作,接著調(diào)用HttpSvr.cpp文件中的BOOL CHttpSvrApp::InitInstance方法對(duì)WEB服務(wù)器進(jìn)行初始化,然后運(yùn)行線程的主函數(shù),最后在THRDCORE.CPP文件中運(yùn)行int CWinThread::Run函數(shù),開(kāi)始HTTP服務(wù)器的循環(huán)。在循環(huán)中,首先通過(guò)CListenSocket::OnAccept( int nErrorCode )函數(shù)生成CRe? questSocket類,將其設(shè)置用于監(jiān)聽(tīng)8080端口。監(jiān)聽(tīng)到連接請(qǐng)求時(shí),Accept函數(shù)創(chuàng)建新的套接字pRequest并返回句柄,AsyncSelect函數(shù)監(jiān)聽(tīng)8080端口的FD_READ和FD_CLOSE兩個(gè)事件,當(dāng)傳入FD_READ事件時(shí),準(zhǔn)備接收,并且觸發(fā)OnReceive()函數(shù),如果傳入FD_WRITE事件,發(fā)送數(shù)據(jù)的時(shí)候,OnSend()函數(shù)就會(huì)觸發(fā)。設(shè)置端口為8080,將web服務(wù)文件夾地址Root Dir指向root所在地址。此時(shí)顯示結(jié)果如圖2所示。
圖2鏈接服務(wù)器
4.3請(qǐng)求客戶
一旦有數(shù)據(jù)到達(dá)時(shí),執(zhí)行ReqSock.cpp中的void CRequestSocket::OnReceive(int nErrorCode)函數(shù)。程序代碼將傳輸控制層上傳的數(shù)據(jù)包存放在請(qǐng)求和應(yīng)答報(bào)文的緩沖區(qū)m_buf中。
接下來(lái)在ReqSock.cpp文件中,根據(jù)響應(yīng)狀態(tài)m_reqStatus的不同,使用swich語(yǔ)句對(duì)接收到的數(shù)據(jù)包進(jìn)行不同的響應(yīng)處理。當(dāng)瀏覽窗口發(fā)送第一個(gè)數(shù)據(jù)包時(shí),響應(yīng)狀態(tài)m_reqStatus被設(shè)置為REQ_REQUEST,之后,對(duì)請(qǐng)求數(shù)據(jù)包的每一行進(jìn)行處理,根據(jù)http的協(xié)議使用ProcessLine方法對(duì)m_pRequest進(jìn)行初始化,完成以上操作之后,當(dāng)請(qǐng)求狀態(tài)m_reqStatus == REQ_DONE,調(diào)用判斷StartRe? sponse方法來(lái)構(gòu)造應(yīng)答報(bào)文,運(yùn)行AsyncSelect( FD_WRITE | FD_CLOSE )函數(shù),之后其調(diào)用void CRequestSocket::OnSend(int nError? Code)方法將緩存m_buf的應(yīng)答報(bào)文發(fā)送給客戶端?!?】
5結(jié)論
該文通過(guò)對(duì)基于HTTP協(xié)議服務(wù)器程序以及瀏覽器與服務(wù)器的交互過(guò)程的分析,熟悉了HTTP協(xié)議的服務(wù)工作流程,對(duì)運(yùn)用Winsock編程來(lái)逐步解析HTTP協(xié)議的服務(wù)器程序的核心部分有更深層次的了解。在本程序中是通過(guò)多線程與異步操作的理論的方法來(lái)實(shí)現(xiàn)多個(gè)客戶端同時(shí)訪問(wèn)的處理,同時(shí)熟悉了多線程的創(chuàng)建過(guò)程以及異步操作理論,了解了各個(gè)線程間是如何協(xié)作的。
不足的是,本實(shí)例程序?qū)崿F(xiàn)的只是一個(gè)輕量級(jí)的服務(wù)器,它的分析過(guò)程相對(duì)比較簡(jiǎn)單。但是只要熟悉了基于HTTP協(xié)議服務(wù)器創(chuàng)建的基本原理,將來(lái)就能在此基礎(chǔ)上實(shí)現(xiàn)功能更加強(qiáng)大的服務(wù)器。
參考文獻(xiàn):
[1]葉強(qiáng).超文本傳輸協(xié)議-HTTP/1.0[J].科技情報(bào)開(kāi)發(fā)與經(jīng)濟(jì),2004(8):66-68.
[2] Fielding R, Gettys J, Mogul J, Frystyk H. RFC 2068 Hypertext Transfer Protocol-HTTP/1.1. MIT/LCS, UC Irvine,1997(1):37-38.
[3]蔡皖東. HTTP協(xié)議的傳輸機(jī)制與超文本鏈的研究[J].微電子學(xué)與計(jì)算機(jī),1997(4):53-55.
[4]李臘元,李春林.計(jì)算機(jī)網(wǎng)絡(luò)技術(shù)[M].北京:國(guó)防工業(yè)出版社, 2004.
[5] Andrew,S.Tanenbaum.計(jì)算機(jī)網(wǎng)絡(luò)[M].潘安民譯.北京:清華大學(xué)出版社,2004(8).