一,http_python服务器

参看: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请求。通常,当浏览器请求某个 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 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服务器。即前面说的“通常,当浏览器请求某个 HTML 文件时,服务器会返回此文件,但是假如此文件含有服务器端的脚本,那么在此 HTML 文件作为纯 HTML 被返回浏览器之前,首先会执行 HTML 文件中的脚本。-w3cschool”

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

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值