#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
@author: homer
@see: ithomer.net
'''
# use TCPServer
import SocketServer
HOST = ''
PORT = 8800
text_content = '''
HTTP/1.x 200 OK
Content-Type: text/html
<head>
<title>hello ithomer</title>
</head>
<html>
<p>hello, Python Server</p>
<img src="/home/homer/1_sunboy_2050.jpg"/>
<form name="input" action="/" method="post">
First name:<input type="text" name="firstname"><br>
<input type="submit" value="Submit">
</form>
</html>
'''
f = open('/home/homer/1_sunboy_2050.jpg', 'rb')
pic_content = '''
HTTP/1.x 200 OK
Content-Type: image/jpg
'''
pic_content = pic_content + f.read()
# This class defines response to each request
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
# self.request is the TCP socket connected to the client
request = self.request.recv(1024)
print 'Connected by', self.client_address[0]
print 'Request is', request
method = request.split(' ')[0]
src = request.split(' ')[1]
if method == 'GET':
if src == '/test.jpg':
content = pic_content
else: content = text_content
self.request.sendall(content)
if method == 'POST':
form = request.split('\r\n')
idx = form.index('') # Find the empty line
entry = form[idx:] # Main content of the request
value = entry[-1].split('=')[-1]
self.request.sendall(text_content + '\n <p>' + value + '</p>')
######
# More operations, such as put the form into database
# ...
######
# Create the server
server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
# Start the server, and work forever
server.serve_forever()
我们建立了一个TCPServer对象来创建一个TCP socket服务器,并同时设置IP地址和端口。然后使用server_forever()方法来让服务器不断工作(就像原始程序中的while循环一样)。我们传递给TCPServer一个MyTCPHandler类,用对socket作出操作。注意,MyTCPHandler继承自BaseRequestHandler,我们通过改写handler()方法来个性化我们的操作。在handler()中,可以通过self.request来引用socket (正如我们在handler()中对socket进行recv()和sendall()操作),还可以使用self.address来引用socket的客户端地址。
3. SimpleHTTPServer: 使用静态文件来回应请求
在经过了SocketServer的改造之后,我们的handler(),也就是对请求进行处理的部分,依然是乱糟糟的一团。这对于大型服务器来说可能是个问题。
为什么呢? 对于一个HTTP请求(request)来说,它的起始行包含两个重要信息:请求方法和URL。之前,我们都用if结构来区分不同的请求方法和URL,并针对不同的情况来进行不同的操作:
请求方法(request method) URL 操作
GET / 发送text_content
GET /text.jpg 发送pic_content
POST / 分析request主体中包含的value(实际上是我们填入表格的内容); 发送text_content和value
根据请求方法和URL的不同,一个大型的HTTP服务器可能需要应付成千上万种不同的请求。如果针对每个请求都在程序中写出不同的操作的话,需要大量的时间和精力,同时为运营和维护带来很大的困难。我们需要有更标准化,也更简便的方式来处理这些请求。在Python中,我们可以使用SimpleHTTPServer包和CGIHTTPServer包来减小以上的负担。其中,SimpleHTTPServer可以用于处理GET方法和HEAD方法的请求。它读取request中的URL地址,并在当前目录中找到对应的静态文件,并将文件的内容发送给客户端。
对应于我们的情况,就是将text_content放置在index.html中,而不用读取text.jpg文件。当一个HTTP请求到来时,其URL指向某个文件,SimpleHTTPServer会读取这个文件,并分析文件类型,自动生成response,回复客户端。如果URL指向某个文件夹,SimpleHTTPServer会读取该文件夹下的index.html或者index.hml文件。
首先,我们在当前目录下生成如下index.html文件:
import SocketServer
import SimpleHTTPServer
HOST = ''
PORT = 8000
# Create the server, SimpleHTTPRequestHander is pre-defined handler in SimpleHTTPServer package
server = SocketServer.TCPServer((HOST, PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
# Start the server
server.serve_forever()
import BaseHTTPServer
import CGIHTTPServer
HOST = ''
PORT = 8000
# Create the server, CGIHTTPRequestHandler is pre-defined handler
server = BaseHTTPServer.HTTPServer((HOST, PORT), CGIHTTPServer.CGIHTTPRequestHandler)
# Start the server
server.serve_forever()
# Written by Vamei
import cgi
form = cgi.FieldStorage()
# Output to stdout, CGIHttpServer will take this as response to the client
print "Content-Type: text/html" # HTML is following
print # blank line, end of headers
print "<p>Hello world!</p>" # Start of content
print "<p>" + repr(form['firstname']) + "</p>"