手记

Python实现简单的Web服务器 Part1

这篇文章想写得是一个简单的web服务器。

首先可能需要了解一点网络编程,web服务器,简单的说,运行在一台机器上的一个进程(通常不在本地),浏览器也是运行在机器上的一个进程(本地的)。也就是说,他的本质其实就是一个跨机器的进程通信。

然后,实现了跨机器的通信之后,服务器和浏览器通信的数据格式,其实就是http协议。也就是说,浏览器和服务器必须按照http协议规定的格式发送数据,要不然的话,对方就听不懂了。

关于网络编程的知识,你可以参考:
http://www.jianshu.com/p/8f1941c4a549

关于http协议的知识,你可以参考:
超简洁的实例 ——关于HTTP协议分析 http://www.jianshu.com/p/f5a5db039737

关于实现一个web服务器

初步的功能是这样,运行服务器之后,在局域网内的设备可以访问这个服务器。比如,我在服务器下面放了一个class.html 文件,这是我的课表。当我用手机访问这个网页的时候,在手机的浏览器中显示这个页面。如下图:(随便写得一个html文件,虽然丑,但是模拟一下这个功能就好)


局域网内手机访问.png

pc访问.png

这篇文章只写了一个静态的web服务器,下一篇文章写了 模拟实现cgi,支持脚本语言:
Python实现简单的Web服务器 Part2
http://www.jianshu.com/p/d28395655bc0

python 下有一个库BaseHTTPServer,这个库封装了一个常用的处理http协议的请求和响应的函数,如果用这个库,基本上就不需要考虑网络层的实现了,只需要了解http协议就好。

但是也可以从网络层写起,利用socket编程。这样你需要考虑的一个很重要的问题就是:服务器处理并发,是多线程还是多进程,还是异步模型,或者是多进程+多线程。现在感觉网络编程真的是博大精深。

这样一个最简单的服务器就写好了,但是他不能对浏览器请求的页面做出响应,对于所有的请求,服务器回复 “to-do”

import BaseHTTPServer#RequestHandler 繼承 BaseHTTPRequestHandler class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    '''处理请求并返回页面'''
    # 页面模板
    Page = "to do "
    #do_GET的函數的名字 是不能改的
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-Type", "text/html")
        self.send_header("Content-Length", str(len(self.Page)))
        self.end_headers()
        self.wfile.write(self.Page)if __name__ == '__main__':
    serverAddress = ('', 8080)
    server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
    server.serve_forever()

demo2 依然不可以响应不同的html文件请求,但是可以利用基类的数据成员来输出http请求的时间,端口,请求的html文件。

#-*- coding:utf-8 -*-import BaseHTTPServer#RequestHandler 繼承 BaseHTTPRequestHandler ,所以他自身就有一個path的數據成員class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    #Page="to doing  wakakak "
    Page='''
    <html>
    <body>
    <table>
    <tr>  <td>Header</td>         <td>Value</td>          </tr>
    <tr>  <td>Date and time</td>  <td>{date_time}</td>    </tr>
    <tr>  <td>Client host</td>    <td>{client_host}</td>  </tr>
    <tr>  <td>Client port</td>    <td>{client_port}</td> </tr>
    <tr>  <td>Command</td>        <td>{command}</td>      </tr>
    <tr>  <td>Path</td>           <td>{path}</td>         </tr>
    </table>
    </body>
    </html>
    '''
    #
    def do_GET(self):        #self.send_response(200)
        #self.send_header('Content-Type','text/html')
        #self.send_header('Content-Length',str(len(self.Page)))
        #self.end_headers()
        #self.wfile.write(self.Page)
        page=self.create_page()        self.send_content(page)    def send_content(self,page):        self.send_response(200)        self.send_header('Content-Type','text/html')        self.send_header('Content-Length','text.html')        self.end_headers()        self.wfile.write(page)    #self.date_time_string 和 client_address[0/1]等等都是父類的書據成員
    def create_page(self):
        values={            'date_time':self.date_time_string(),            'client_host':self.client_address[0],            'client_port':self.client_address[1],            'command':self.command,            'path':self.path
        }
        page=self.Page.format(**values)        return pageif __name__ == '__main__':
    serverAddress = ('', 8080)
    server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
    server.serve_forever()

运行结果:


image.png

对于html 请求的响应,其实就是拿去这个url请求后面的路径,然后在服务器的目录下面去找这个文件,如果有,就返回给浏览器。如果没有,就返回404状态码。

下面是demo3:

#coding:utf-8import sys,os,BaseHTTPServerclass ServerException(Exception):
    pass#RequestHandler 繼承 BaseHTTPRequestHandler ,所以他自身就有一個path的數據成員class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    Error_Page="""\
    <html>
    <body>
    <h1>Error accessing {path}</h1>
    <p>{msg}</p>
    </body>
    </html>
    """

    def do_GET(self):
        try:            print 'get'
            full_path=os.getcwd()+self.path            print full_path            print os.getcwd()            if not os.path.exists(full_path):                raise ServerException("'{ 0 }'  not found ",format(self.path))            elif os.path.isfile(full_path):
                self.handle_file(full_path)            else:                raise ServerException("unkown object '{ 0 }'",format(self.path))        except Exception as msg:
            self.handle_error(msg)    def handle_file(self,full_path):
        try:            with open(full_path,'rb') as reader:
                content=reader.read()
            self.send_content(content)        except IOError as msg:
            msg="'{0}' cannot be read :{1} ".format(self.path,msg)
            self.handle_error(msg)    def handle_error(self,msg):
        content=self.Error_Page.format(path=self.path,msg=msg)
        self.send_content(content,404)    def send_content(self,page,status=200):
        #print 'send_content function '
        self.send_response(status)
        self.send_header('Content-Type','text/html')
        self.send_header('Content-Length','text.html')
        self.end_headers()
        self.wfile.write(page)if __name__ == '__main__':
    serverAddress = ('', 8081)
    server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
    server.serve_forever()

运行结果就是第一张图那法样,实现了一个简单的静态服务器。
git链接:https://github.com/zhaozhengcoder/Python

下一步,试着去模拟实现一些cgi
//to-do 吃饭去了 好饿
后续可以参考:
Python实现简单的Web服务器 Part2 http://www.jianshu.com/p/d28395655bc0


作者:sexycoder
链接:https://www.jianshu.com/p/2fc93de02b94


0人推荐
随时随地看视频
慕课网APP