%# -*- coding: utf-8-unix -*- \section{TCP通信与Web服务器} \label{sec:c:socket:s:web} \subsection{实验目的} \label{subsec:c:socket:s:web_object} 熟悉基于Python进行TCP套接字编程的基础知识,理解HTTP报文格式, 能基于Python编写一个可以一次响应一个HTTP请求, 并返回静态文件的简单Web服务器。 \subsection{实验内容} \label{subsec:c:socket:s:web_content} 利用Python开发一个可以一次处理一个HTTP请求的Web服务器, 该服务器可以接受并解析HTTP请求, 然后从服务器的文件系统中读取被HTTP请求的文件, 并根据该文件是否存在而向客户端发送正确的响应消息, \subsection{实验原理、方法和手段} \label{subsec:c:socket:s:web_principle} 基于TCP的面向客户端/服务器在Python实现中的的工作流程是: \begin{enumerate} \item 首先在服务器端通过调用\texttt{socket()}创建套接字来启动一个服务器; \item 服务器调用\texttt{bind()}绑定指定服务器的套接字地址(IP地址+端口号); \item 服务器调用\texttt{listen()}做好侦听准备,同时规定好请求队列的长度; \item 服务器进入阻塞状态,等待客户的连接请求; \item 服务器通过\texttt{accept()}来接收连接请求,并获得客户的socket地址。 \item 在客户端通过调用\texttt{socket()}创建套接字; \item 客户端调用\texttt{connect()}和服务器建立连接。 \item 连接建立成功后,客户端和服务器之间通过调用\texttt{read()} 和\texttt{write()}来接收和发送数据。 \item 数据传输结束后,服务器和客户各自通过调用\texttt{close()}关闭套接字。 \end{enumerate} 注意在不同的计算机语言实现中,上述调用的名字和具体工作流程可能略有不同。 基于Python的TCP客户端/服务器具体工作流程如图\ref{fig:c:socket_tcp-flow}所示。 \begin{figure}[!ht] \centering \includegraphics[width=6cm]{tcp-flow} \caption{面向连接客户/服务器流程图} \label{fig:c:socket_tcp-flow} \end{figure} \subsection{实验条件} \label{subsec:c:socket:s:web_requirement} \begin{itemize} \item 装有python环境的电脑两台; \item 局域网环境; \item 部分代码(\nameref{subsec:c:socket:s:web_additional}中已给出); \item Python语言参考手册 -- TCP部分\footnote{可以参考Python3官方手册的 \href{https://docs.python.org/zh-cn/3/library/socket.html} {套接字}部分,也可以查询其他相关手册}; \item HTTP协议参考手册\footnote{可以参考Mozilla提供的 \href{https://developer.mozilla.org/zh-CN/docs/Web/HTTP/OverView} {HTTP概述},或者HTTP 1.1版的\href{https://tools.ietf.org/html/rfc2616}{RFC文档}, 也可以查询其他相关手册}。 \end{itemize} \subsection{实验步骤} \label{subsec:c:socket:s:web_procedure} 本实验\nameref{subsec:c:socket:s:web_additional}一节中展示了一份不完整的Web服务器框架代码, 学生需要逐步填充代码中不完善的部分,并完成一个具有以下功能的简单Web服务器: \begin{enumerate} \item 服务器收到请求时能创建一个TCP套接字; \item 可以通过这个TCP套接字接收HTTP请求; \item 解析HTTP请求并在操作系统中确定客户端所请求的特定文件; \item 从服务器的文件系统读取客户端请求的文件; \item 当被请求文件存在时,创建一个由被请求的文件组成的“请求成功”HTTP响应报文; \item 当被请求文件不存在时,创建“请求目标不存在”HTTP响应报文; \item 通过TCP连接将响应报文发回客户端; \end{enumerate} 在开发过程中,可以使用浏览器访问运行在同一台电脑上的服务器程序进行测试。 在完成代码调试后,可以尝试在不同主机上的使用浏览器发送请求测试服务器,分析并记录结果。 \subsection{进阶任务} \label{subsec:c:socket:s:web_rethink} 本实验中的Web服务器一次只能处理一个HTTP请求,请自行查阅线程知识, 修改代码,实现一个能够同时处理多个请求的多线程服务器。 \subsection{注意事项及有关说明} \label{subsec:c:socket:s:web_notice} 客户端发来的HTTP请求中,URL都是相对根“/”的相对路径, 因此需要将服务器文件系统中的某个地址映射为这个“根”。 为了简化工作,建议将服务器程序存放的路径作为这个根, 这样运行服务器程序时,程序会自动从当前运行的路径开始查询文件。 例如: 假设将“HelloWorld.html”这个html文件放置在服务器程序文件存放目录中, 服务器运行主机的IP地址为“\texttt{123.234.12.34}”, 6789为服务器监听的端口号。 则从URL:\url{http://123.234.12.34:6789/HelloWorld.html} 出发可以获取到“HelloWorld.html”这个文件。 \subsection{考核方法} \label{subsec:c:socket:s:web_criterion} 本实验需提交一份实验报告和编写的代码文件。报告内容应当包括以下三个部分: \begin{itemize} \item 代码的说明; \item 不同环境下代码运行的结果; \item 对结果的分析和总结体会。 \end{itemize} 本实验评分标准: \begin{enumerate} \item 规定时间内完成实验报告20分; \item 代码正确运行,20分(不能正常运行0分); \item 实验报告格式整洁,20分; \item 实验报告中详细记录了实验过程,在实验中所遇到的问题以及解决方法,20分; \item 实验报告中仔细分析了实验结果,并能提出自己的改进措施,20分。 \end{enumerate} \subsection{附件} \label{subsec:c:socket:s:web_additional} 基于Python的Web服务器框架程序: \begin{code}[python] #import socket module from socket import * # 准备服务器端socket(按需补充) serverSocket = socket(AF_INET, SOCK_STREAM) while True: # 建立连接 print 'Ready to serve...' connectionSocket, addr = # 按需补充 try: message = # 按需补充 filename = message.split()[1] f = open(filename[1:]) # 通过socket发送HTTP头部 outputdata = #将请求文件的内容发送到客户端(按需补充) for i in range(0, len(outputdata)): connectionSocket.send(outputdata[i]) connectionSocket.close() except IOError: # 发送未找到文件的响应消息(按需补充代码) # 关闭客户端socket(按需补充代码) serverSocket.close() \end{code}