任振兴
摘要:HTTP(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。该文所给的程序一个简单的基于HTTP协议的服务器程序,实现所有基于HTTP协议的服务器程序的核心部分。该文对所给的具有图形界面HTTP服务器程序源代码进行分析和调试,介绍程序实现的主要功能,运行过程以及程序中各类实现的功能。
关键词:HTTP;HTTP协议;服务器
中图分类号:TP393文献标识码:A文章编号:1009-3044(2012)02-0282-03
1 HTTP协议介绍
1.1 HTTP协议
HTTP(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。本程序实现的是一个轻量级的web服务器。【1】
1.2 HTTP组成
HTTP请求由三部分组成,分别是:请求行、消息报头、请求正文。
1)请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF。其中Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。
2)HTTP响应由三个部分组成,分别是:状态行、消息报头、响应正文。
状态行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF,其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种
可能取值:【2】
1xx:指示信息--表示请求已接收,继续处理。
2xx:成功--表示请求已被成功接收、理解、接受。
3xx:重定向--要完成请求必须进行更进一步的操作。
4xx:客户端错误--请求有语法错误或请求无法实现。
5xx:服务器端错误--服务器未能实现合法的请求。
常见状态代码、状态描述、说明:
200 OK//客户端请求成功
400 Bad Request//客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden//服务器收到请求,但是拒绝提供服务
404 Not Found//请求资源不存在,eg:输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable//服务器当前不能处理客户端的请求,一段时间后,可能恢复正常
HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行(对于请求消息,开始行就是请求行,对于响应消息,开始行就是状态行),消息报头(可选),空行(只有CRLF的行),消息正文(可选)组成。
3)请求正文。
2 HTTPSVR程序功能
2.1工作原理
建立在请求/响应模式(Request/Response)上:一个客户端与服务器建立连接后,客户端向Web服务器发出一个HTTP请求行;Web服务器在收到有效的请求后,返回一个状态行或多个响应标题、一个空白行和相关文档。HTTP协议使用的端口号,通常为80。
2.2工作流程图
HTTPSVR的信息交换过程,它分四个过程:建立连接、发送请求信息、获取服务器响应状态、关闭连接。其中较为复杂的过程是:发送请求信息、获取服务器响应状态。【3】如图1所示:
图1工作流程图
1)建立连接:连接的建立是通过申请套接字(Socket)实现的。客户打开一个套接字并把它约束在一个端口上,如果成功,就相当于建立了一个虚拟文件。以后就可以在该虚拟文件上写数据并通过网络向外传送。
2)发送请求:打开一个连接后,客户机把请求消息送到服务器的停留端口上,完成提出请求动作。
3)发送响应:服务器在处理完客户的请求之后,要向客户机发送响应消息。
4)关闭连接:客户和服务器双方都可以通过关闭套接字来结束TCP/IP对话。
3 HTTPSVR程序分析
基于HTTPSVR所用到的类进行分析:
1)CGenPage类(genpage.h/.cpp):这是通用设置的属性对话框,是UI的一部分,设置属性中的一般属性,其中包括:服务器视图界面的列表显示、访问日志、图标显示三个属性。
2)CHitDoc类(http.h/.cpp):这是http响应类,处理客户端请求的URL地址,并且进行本地地址转化,指向服务器资源目录。
3)CHitDoc(httpDoc.h/.cpp):这是mfc的doc视图主类。
4)CHttpSvrApp(httpsvr.h/.cpp):这是处理web服务器的初始化,包括访问目录、登录日志、HTTP端口的设置、文件的保存等。
5)CHttpSvrView(httpview.h/.cpp):这是UI中的日志列表控件,主要是客户登录服务器的访问的记录和错误记录的显示,包括文件的路径,用户登录时间,用户访问次数等。
6)CListenSocket(listen.h/.cpp):这是监听套接字的拓展类,用于创建服务器在端口(默认80端口)上的监听套接字。
7)CNamePage(namepage.h/.cpp):这是一般属性中的Server Name属性页,是UI的一部分,其中包括服务器名称的设置和端口的设置,而服务器名称有默认和指定的两种情况。
8)CNoRootDlg(NoRoot.h/.cpp):这是服务器地址属性设置对话框,是UI的一部分。在此部分中,如果URL不存在则会弹出此对话框,让我们输入正确的URL地址。
9)CRequestSocket(reqsock.h/.cpp):这是服务器的主要功能实现部分,包括接受、发送数据,并且支持cgi的动态生成页面,详细的功能有:接收请求,对http请求报文进行解析,根据请求内容构造响应报文。
10)CRequest(Request.h/.cpp):实现保存上一次web请求的内容。
11)CRootPage(RootPage.h/.cpp):这是一般属性中Root Dir对话框,是UI的一部分。在此部分我们可以对服务器的资源根目录进行设置,同时也可以重置为默认的根目录G:WebPages。
12)CasyncSocket(AsyncSock.h /.cpp):这是异步非阻塞类,它的Create()函数,除了创建了一个SOCKET以外,使用WSAAsyncSe? lect()将这个SOCKET与该窗口对象关联,以让该窗口对象处理来自Socket的事件(消息),然而CSocketWnd收到Socket事件之后,只是简单地回调CAsyncSocket::OnReceive() CAsyncSocket::OnSend(),CAsyncSocket::OnAccept(),CAsyncSocket::OnConnect()等虚函数。所以CAsyncSocket的派生类,只需要在这些虚函数里添加发送和接收的代码。
使用CAsyncSocket时,如果使用Create缺省创建socket,则所有网络I/O都是异步操作,进行有关网络数据传输时需要用到以下函数:OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend。【4】
4 HTTPSRV服务器程序分析流程
4.1执行函数
服务器程序分析首先在MFC中的文件APPMODULE.CPP的_tWinMain函数处开始执行,执行该文件的return AfxWinMain函数。
4.2客户端链接服务器
在服务器程序中由AfxWinMain函数负责建立工作线程pWinThread,此线程对HttpSvr进行初始化工作,接着调用HttpSvr.cpp文件中的BOOL CHttpSvrApp::InitInstance方法对WEB服务器进行初始化,然后运行线程的主函数,最后在THRDCORE.CPP文件中运行int CWinThread::Run函数,开始HTTP服务器的循环。在循环中,首先通过CListenSocket::OnAccept( int nErrorCode )函数生成CRe? questSocket类,将其设置用于监听8080端口。监听到连接请求时,Accept函数创建新的套接字pRequest并返回句柄,AsyncSelect函数监听8080端口的FD_READ和FD_CLOSE两个事件,当传入FD_READ事件时,准备接收,并且触发OnReceive()函数,如果传入FD_WRITE事件,发送数据的时候,OnSend()函数就会触发。设置端口为8080,将web服务文件夹地址Root Dir指向root所在地址。此时显示结果如图2所示。
图2链接服务器
4.3请求客户
一旦有数据到达时,执行ReqSock.cpp中的void CRequestSocket::OnReceive(int nErrorCode)函数。程序代码将传输控制层上传的数据包存放在请求和应答报文的缓冲区m_buf中。
接下来在ReqSock.cpp文件中,根据响应状态m_reqStatus的不同,使用swich语句对接收到的数据包进行不同的响应处理。当浏览窗口发送第一个数据包时,响应状态m_reqStatus被设置为REQ_REQUEST,之后,对请求数据包的每一行进行处理,根据http的协议使用ProcessLine方法对m_pRequest进行初始化,完成以上操作之后,当请求状态m_reqStatus == REQ_DONE,调用判断StartRe? sponse方法来构造应答报文,运行AsyncSelect( FD_WRITE | FD_CLOSE )函数,之后其调用void CRequestSocket::OnSend(int nError? Code)方法将缓存m_buf的应答报文发送给客户端。【5】
5结论
该文通过对基于HTTP协议服务器程序以及浏览器与服务器的交互过程的分析,熟悉了HTTP协议的服务工作流程,对运用Winsock编程来逐步解析HTTP协议的服务器程序的核心部分有更深层次的了解。在本程序中是通过多线程与异步操作的理论的方法来实现多个客户端同时访问的处理,同时熟悉了多线程的创建过程以及异步操作理论,了解了各个线程间是如何协作的。
不足的是,本实例程序实现的只是一个轻量级的服务器,它的分析过程相对比较简单。但是只要熟悉了基于HTTP协议服务器创建的基本原理,将来就能在此基础上实现功能更加强大的服务器。
参考文献:
[1]叶强.超文本传输协议-HTTP/1.0[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协议的传输机制与超文本链的研究[J].微电子学与计算机,1997(4):53-55.
[4]李腊元,李春林.计算机网络技术[M].北京:国防工业出版社, 2004.
[5] Andrew,S.Tanenbaum.计算机网络[M].潘安民译.北京:清华大学出版社,2004(8).