标签:
参看: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 side
import socket
# Address
HOST = ‘‘
PORT = 8000
reply = ‘Yes‘
# Configure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
# passively wait, 3: maximum number of connections in the queue
s.listen(3)
# accept and establish connection
conn, addr = s.accept()
# receive message
request = conn.recv(1024)
print ‘request is: ‘,request
print ‘Connected by‘, addr
# send message
conn.sendall(reply)
# close connection
conn.close()
然后用另一台电脑作为客户,我们主动使用connect()方法来搜索服务器端的IP地址和端口,以便客户可以找到服务器,并建立连接:
# Written by Vamei
# Client side
import socket
# Address
HOST = ‘127.0.0.1‘
PORT = 8000
request = ‘can you hear me?‘
# configure socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
# send message
s.sendall(request)
# receive message
reply = s.recv(1024)
print ‘reply is: ‘,reply
# close connection
s.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 Vamei
import socket
# Address
HOST = ‘‘
PORT = 8000
# Prepare HTTP response
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"/>
</html>
‘‘‘
# Read picture, put into HTTP format
f = open(‘test.jpg‘,‘rb‘)
pic_content = ‘‘‘
HTTP/1.x 200 OK
Content-Type: image/jpg
‘‘‘
pic_content = pic_content + f.read()
f.close()
# Configure socket
s = socket.socket(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.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
# 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(‘\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 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()
这里只不过是使用类来封装了操作。
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 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()
这里,已经完成了静态http服务器的出现,免去了客户端请求的处理代码。这里的程序不能处理POST请求。我会在后面使用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 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()
修改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 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>"
这样,就能根据URL索引到具体的静态页面或者CGI脚本文件,基本实现了HTTP服务器。
综上,这一起都是基于socket来手动完成的,即使是后面使用上层库,处理请求还是只能简单的返回数据。在此之上,如果既能自动返回静态文件和运行CGI文件,又能插手到URL的处理中,让URL不再单纯的是文件索引地址,而是可处理的就好了。在“二,使用框架完成http_python服务器”中丰富这个demo。
标签:
原文地址:http://blog.csdn.net/r784709355/article/details/51452098