2015年1月

Python网络编程五:WSGI接口与网关协议

以前写PHP的时候,对于网关协议的理解只有CGI:外部应用程序(CGI程序)与Web服务器之间的接口标准,而对于python,wsgi是python的专属服务器网关接口。
引用校友“松鼠奥利奥”的一段看法:

"我对 CGI 的理解是利用程序的标准输入输出流,完成 HTTP 通信。HTTP 是文本协议,每次请求的文本以标准输入流的形式进入服务器端 CGI 程序,创建进程;然后进程的标准输出流作为响应 。
WSGI 和 CGI 原理极其相似,但是是完全不同的实现。WSGI 是 Python 专用的协议,也是输入&输出的方式传输文本流,但不是创建进程,而是对一个 WSGI 程序(callable 的对象,可以是函数也可以是实现了 __call__ 的对象),将 request 作为参数传入(不再是纯文本,而是经过包装),同样将经过包装的 response 作为响应返回。request/response 的包装由 Python 标准库提供。
二者都是标准,都有诸多实现。CGI 最为广泛,无论是二进制程序、perl 脚本、python 脚本还是 PHP 都可以以这样的原理提供 HTTP 服务;WSGI 在 Python 界也是公共标准,主流 Web 框架基本都有实现,例如 Bottle 的 bottle.Bottle (Application 对象)就实现了 WSGI 协议(见 __call__ 方法)。"

WSGI区分为两个部份:一为“服务器”或“网关”,另一为“应用程序”或“应用框架”。在处理一个WSGI请求时,服务器会为应用程序提供环境资讯及一个回呼函数(Callback Function)。当应用程序完成处理请求后,透过前述的回呼函数,将结果回传给服务器。
所谓的 WSGI 中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。

我们通常使用的web框架如:flask django 就是一个web 应用程序,借助wsgi接口,完成与web服务器(nginx)的协调联系。
理解了这一点,自己开发web框架有有路可寻,一个链接:web框架开发
当然,在python里,还有一些服务器网关接口,如FastCGI之类,可以google一下。

理解了Wsgi,开发web将会有一个大致的理解。
上代码:

#coding:utf-8

from wsgiref.simple_server import make_server

# web app
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield "Hello world!\n"

# web server
httpd = make_server('', 8000, app)
print "Serving HTTP on port 8000..."
# 开始监听HTTP请求:
httpd.serve_forever()

Python网络编程四:HTTP服务器

前面我介绍了一些服务器的基本知识,这一节我们要来了解学习web开发要了解的Web服务器。
比较好的web服务器像NGINX,能处理静态文件,多并发,反向代理,是一个高效的软件,用c语言开发的。
然而,我们在这里介绍的只是一个简单的Http服务器。
科普http:

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是互联网上应用最为广泛的一种网络协议。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识。

我们做服务器这边,所以要注意返回信息的输出格式:

HTTP/1.1 200 OK
Content-Length: 3059
Server: GWS/2.0
Date: Sat, 11 Jan 2003 02:44:04 GMT
Content-Type: text/html
Cache-control: private
Set-Cookie: PREF=ID=73d4aef52e57bae9:TM=1042253044:LM=1042253044:S=SMCc_HRPCQiqy
X9j; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Connection: keep-alive

以下,呈上两段学习时候阅读到的代码,一个来自Vamei,一个来自the5fire,两位的博客都是很好的学习资源。
我们先来看看简单基于socket的代码:

# Written by Vamei

import socket

# Address
HOST = ''
PORT = 8000

# Prepare HTTP response
text_content = '''HTTP/1.x 200 OK
Content-Type: text/html

WOW

Wow, Python Server


'''

# Read picture, put into HTTP format
f = open('test.png','rb')
pic_content = '''
HTTP/1.x 200 OK
Content-Type: image/png

'''
pic_content = pic_content + f.read()
f.close()

# Configure socket
s = socket.ocket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))

# infinite loop, server forever
while 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.png':
content = pic_content
else: content = text_content

print 'Connected by', addr
print 'Request is:', request
conn.sendall(content)
# close connection
conn.close()

服务器端的输出:

Connected by ('127.0.0.1', 2727)
Request is: GET /test.png HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Cache-Control: max-age=0
Accept: image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
Referer: http://127.0.0.1:8000/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.6,fr;q=0.4,ja;q=0.2


Connected by ('127.0.0.1', 2728)
Request is: GET /favicon.ico HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.6,fr;q=0.4,ja;q=0.2

以上代码应该不难理解,关键在于,Request的不同请求会得到不同输出,一个是文本,一个是图片。

好,接下来,我们用python的库,BaseHTTPServer,看名字就知道干嘛的了。
上代码:

#coding:utf-8
__author__ = 'the5fire'
from os import path

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler

class Handler(BaseHTTPRequestHandler):

def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write(self.render('index'))
self.wfile.write('\n')
return

def render(self, name='index'):
file_name = '%s.html' % name
if path.isfile(file_name):
html = open(file_name, 'r').read()
return html
return None


if __name__ == '__main__':
server = HTTPServer(('localhost', 8181), Handler)
print 'Development server is running at http://127.0.0.1:8181/'
print 'Starting server, use to stop'
server.serve_forever()

我们只需要放上index.html就会在用户请求时,把index.html返回给客户端。