Web Server 王宏瑾
Induction (1) Server Client Web Browser TCP/IP HTTP/1.0
Induction (2) Create a socket ( port : 80 ) Bind Listen Accept 網 路 部 分 接收 HTTP需求資料 包含需求標頭和需求主體 送出 HTTP回覆資料 包含回覆標頭和回覆主體 Web Server 工作
Socket (1) Socket ID Protocol IP Address Port number Association Local IP Address Local Port number Remote IP Address Remote Port number Socket descriptor 通訊協定 本地IP位址 本地PORT 遠端IP位址 遠端PORT
Socket (2) Socket Type Stream socket Datagram socket byte-stream of two-way, reliable, sequenced, full-duplex and out-of-band connection-oriented ( TCP/IP ) Datagram socket datagram-stream of two-way, unreliable connectionless ( UDP )
Socket (3) socket() bind() (可有可無) connect() Winsock應用程式介面 Socket descriptor 通訊協定 本地IP位址 本地PORT 遠端IP位址 遠端PORT 網路系統 TCP Client 程式
Socket (4) socket() bind() listen() accept() Winsock應用程式介面 Socket descriptor 通訊協定 本地IP位址 本地PORT 遠端IP位址 遠端PORT 網路系統 TCP Server 程式
Web Server 類別層級架構分為七部分 System Environment (使工作平台與程式內部分離) Client Information (記錄工作環境與socket) Service Provider (接收Client端的資料) Service Response (處理各種的回應) Service Request (擷取 HTTP需求資料) Access of Resource (使用HKEY與HANDLE) Exception (異常狀況之處理)
Web Server - Object Class (1) System Environment IBasicEnv Cwin32TCPEnv Cwin32TCPEnv_2 CHttpAgent Service Provider CBasicClient Client Information CClientStub
Web Server - Object Class (2) Service Response IHttpResponse CDocumeResponse CDirectoryResponse CCgiResponse Service Request CHttpRequest Access of Resource Cwin32InScopeRegistryKey Cwin32InScopeHandle
Web Server - Object Class (3) Exception ENSocket ( in IBasicEnv ) EbadSocket ( in IBasicEnv ) EPortNotOpen ( in IBasicEnv ) EsvncTimeout ( in IBasicEnv ) ETooManyService ( in IBasicEnv ) EInternalError ( in IhttpResponse ) ECgiErrorError ( in IhttpResponse ) EHttpConfig ( in Cwin32TCPEnv_2 ) EContentAccess ( in Cwin32TCPEnv_2 )
Program catch( IBasicEnv::ENoSocket & ) int main( void ) {// Abstraction for the underlying system char msg[ 256 ] ; try{ } catch ( ) { } return 0; } CWin32TCPEnv_2 Env ; 做一些初始化的動作 主程式部分 哪種異常狀況 對應處理方式 catch( IBasicEnv::ENoSocket & ) {Env.ShowError( "ERR> TCP/IP stack error." ) ;} catch( IBasicEnv::EBadSocket & ) {Env.ShowError( "ERR> Server socket is bad." ) ;} catch( IBasicEnv::EPortNotOpen & ) {Env.ShowError( "ERR> Server port not open." ) ;} catch( IBasicEnv::ETooManyServices & ) {Env.ShowError( "ERR> Too many services are running." ) ;} 異 常 處 理
for( int id = 1; TRUE; id++ ) { Env.ReadyPort( 80 ) ; for( int id = 1; TRUE; id++ ) { sprintf( msg, "SERVER> Ready to serve browser #%d...", id ) ; Env.ShowStatus( msg ) ; // Block here until client arrives Env.SpawnService( Env.BlockForClient( 80 ), ::HttpdThread, 80 ) ; } // Wait here when both service threads have started Env.ShowStatus( "SERVER> Waiting for service threads to end..." ) ; 主 程 式 Env.ReadyPort( 80 ); create a socket 指定port為80 bind listen
Web Server Env.SpawnService( Env.BlockForClient( 80 ), ::HttpdThread, 80 ) ; Env.BlockForClient( 80 ) 等待Client端建立連線,並傳回socket ::HttpdThread 處理Client端送來的資料 ( 判斷是否合法讀取 ) 送出HTTP回覆資料 Env.SpawnService Create Thread去負責Client端送來的另外資料
HTTP Request GET /home.html HTTP/1.1 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword* Referer: http://140.112.29.172:80/ Accept-Language: zh-tw Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows NT) Host: 140.112.29.172:80 Connection: Keep-Alive
Web Server Http需求資料 Http回覆資料 需求標頭 : 需求指令 ( 需求方法 資源名稱 適用的HTTP規格版本 ) 與其他資訊 需求主體 : 其他資訊 Http回覆資料 回覆標頭 : 狀態指令 ( 回覆資料的格式版本 所求資源的狀況 ) 與其他資訊 回覆主體 : 其他資訊 詳細內容請參考 rfc1945
Web Server DWORD __stdcall HttpdThread( LPVOID lpArg ) { // Client will be automatically disconnected once out of this thread CHttpAgent BernersLee( (CClientStub *)lpArg ) ; BernersLee.Engage() ; return 0 ; } CHttpAgent::CHttpAgent( CClientStub *pStub ) :CBasicClient( pStub ) {}
Web Server Engage() : CHttpRequest theRequest( this ) ; CWin32TCPEnv_2 *pEnv = (CWin32TCPEnv_2 *)m_pEnv ; try { IHttpResponse *pResponse ; // Attempt to resolve client request pResponse = theRequest.Resolve() ; (處理Client端送來的資料) pResponse->Deliver() ; (送出HTTP回覆資料) } catch (…) { 異常處理 }
pResponse = theRequest.Resolve() ; IHttpResponse *CHttpRequest::Resolve( void ) { // 每個request只由一個Request Object來服務 if( m_pResponse != NULL ) return m_pResponse ; // 接受Client端的Request Header m_pAgent->ReceiveUntilStr( m_szHeader, sizeof( m_szHeader ), 0, CRLF CRLF, 4 ) ; // Determine if the service is proxy or local if( ::strlen( GetScheme() ) > 0 ) throw ENoRemoteSupport() ; if( !GetEnv()->IsResourcePublic( GetUri() ) ) //讀取的Path是否合法 throw EInvalidRequest() ; if( GetEnv()->IsResourceDirectory( GetUri() ) ) //是否為Directory return new CDirectoryResponse( this ) ; if( ::strlen( GetCgiExecutable() ) > 0 ) //是否為可執行檔 return new CCgiResponse( this ) ; return new CDocumentResponse( this ) ; }
pResponse->Deliver() ; Web Server pResponse->Deliver() ; 若pResponse是指CDirectoryResponse( ) 則顯示出目錄內容 若pResponse是指CCgiResponse( ) 則執行 CGI 服務 若pResponse是指CDocumentResponse( ) 則顯示出顯示出檔案內容
Web Server 將input存成一個檔案 以此作為欲執行的CGI程式之input 將CGI程式執行完後的結果輸出到檔案 最後再將此檔傳回Client端
Web Server 由於整個程式是採用Exception Handling的機制,所以有可能exception發生後,使得一些在程式尾端的資源釋放動作不被執行到,所以採用更安全的資源控制方式。 Cin32InScopeRegistryKey 與 Cwin32InScopeHandle 分別對應到Win32環境下的 HKEY 及 HANDLE 兩種資源種類,這兩種資源要被 ::RegCloseKey 和 ::CloseHandle 來釋放,當一個變數的生命期結束時,此變數的Destructor會被呼叫,而達到釋放資源的效果。
ReceiveUntilStr( szInBuffer, sizeof( szInBuffer ), 0, " ", 1 ) ; 處理Client端送來的資料 // HTTP Request header: GET uri ... CRLF CRLF ReceiveUntilStr( szInBuffer, sizeof( szInBuffer ), 0, " ", 1 ) ; ShowStatus( "HTTPAGENT> Request method:" ) ; ShowStatus( szInBuffer ) ; ReceiveUntilStr( szUri, sizeof( szUri ), 0, " ", 1 ) ; ShowStatus( "HTTPAGENT> Request URI:" ) ; ShowStatus( szUri ) ; // Must receive all header until it ends in CRLF+CRLF ReceiveUntilStr( szInBuffer, sizeof( szInBuffer ), 0, CRLF CRLF, 4 ) ; // Attempt to resolve client request char szResponse[ 1024 ] ; CWin32TCPEnv_2 *pEnv = (CWin32TCPEnv_2 *)m_pEnv ; int cbResource = pEnv->GetResourceLength( szUri ) ; ::wsprintf( szResponse,"HTTP/1.0 200 OK" CRLF "CONTENT-LENGTH: %d" CRLF CRLF, cbResource ) ; SendUntilDone( szResponse, strlen( szResponse ), 0 ) ; pEnv->SendResource( m_Socket, szUri ) ; 送出HTTP回覆資料