首页 文学文摘 时政新闻 科技科普 经济法律 健康生活 管理财经 教育教学 文化艺术 社科历史

基于HTTP协议的服务器程序分析

作者:祝瑞 车敏 来源:现代电子技术


  摘要:绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。为了分析基于HTTP协议服务器程序以及浏览器与服务器的交互过程,以一个简单的基于HTTP协议中服务器程序为实例,探讨怎样使用多线程与异步操作的理论,运用WinSock编程来逐步解析HTTP协议的服务器程序的核心部分,这样既可达到避免调用线程阻塞的目的,又可提高服务器程序的可响应性。
  关键词:HTTP; HTTP协议; 服务器; WinSock
  中图分类号:TN91934文献标识码:A文章编号:1004373X(2012)04011703
  Analysis of server program based on HTTP protocol
  ZHU Rui
  (Department of Electronic Science, Xi’an Air Forces Engineering University, Xi’an 710043, China)
  
  Abstract: The overwhelming majority of Web developments are built based on Web application of HTTP protocol. To analyze the server program based on the HTTP protocol and the process of interaction between the browser and server, a simple HTTPbased server program is taken as an example to describe how to use the theories of multithreading and asynchronous operation, and the core section of HTTPbased server program is gradually resolved with Winsock programming. This method can achieve the purpose to avoid blocking the calling thread, and improve the responsiveness of a server program.
  Keywords: HTTP; HTTP protocol; server; WinSock
  
  
  收稿日期:20110913
  基金项目:陕西省电子信息系统和综合集成重点试验室基金项目资助项目(200904B)0引言
  Web的应用层协议HTTP是Web的核心。HTTP在Web的客户程序和服务器程序中得以实现。运行在不同端系统上的客户程序和服务器程序通过交换HTTP消息彼此交流。HTTP定义这些消息的结构以及客户和服务器如何交换这些消息。Web页面(Web Page)也称为文档,由多个对象构成。对象(Object)仅仅是可由单个URL寻址的文件,例如HTML文件、JPG图像、GIF图像、JAVA小应用程序、语音片段等。大多数Web页面由单个基本HIML文件和若干个所引用的对象构成。HTTP定义Web客户(即浏览器)如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户。
  1HTTP协议
  超文本传输协议(HTTP)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。本程序实现的是一个轻量级的Web服务器\[1\]。
  (1) HTTP请求由3部分组成,分别是:请求行、消息报头、请求正文。
  请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method RequestURI HTTPVersion CRLF 。其中 Method表示请求方法;RequestURI是一个统一资源标识符;HTTPVersion表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)\[2\]。
  (2) HTTP响应由3个部分组成,分别是:状态行、消息报头、响应正文。
  状态行格式是:HTTPVersion StatusCode ReasonPhrase CRLF,其中HTTPVersion表示服务器HTTP协议的版本;StatusCode表示服务器发回的响应状态代码;ReasonPhrase表示状态代码的文本描述。状态代码由3位数字组成,第1个数字定义了响应的类别,且有五种可能取值\[3\]:
  1xx:指示信息,表示请求已接收,继续处理;
  2xx:成功,表示请求已被成功接收、理解、接受;
  3xx:重定向,要完成请求必须进行更进一步的操作;
  4xx:客户端错误,请求有语法错误或请求无法实现;
  5xx:服务器端错误,服务器未能实现合法的请求。
  常见状态代码、状态描述、说明\[4\]:
  200 OK//客户端请求成功
  400 Bad Request//客户端请求有语法错误,不能被服务器所理解
  HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行)、消息报头(可选)、空行(只有CRLF的行)、消息正文(可选)组成。
  (3) 请求正文。
  2HTTPSVR程序分析
  HTTPSVR是一个简单的Web服务器,具有图形界面,可以指定端口和Web主目录,且可以生成Web访问的日志,它由一个无限循环构成:接收来自客户端的请求,根据HTTP协议解析并处理请求,然后发送应答至客户端。
  2.1HTTPSVR主要工作流程图
  HTTPSVR功能强大,具有丰富的图形界面,主要的工作流程如图1所示\[5\]。
  图1HTTPSVR工作流程图首先通过CListenSocket::OnAccept( int nErrorCode )函数生成CRequestSocket类,监听到连接请求时,Accept函数创建新的套接字pRequest并返回句柄,AsyncSelect函数监听80端口的FD_READ和FD_CLOSE两个事件,当传入FD_READ事件时,准备接收,并且触发OnReceive()函数,如果传入FD_WRITE事件,发送数据的时候,OnSend()函数就会触发。
  当有数据到达时,执行ReqSock.cpp中的void CRequestSocket::OnReceive(int nErrorCode)函数。代码int nBytes = Receive( m_buf.GetData(),m_buf.GetSize() )将tcp层上传的数据包存放在请求和应答报文的缓冲区m_buf中。
  接下来使用swich语句对3种请求状态(REQ_REQUEST,REQ_HEADER,REQ_BODY)进行处理,当请求状态m_reqStatus == REQ_DONE,调用StartResponse()开始构造应答报文。
  
  当StartResponse()构造应答报文结束后,利用AsyncSelect( FD_WRITE | FD_CLOSE );调用void CRequestSocket::OnSend(int nErrorCode)函数发送缓冲区m_buf中的数据\[6\]。
  2.2异步非阻塞类CAsyncSocket
  CAsyncSocket的Create()函数,除了创建了一个Socket以外,使用WSAAsyncSelect()将这个Socket与该窗口对象关联,以让该窗口对象处理来自Socket的事件(消息),然而CSocketWnd收到Socket事件之后,只是简单地回调CAsyncSocket::OnReceive() CAsyncSocket::OnSend(),CAsyncSocket::OnAccept(),CAsyncSocket::OnConnect()等虚函数。所以CAsyncSocket的派生类,只需要在这些虚函数里添加发送和接收的代码。使用CAsyncSocket时,如果使用Create缺省创建socket,则所有网络I/O都是异步操作,进行有关网络数据传输时需要用到以下函数:OnAccept,OnClose,OnConnect,OnOutOfBandData,OnReceive,OnSend\[7\]。
  2.3HTTPSVR关键类分析
  void CListenSocket::OnAccept(int nErrorCode) 创建新的CRequestSocket,并监听80端口,请求到达时返回套接字,并通过AsyncSelect(FD_READ|FD_CLOSE)调用void CRequestSocket::OnReceive(int nErrorCode)函数接收数据。
  void CRequestSocket::OnReceive(int nErrorCode) \[8\]实现httpsvr的所有主要功能,包括接受浏览器的请求,根据Http协议解析请求报文,构造应答报文,将处理后的数据发送回浏览器。Receive(m_buf.GetData(),m_buf.GetSize())函数接收来自浏览器的请求数据包,switch语句根据不同的状态处理。浏览器的请求状态m_reqStatus为REQ_REQUEST时,使用while循环进行处理。ProcessLine()函数对接收到报文的每一行进行解析,然后初始化m_pRequest类。Swich语句结束后调用StartResponse()根据m_pRequest中的解析结果构造应答报文,最后函数AsyncSelect( FD_WRITE | FD_CLOSE )调用OnSend函数进行发送。
  void CRequestSocket::OnSend(int nErrorCode)调用int nBytes = Send(m_buf.GetData(),m_cbOut)函数发送缓存中的数据,并对可能出现的错误进行处理,或者关闭或者重新进行发送。
  void CRequestSocket::ProcessLine(void)根据请求状态m_reqStatus的不同作出处理,完成对CRequest*m_pRequest的初始化。
  BOOL CRequestSocket::StartResponse(void)\[9\],该函数用于构造应答报文(将构造好的应答报文缓存在m_buf中,用void CRequestSocket::OnSend(int nErrorCode)发送m_buf),并根据不同的Method(GET,HEAD,POST)构造应答报文。例如当Method为GET时,表示浏览器需要浏览网页,就用FindTarget(strFile);在默认目录底下进行查找,如果存在则打开报文,并使用 StuffHeading()构造http协议要求的头部,然后使用StartTargetStuff();将刚才打开的网页内容填充到m_buf中,等待发送,构造完毕后再返回BOOL CRequestSocket::StartResponse(void),然后调用AsyncSelect(FD_WRITE |FD_CLOSE);该函数调度void CRequestSocket::OnSend(int nErrorCode)对缓存m_buf中的数据进行发送。
  BOOL CRequestSocket::FindTarget( CString& strFile ),由于浏览器发送过来的请求都是网络地址格式的,所以该函数将网络地址格式转为windows文件系统的路径格式。例如发送:GET /default.html,经过本函数转换之后变为c:\WebPages\default.html(httpsvr默认根目录为c:\WebPages)。
  void CRequestSocket::StartTargetStuff( void ),该函数读取客户端请求浏览的网页文件内容到m_buf中,等待进一步发送。
  UINT CGIThread( LPVOID pvParam )\[10\],该函数以线程的方式运行,调用相应的GCI函数处理,并输出结果到一个临时文件。该线程处理完毕后,再由BOOL CRequestSocket::StartResponse( void )将临时文件发给浏览器。
  3HTTPSRV程序执行过程
  3.1运行函数
  程序由MFC中的文件APPMODULE.CPP的_tWinMain函数开始执行,执行该文件的return AfxWinMain函数。
  3.2链接服务器
  由WINMAIN.CPP文件中的int AFXAPI AfxWinMain函数来创建工作线程pThread对httpsvr进行初始化工作,调用HttpSvr.cpp文件中的BOOL CHttpSvrApp::InitInstance方法对HTTP服务器进行初始化,然后运行线程的主函数,最后在THRDCORE.CPP文件中运行int CWinThread::Run函数,开始服务器的循环。
  在循环中,首先调用Listen.cpp中的void CListenSocket::OnAccept方法,生成CRequestSocket,将其设置用于监听8080端口。设置端口为8080,将Web服务文件夹地址Root Dir指向root所在地址。此时显示结果如图2所示。
  图2链接服务器3.3请求客户
  在ReqSock.cpp文件中,根据响应状态m_reqStatus的不同,对接收到的数据包进行不同的响应处理。当browser发送第一个数据包时,响应状态m_reqStatus被设置为REQ_REQUEST,之后,对REQ_REQUEST数据包的每一行进行处理,根据http的协议使用ProcessLine方法对m_pRequest进行初始化,完成以上操作之后,调用判断StartResponse方法来构造应答报文,运行AsyncSelect函数,之后其调用void CRequestSocket::OnSend方法将缓存m_buf的应答报文发送给客户端,如图3所示。