最终版的Web(Python实现)

时间:2024-04-16 10:08:22

天啦,要考试了,要期末考试了,今天把最终版的Python搭建Web代码先写这里记下了。详细的过程先不写了。

这次是在前面的基础上重写 HTTPServer 与 BaseHTTPRequestHandler,主要利用 python 提供

的 socket 进行编程,从而实现消息的接收与相应;然后再接着引入多线程,分别处理来自客户
端的请求;最后实现根据客户端传递的参数动态生成页面的功能。

主要步骤如下:

一、 .重写 HTTPServer 与 BaseHTTPRequestHandler
Python socket 编程流程(服务器端) :
1. 第一步是创建 socket 对象。调用 socket 构造函数。如:
socket = socket.socket( family, type )
family 参数代表地址家族, 可为 AF_INET 或 AF_UNIX。 AF_INET 家族包括 Internet
地址,AF_UNIX 家族用于同一台机器上的进程间通信。
type 参数代表套接字类型,可为 SOCK_STREAM(流套接字)和 SOCK_DGRAM(数
据报套接字)。
2. 第二步是将socket绑定到指定地址。 这是通过socket对象的bind方法来实现的:
socket.bind( address )
由 AF_INET 所创建的套接字,address 地址必须是一个双元素元组,格式是
(host,port)。host 代表主机,port 代表端口号。如果端口号正在使用、主机名不正
确或端口已被保留,bind 方法将引发 socket.error 异常。
3. 第三步是使用 socket 套接字的 listen 方法接收连接请求。
socket.listen( backlog )
backlog 指定最多允许多少个客户连接到服务器。 它的值至少为 1。 收到连接请求后,
这些请求需要排队,如果队列满,就拒绝请求。
4. 第四步是服务器套接字通过 socket 的 accept 方法等待客户请求一个连接。
connection, address = socket.accept()
调 用 accept 方法时,socket 会时入“waiting”状态。客户请求连接时,方法建立连
接并返回服务器。 accept 方法返回一个含有两个元素的 元组(connection,address)。
第一个元素 connection 是新的 socket 对象,服务器必须通过它与客户通信;第二
个元素 address 是客户的 Internet 地址。
5. 第五步是处理阶段, 服务器和客户端通过 send 和 recv 方法通信(传输 数据)。 服
务器调用 send,并采用字符串形式向客户发送信息。send 方法返回已发送的字符
个数。服务器使用 recv 方法从客户接收信息。调用 recv 时,服务器必须指定一个
整数,它对应于可通过本次方法调用来接收的最大数据量。recv 方法在接收数据时
会进入“blocked”状态,最后返回一个字符 串,用它表示收到的数据。如果发送的数
据量超过了 recv 所允许的,数据会被截短。多余的数据将缓冲于接收端。以后调用
recv 时,多余的数据会从缓冲区 删除(以及自上次调用 recv 以来,客户可能发送的
其它任何数据)。
6. 传输结束,服务器调用 socket 的 close 方法关闭连接。

整个代码块儿如下:

注释有点乱, 中英文结合。。

Test.py

# -*-coding:utf-8 -*-
import BaseHTTPServer
import os
import socket
import subprocess
import threading

from datetime import datetime

class ServerException( Exception ):
    pass

    #
    # 重写 HTTPServer 与 BaseHTTPRequestHandler,主要利用 python 提供
    # 的 socket 进行编程,从而实现消息的接收与相应;然后再接着引入多线程,分别处理来自客户
    # 端的请求;最后实现根据客户端传递的参数动态生成页面的功能。

# step1:reWrite HttpServer and BaseHTTPRequestHandler

class HttpServer:
    def __init__(self, serverAddr, RequestHandler):
        self.serverAddr = serverAddr
        self.requestHandler = RequestHandler

    def serve_forever(self):
        #    1. create socket object(对象), call socket create function
        server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 1family=AF_INET,type=SOCK_STREAM
        server_sock.bind(self.serverAddr)
        # 2. call socket bind, aim to bind socket to pointed address
        # address must be a two-elements yuanzu, style is (host, port)
        # host stands for port number. if port number is in using\
        #、host name is wrong or port number has been saved, bind way will
        # bring socket.error yi chang
        server_sock.listen(10)
        # 3.  using socket's listen way to receive connect request
        # listen parament backlog=10:point allow no more than 10 clients
        # can connect to server. its value at least is 1. when received
        # request, those requests need pai dui,if list full, then refuse request

        while True:
            print 'waiting for connection...'
            clientsock, addr = server_sock.accept()
            # 4. server socket wait for client to request a connection
            # when call accept() function, socket will come into "wating"
            # states. when client request connection,function create
            # connection and return server..
            # accept function return with two elements
            # (connection, address)., the first connection is new socket's
            # object, server must through it to tong xin with client.
            # the second element address is clients' Internet address
            print 'received from :', addr
            thread = threading.Thread(target=self.startThread, args=(clientsock, addr,))
            thread.setDaemon(True)
            thread.start()
            # handler = RequestHandler(clientsock, addr,self)

        server_sock.close()

    def startThread(self, clientsock, addr):
        handler = RequestHandler(clientsock, addr, self)

class HttpRequestHandler:
    bufsize = 1024

    def __init__(self, clientsock, addr):
        self.cliensock = clientsock
        self.client_address = addr
        self.date_time_string = datetime.now()
        self.analyze() # http head part analyze

    def analyze(self):
        # receive dates, bufsize points to read dates num one time
        """

        :type self: object

        """
        data = self.cliensock.recv(self.bufsize)
        # print'receive ------->%s\n%s' %(datetime.now(), data)

        # chrome sometimes will send two request consist, tne second is null, reason is unknow
        if data.repalce(" ", "") == "":
            print "data is null"
            return
        data = data.split('\r\n')
        # first line is "GET/something.html?a=1&b=2 HTTP/1.1
        firstLine = data[0]
        arr = firstLine.split(' ')
        self.command = arr[0]
        self.protocol = arr[2]
        if '?' in arr[1]:
            # path put absolute way
            self.path, self.paramStr = arr[1].spilt('?')
        else:
            self.path = arr[1]
            self.paramStr = None

            # put the remain of head information in heades's dictionary in the way of key value
            # Accept-Language : zh - cn
            # Connection : Keep - Alive
            # Host : Localhost
            # Accept - Encoding : gzip, deflate
            self.headers = {}
            for line in data[1:]:
                if ':' in line:
                    key, value = line.split(':', 1)
                    self.headers[key] = value

                    # call function to deal with, this function has come in the first
            self.do_GET()
            # time.sleep(30)
            # when this function receive data, first feng li data
            # char string as th rule of '\r\n'
            # then ti qu first line, that is "GET/something.html?a=1&b=2 HTTP/1.1
            # then analyze it , put it to the variable

    # path

    http_response = "HTTP/1.1"

    def send_response(self, status):
        if status == 200:
            self.http_response += "200" + " " + "OK"
        elif status == 404:
            self.http_response += "400" + " " + "Not Found"
        self.http_response += '\r\n'

    # "Content_Type", "text/html"
    # "Content-Length", Length

    def send_heade(self, key, value):
        self.http_response += str(key) + ": " + str(value) + '\r\n'

    def end_headers(self):
        self.http_response += '\r\n'

    def write(self, page):
        self.http_response += str(page)
        self.clientsock.send(self.http_response)
        self.cliensock.close()

        # those functions are pin zhuang http_response char string
        # as the rule of response head style

class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    """dealt with and return page"""
    # page model
    Page = '''
    <html>
    <body>
    <table border=2s>
   <tr> <td>Header</td> <td>Value</td> </tr>
<tr> <td>Date</td><td> 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>
    '''
    Error_Page = """\
    <html>
    <body>
    <h1>Error accessing {path}</h1>
    <p>{msg}</p>
    </body>
    </html>
    """

    def handle_error(self, msg):
        content = self.Error_Page.format( path=self.path, msg=msg )
        self.send_content( content, 404 )

    def handle_file(self, full_path):
        # 处理 python 脚本
        if full_path.endswith('.py'):
            # data 为脚本运行后的返回值
            data = subprocess.check_output(['python', full_path, self.paramStr] )
            self.send_content(data)
            return
        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)

    # deal with a request
    def do_GET(self):
        # type: () -> object
        try:
            full_path = os.getcwd() + self.path

            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)
                # 访问根路径
            elif os.path.isdir(full_path):
                # fullPath = os.path.join(fullPath, "index.html")
                full_path += "index.html"
                if os.path.isfile(full_path):
                    self.handle_file(full_path )
                else:
                    raise ServerException( "'{0}' not found".format( self.path ) )
            else:
                raise ServerException( "Unknown object '{0}'".format( self.path ) )
        except Exception as msg:
            self.handle_error(msg)

    @property
    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 page
        pass

    def send_content(self, content, status=200):
        self.send_response(status)

        self.send_header("Content-type", "text/html" )
        self.send_header("Content-Length", str( len( content ) ) )
        self.end_headers()
        # self.wfile.write(self.create_page)
        self.wfile.write(content)
        # self.write(page)
        pass

if __name__ == '__main__':
    serverAddr = ('localhost', 5555)
    server = HttpServer(serverAddr, RequestHandler )
    server.serve_forever()

 

calc.py

import sys

paramStr = sys.argv[1]
paramArr = paramStr.split('&')
paramDict = {}
for param in paramArr:
    key, value = param.split('=')
    paramDict[key] = value

print '''\
<html>
<body>
<p>a + b = {0}</p>
</body>
</html>'''.format((int(paramDict['a']) + int(paramDict['b'])))

  index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
   <h1>Index Page </h1>
   <p>I love Python, to be honest</p>

<table border=2s>
   <tr> <td>Header</td> <td>Value</td> </tr>
<tr> <td>Date</td><td> 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>

<p>please input first number:</p>
<input>
<br>
<p>please input second number:</p>
<input>

<button>add</button>
</body>
</html>

 运行效果如图:

最终版的Web(Python实现)

最终版的Web(Python实现)

好好准备期末考了。。。。