Python学习笔记20:server先进
我们不依赖于一个框架,CGI如果是,只能使用socket介面。他完成了一个可以处理HTTP要求Pythonserver。
基于,不管是什么的计算机的操作系统(推荐Linux)和Python该计算机可被用作HTTPserver采用。要设置你的网站。
改写上一篇文章中的程序。并引入更高级的Python包,以写出更成熟的Pythonserver。
一 支持POST
我们首先改写原文中的HTTPserver,从而让该server支持更加丰富的HTTP请求。
相对于原程序,这里增添了表格以及相应”POST”方法的操作。假设你已经读过用socket写一个Pythonserver,会发现这里仅仅是添加非常少的一点内容。
原始程序:
# A messy HTTP server based on TCP socket import socket # Address
HOST = ''
PORT = 8000 text_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 socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT)) # Serve forever
while 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('rn')
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()
执行上面Pythonserver,向上一篇文章那样,使用一个浏览器作为client。
我们看到了新增的表格以及提交(submit)button。在表格中输入aa并提交表格,我们的Pythonserver给出上面的结果。
二 使用SocketServer
我们首先使用SocketServer包来简化我们架设server的过程。
在上面使用socket的过程中。我们先设置了socket的类型,然后依次调用bind(),listen(),accept(),并使用while循环来让server不断的接受请求。
上面的这些步骤能够通过SocketServer包来简化。
SocketServer: # use TCPServer import SocketServer HOST = ''
PORT = 8000 text_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() # 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('rn')
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 socketserver。并同一时候设置IP地址和port。
然后使用server_forever()方法来让服务器不断工作(就像原始程序中的while循环一样)。
我们传递给TCPServer一个MyTCPHandler类。用对socket作出操作。
注意。MyTCPHandler继承自BaseRequestHandler,我们通过改写handler()方法来个性化我们的操作。
在handler()中,能够通过self.request来引用socket (正如我们在handler()中对socket进行recv()和sendall()操作),
还能够使用self.address来引用socket的client地址。
三 SimpleHTTPServer: 使用静态文件来回应请求
在经过了SocketServer的改造之后。我们的handler(),也就是对请求进行处理的部分,依旧是乱糟糟的一团。
这对于大型server来说可能是个问题。
为什么呢? 对于一个HTTP请求(request)来说。它的起始行包括两个重要信息:请求方法和URL。
之前,我们都用if结构来区分不同的请求方法和URL。并针对不同的情况来进行不同的操作:
请求方法(request method) URL 操作
GET / 发送text_content
GET /text.jpg 发送pic_content
POST / 分析request主体中包括的value(实际上是我们填入表格的内容); 发送text_content和value
依据请求方法和URL的不同,一个大型的HTTPserver可能须要应付成千上万种不同的请求。
假设针对每一个请求都在程序中写出不同的操作的话,须要大量的时间和精力,同一时候为运营和维护带来非常大的困难。
我们须要有更标准化。也更简便的方式来处理这些请求。
在Python中。我们能够使用SimpleHTTPServer包和CGIHTTPServer包来减小以上的负担。
当中,SimpleHTTPServer能够用于处理GET方法和HEAD方法的请求。
它读取request中的URL地址,并在当前文件夹中找到相应的静态文件。并将文件的内容发送给client。
相应于我们的情况,就是将text_content放置在index.html中,而不用读取text.jpg文件。
当一个HTTP请求到来时,其URL指向某个文件,SimpleHTTPServer会读取这个文件,并分析文件类型,自己主动生成response,回复client。
假设URL指向某个目录,SimpleHTTPServer会读取该目录下的index.html或者index.hml文件。
首先,我们在当前文件夹下生成例如以下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>
然后,改写我们的Pythonserver程序。
实际上。我们仅仅是更换了TCPServer的Handler:使用SimpleHTTPServer包中唯一的类SimpleHTTPRequestHandler,而不是我们之前自定义的MyTCPHandler
SimpleHTTPServer: # Simple HTTPsERVER 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()
注意。我们这里的程序还不能等效于之前的程序。由于不能处理POST请求。
我们会在后面使用CGI来弥补这个缺陷。
但要点是,我们的Pythonserver程序已经变得很easy。
我们将内容存放于静态文件,并依据URL指向的静态文件为client提供内容,从而让内容和Pythonserver相分离。
这种话。我们每次更新内容的时候就能够仅仅改动静态文件,而不用停止整个Pythonserver。
我们也应该注意到使用这些改进付出的代价。
比方说。对于原始程序来说。request中的URL仅仅具有指导意义,我们能够随意规定对应的操作。
而在 SimpleHTTPServer的改进中,response固化成为:读取URL相应文件并将其内容呈现给客户。这大大限制了我们的自由度。
即使在后面我们使用CGI增大了自由度。但相对于原始程序,我们依旧是添加了自己的限制。
有时候,程序的便捷与程序的自由度相抵触。程序猿须要在两者之间取舍。
对于一个小的项目来说,我们能够尾随已经 制定的标准(比方这里的SimpleHTTPServer,或者使用一个框架),使用这些新的标准能够让开发变得非常便捷。
然而对于一个大型的项目来说。我们往往须要争取回自己的自由度,修订成为项目须要的标准。
四 CGIHTTPServer:使用静态文件或者CGI来回应请求
CGIHTTPServer包中的CGIHTTPRequestHandler类继承自SimpleHTTPRequestHandler类。所以能够用来取代上面的样例,来提供静态文件的服务。
此外,CGIHTTPRequestHandler类还能够用来执行CGI脚本。
首先,我们先看看什么是CGI (Common Gateway Interface)。CGI是server和应用脚本之间的一套接口标准。目的是让server程序执行脚本程序,将程序的输出作为response发送给客户。
通常来说,支持CGI的server程在接收到客户的request之后,依据request中的URL。执行相应的脚本文件。
server会将HTTP request信息以及socket信息输入给脚本文件,也负责收集脚本的输出,并组装成为合法的HTTP response。
利用CGI,我们能够充分发挥server的可编程性,动态的生成response。而不必局限于静态文件。
server和CGI脚本之间通过CGI标准作为接口。
这样就能够让server与不同语言写的CGI脚本相配合,比方说使用Apacheserver与Perl写的CGI脚本,或者Pythonserver与shell写的CGI脚本。
到这里为止,我们都在使用TCPServer来构建server。
为了使用CGI,我们须要使用BaseHTTPServer包中的HTTPServer类来构建server。
事实上HTTPServer是TCPServer的子类,其用法也与TCPServer同样。它仅仅是添加了server_name和server_port两个属性。
但不凑巧的是。我们的CGIHTTPRequestHandler须要调用这两个属性…
Pythonserver的修改非常easy。
CGIHTTPServer: # A messy HTTP server based on TCP socket 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()
CGIHTTPRequestHandler默认当前文件夹下的cgi-bin和ht-bin文件夹中的文件为CGI脚本,而存放于其它地方的文件被觉得是静态文件。
因此。我们须要改动一下index.html。将当中form元素指向的action改为cgi-bin/post.py。
<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 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>"
第一行必需要有,以便告诉Pythonserver,脚本所使用的语言 (我们这里的CGI是Python。当然也能够是别的语言,比方bash)。
cgi包用于提取request中提交的表格信息(我们临时不深入cgi包)。脚本仅仅负责将全部的结果输出到标准输出(使用print)。
而CGIHTTPRequestHandler会收集这些输出。并组装成为response传送给client。
假设一个请求是POST方法,那么它的URL必须指向一个CGI脚本(也就是在cgi-bin或者ht-bin中的文件)。
CGIHTTPRequestHandler继承自SimpleHTTPRequestHandler,所以也能够处理GET方法和HEAD方法的请求。
此时。假设URL指向CGI脚本时。server将脚本的执行结果传送到client;当此时URL指向静态文件时,server将文件的内容传送到client。
我们能够让CGI脚本运行数据库操作。比方将接收到的数据放入到数据库中,以及更丰富的程序操作。
CGI脚本提供了LAMP架构中PHP任务 (我们的Pythonserver等价物LAMP中间Apache)。
版权声明:本文博主原创文章,博客,未经同意不得转载。
Python学习笔记20:server先进的更多相关文章
- Python 学习笔记20 自定义robot Framework 关键字
Robot Framework 自定义关键字 Robot framework 自定义了一些关键字我们可以把他们当作函数在设计测试用例的时候使用. 同时RF也提供了许多第三方的库,我们可以自己下载使用. ...
- python学习笔记20(字符串格式化)
Python中内置有对字符串进行格式化的操作% 模板 格式化字符串时,Python使用一个字符串作为模板.模板中有格式符,这些格式符为真实值预留位置,并说明真实数值应该呈现的格式.Python用一个t ...
- Python学习笔记(20)-文件和文件夹的移动、复制、删除、重命名
一,概述 python中对文件和文件夹进行移动.复制.删除.重命名,主要依赖os模块和shutil模块,要死记硬背这两个模块的方法还是比较困难的,可以用一个例子集中演示文件的移动.复制.删除.重命名, ...
- Ext.Net学习笔记20:Ext.Net FormPanel 复杂用法
Ext.Net学习笔记20:Ext.Net FormPanel 复杂用法 在上一篇笔记中我们介绍了Ext.Net的简单用法,并创建了一个简单的登录表单.今天我们将看一下如何更好是使用FormPanel ...
- python学习笔记--Django入门0 安装dangjo
经过这几天的折腾,经历了Django的各种报错,翻译的内容虽然不错,但是与实际的版本有差别,会出现各种奇葩的错误.现在终于找到了解决方法:查看英文原版内容:http://djangobook.com/ ...
- OpenCV之Python学习笔记
OpenCV之Python学习笔记 直都在用Python+OpenCV做一些算法的原型.本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段.现在看 到一本国外的新书< ...
- 【Python学习笔记之二】浅谈Python的yield用法
在上篇[Python学习笔记之一]Python关键字及其总结中我提到了yield,本篇文章我将会重点说明yield的用法 在介绍yield前有必要先说明下Python中的迭代器(iterator)和生 ...
- Python学习笔记(十四)
Python学习笔记(十四): Json and Pickle模块 shelve模块 1. Json and Pickle模块 之前我们学习过用eval内置方法可以将一个字符串转成python对象,不 ...
- Python学习笔记(十一)
Python学习笔记(十一): 生成器,迭代器回顾 模块 作业-计算器 1. 生成器,迭代器回顾 1. 列表生成式:[x for x in range(10)] 2. 生成器 (generator o ...
随机推荐
- BZOJ 3112 Zjoi2013 防守战线 单纯形
题目大意: 单纯形*2.. . #include <cmath> #include <cstdio> #include <cstring> #include < ...
- (hdu 简单题 128道)平方和与立方和(求一个区间的立方和和平方和)
题目: 平方和与立方和 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total ...
- ZOJ 3635 Cinema in Akiba(线段树)
Cinema in Akiba (CIA) is a small but very popular cinema in Akihabara. Every night the cinema is ful ...
- hdu1712(分组背包)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1712 分析: 典型的分组背包问题,如果不会的可以看一下背包九讲. 看下背包九讲中的描述: for 所有 ...
- ftk学习记(waitbox篇)
[声明:版权全部.欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 前面说到了脚本.那么就看看ftk中demo与script搭配的效果是什么样的? 上面的效果图就相 ...
- SVNKIT的SVNCommitClient的doMkDir的操作
package com.repositoryclient.svnoptions; import java.io.File; import org.tmatesoft.svn.core.SVNCommi ...
- svn rm --keep-local ./QueryParser_kill.logs
svn rm --keep-local ./QueryParser_kill.logs
- Spark技术内幕:Stage划分及提交源代码分析
当触发一个RDD的action后.以count为例,调用关系例如以下: org.apache.spark.rdd.RDD#count org.apache.spark.SparkContext#run ...
- 文件下载-SpringMVC中測试
直接改动文件路径就能够.其它都不须要改动,帮助类已经为大家写好,可直接使用 1.Scroller: /** * 下载文件 * @author liupeng * @param request * @p ...
- TCP/IP-协议族----17、应用层简单
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaGVrZXdhbmd6aQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...