一,http_python服务器

来源:互联网 发布:小学古诗大全软件 编辑:程序博客网 时间:2024/06/03 09:26

参看:http://www.cnblogs.com/vamei,部分整合修改
代码皆复制教程内代码,保留作者署名。

当在浏览器中输入类似127.0.0.1:8000的url时,其实就是浏览器进程在和127.0.0.1服务器上的8000端口在通信。
socket是进程间通信的一种方法,socket有许多种类型,比如基于TCP协议或者UDP协议(两种网络传输协议)。
在互联网上,我们可以让某台计算机作为服务器。服务器开放自己的端口,被动等待其他计算机连接,当其他计算机作为客户,主动使用socket连接到服务器的时候,服务器就开始为客户提供服务。
HTTP协议基于TCP协议,但增加了更多的规范。这些规范,虽然限制了TCP协议的功能,但大大提高了信息封装和提取的方便程度。

一,简单进程TCP通信
在Python中,我们使用标准库中的socket包来进行底层的socket编程。

首先是服务器端,我们使用bind()方法来赋予socket以固定的地址和端口,并使用listen()方法来被动的监听该端口。当有客户尝试用connect()方法连接的时候,服务器使用accept()接受连接,从而建立一个连接的socket:

# Written by Vamei# Server sideimport socket# AddressHOST = ''PORT = 8000reply = 'Yes'# Configure sockets      = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.bind((HOST, PORT))# passively wait, 3: maximum number of connections in the queues.listen(3)# accept and establish connectionconn, addr = s.accept()# receive messagerequest    = conn.recv(1024)print 'request is: ',requestprint 'Connected by', addr# send messageconn.sendall(reply)# close connectionconn.close()

然后用另一台电脑作为客户,我们主动使用connect()方法来搜索服务器端的IP地址和端口,以便客户可以找到服务器,并建立连接:

# Written by Vamei# Client sideimport socket# AddressHOST = '127.0.0.1'PORT = 8000request = 'can you hear me?'# configure sockets       = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect((HOST, PORT))# send messages.sendall(request)# receive messagereply   = s.recv(1024)print 'reply is: ',reply# close connections.close()

这样,先运行服务器程序,再运行客户端,就完成了一次进程间通信,这里每次通信建立的socket可以单纯创建出一个线程处理。

二,基于TCP socket的HTTP服务器
socket传输自由度太高,从而带来很多安全和兼容的问题。我们往往利用一些应用层的协议(比如HTTP协议)来规定socket使用规则,以及所传输信息的格式。
HTTP协议利用请求-回应(request-response)的方式来使用TCP socket。客户端向服务器发一段文本作为request,服务器端在接收到request之后,向客户端发送一段文本作为response。在完成了这样一次request-response交易之后,TCP socket被废弃。下次的request将建立新的socket。request和response本质上说是两个文本,只是HTTP协议对这两个文本都有一定的格式要求。

我们写出一个HTTP服务器端:

# Written by Vameiimport socket# AddressHOST = ''PORT = 8000# Prepare HTTP responsetext_content = '''HTTP/1.x 200 OK  Content-Type: text/html<head><title>WOW</title></head><html><p>Wow, Python Server</p><IMG src="test.jpg"/></html>'''# Read picture, put into HTTP formatf = open('test.jpg','rb')pic_content = '''HTTP/1.x 200 OK  Content-Type: image/jpg'''pic_content = pic_content + f.read()f.close()# Configure sockets    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.bind((HOST, PORT))# infinite loop, server foreverwhile True:    # 3: maximum number of requests waiting    s.listen(3)    conn, addr = s.accept()    request    = conn.recv(1024)    method    = request.split(' ')[0]    src            = request.split(' ')[1]    # deal with GET method    if method == 'GET':        # ULR            if src == '/test.jpg':            content = pic_content        else: content = text_content        print 'Connected by', addr        print 'Request is:', request        conn.sendall(content)    # close connection    conn.close()

客户端使用浏览器,访问127.0.0.1:8000,可以看到浏览器发送了两次请求,创建了2个socket,第一次是文本,第二次是图片。另外可以将while循环中的内容改为多进程或者多线程工作。

三,支持POST的基于TCP socket的HTTP服务器
修改服务器代码:

# Written by Vamei# A messy HTTP server based on TCP socket import socket# AddressHOST = ''PORT = 8000text_content = '''HTTP/1.x 200 OK  Content-Type: text/html<head><title>WOW</title></head><html><p>Wow, Python Server</p><IMG src="test.jpg"/><form name="input" action="/" method="post">First name:<input type="text" name="firstname"><br><input type="submit" value="Submit"></form> </html>'''f = open('test.jpg','rb')pic_content = '''HTTP/1.x 200 OK  Content-Type: image/jpg'''pic_content = pic_content + f.read()# Configure sockets    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.bind((HOST, PORT))# Serve foreverwhile True:    s.listen(3)    conn, addr = s.accept()                        request    = conn.recv(1024)         # 1024 is the receiving buffer size    method     = request.split(' ')[0]    src        = request.split(' ')[1]    print 'Connected by', addr    print 'Request is:', request    # if GET method request    if method == 'GET':        # if ULR is /test.jpg        if src == '/test.jpg':            content = pic_content        else: content = text_content        # send message        conn.sendall(content)    # if POST method request    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]        conn.sendall(text_content + '\n <p>' + value + '</p>')        ######        # More operations, such as put the form into database        # ...        ######    # close connection    conn.close()

这样,访问第一次得到页面,在页面内点击submit,将发起一次请求,这次是POST数据,然后处理,返回数据

四,使用SimpleHTTPServer包完成静态文件回应请求的http服务器
在使用SimpleHTTPServer之前,先使用SocketServer来架设服务器:
省略响应数据声明,

# This class defines response to each requestclass 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 serverserver = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)# Start the server, and work foreverserver.serve_forever()

这里只不过是使用类来封装了操作。

SimpleHTTPServer可以用于处理GET方法和HEAD方法的请求。它读取request中的URL地址,找到对应的静态文件,分析文件类型,用HTTP协议将文件发送给客户。
创建index.html:

<head><title>WOW</title></head><html><p>Wow, Python Server</p><IMG src="test.jpg"/><form name="input" action="/" method="post">First name:<input type="text" name="firstname"><br><input type="submit" value="Submit"></form></html>

单独存储text.jpg文件。
改写Python服务器程序:

# Written by Vamei# Simple HTTPsERVERimport SocketServerimport SimpleHTTPServerHOST = ''PORT = 8000# Create the server, SimpleHTTPRequestHander is pre-defined handler in SimpleHTTPServer packageserver = SocketServer.TCPServer((HOST, PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)# Start the serverserver.serve_forever()

这里,已经完成了静态http服务器的出现,免去了客户端请求的处理代码。这里的程序不能处理POST请求。通常,当浏览器请求某个 HTML 文件时,服务器会返回此文件,但是假如此文件含有服务器端的脚本,那么在此 HTML 文件作为纯 HTML 被返回浏览器之前,首先会执行 HTML 文件中的脚本。后面使用CGI来弥补这个缺陷。值得注意的是,Python服务器程序变得非常简单。将内容存放于静态文件,并根据URL为客户端提供内容,这让内容和服务器逻辑分离。
每次更新内容时,我可以只修改静态文件,而不用停止整个Python服务器。这些改进也付出代价。在原始程序中,request中的URL只具有指导意义,可以规定任意的操作。在SimpleHTTPServer中,操作与URL的指向密切相关。用自由度,换来了更加简洁的程序。

五,CGIHTTPServer:使用静态文件或者CGI来回应请求
CGIHTTPServer包中的CGIHTTPRequestHandler类继承自SimpleHTTPRequestHandler类,所以可以用来代替上面的例子,来提供静态文件的服务。此外,CGIHTTPRequestHandler类还可以用来运行CGI脚本。
CGI是服务器和应用脚本之间的一套接口标准。它的功能是让服务器程序运行脚本程序,将程序的输出作为response发送给客户。总体的效果,是允许服务器动态的生成回复内容,而不必局限于静态文件。

改写服务器代码:

# Written by Vamei# A messy HTTP server based on TCP socket import BaseHTTPServerimport CGIHTTPServerHOST = ''PORT = 8000# Create the server, CGIHTTPRequestHandler is pre-defined handlerserver = BaseHTTPServer.HTTPServer((HOST, PORT), CGIHTTPServer.CGIHTTPRequestHandler)# Start the serverserver.serve_forever()

修改index.html:

<head><title>WOW</title></head><html><p>Wow, Python Server</p><IMG src="test.jpg"/><form name="input" action="cgi-bin/post.py" method="post">First name:<input type="text" name="firstname"><br><input type="submit" value="Submit"></form></html>

创建一个cgi-bin的文件夹,并在cgi-bin中放入如下post.py文件,也就是我们的CGI脚本:

#!/usr/bin/env python# Written by Vameiimport cgiform = cgi.FieldStorage()# Output to stdout, CGIHttpServer will take this as response to the clientprint "Content-Type: text/html"     # HTML is followingprint                               # blank line, end of headersprint "<p>Hello world!</p>"         # Start of contentprint "<p>" +  repr(form['firstname']) + "</p>"

这样,就能根据URL索引到具体的静态页面或者CGI脚本文件,基本实现了HTTP服务器。即前面说的“通常,当浏览器请求某个 HTML 文件时,服务器会返回此文件,但是假如此文件含有服务器端的脚本,那么在此 HTML 文件作为纯 HTML 被返回浏览器之前,首先会执行 HTML 文件中的脚本。-w3cschool”

综上,这一起都是基于socket来手动完成的,即使是后面使用上层库,处理请求还是只能简单的返回数据。在此之上,如果既能自动返回静态文件和运行CGI文件,又能插手到URL的处理中,让URL不再单纯的是文件索引地址,而是可处理的就好了。在“二,使用框架完成http_python服务器”中丰富这个demo。

0 0