预备知识:

关于http协议的基础请参考这里

关于socket基础函数请参考这里

关于python网络编程基础请参考这里


一、python socket 实现的简单http服务器


废话不多说,前面实现过使用linux c 或者python 充当客户端来获取http 响应,也利用muduo库实现过一个简易http服务器,现在来实现一个python版

的简易http服务器,代码改编自http://www.cnblogs.com/vamei/ 


httpServer.py

 Python Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python
#coding=utf-8

import socket
import re

HOST =  ''
PORT =  8000

#Read index.html, put into HTTP response data
index_content =  '''
HTTP/ 1.x  200 ok
Content-Type: text/html

'''

file =  open( 'index.html''r')
index_content +=  file. read()
file. close()

#Read reg.html, put into HTTP response data
reg_content =  '''
HTTP/ 1.x  200 ok
Content-Type: text/html

'''

file =  open( 'reg.html''r')
reg_content +=  file. read()
file. close()

#Read picture, put into HTTP response data
file =  open( 'T-mac.jpg''rb')
pic_content =  '''
HTTP/ 1.x  200 ok
Content-Type: image/jpg

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



#Configure socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((HOST, PORT))
sock.listen( 100)

#infinite loop
while  True:
     # maximum number of requests waiting
    conn, addr = sock.accept()
    request = conn.recv( 1024)
    method = request. split( ' ')[ 0]
    src  = request. split( ' ')[ 1]

     print  'Connect by: ', addr
     print  'Request is:\n', request

     #deal wiht GET method
     if method ==  'GET':
         if src ==  '/index.html':
            content = index_content
         elif src ==  '/T-mac.jpg':
            content = pic_content
         elif src ==  '/reg.html':
            content = reg_content
         elif re.match( '^/\?.*$', src):
            entry = src. split( '?')[ 1]       # main content of the request
            content =  'HTTP/1.x 200 ok\r\nContent-Type: text/html\r\n\r\n'
            content += entry
            content +=  '<br /><font color="green" size="7">register successs!</p>'
         else:
             continue

    
     #deal with POST method
     elif method ==  'POST':
        form = request. split( '\r\n')
        entry = form[- 1]       # main content of the request
        content =  'HTTP/1.x 200 ok\r\nContent-Type: text/html\r\n\r\n'
        content += entry
        content +=  '<br /><font color="green" size="7">register successs!</p>'
    
     ######
     # More operations, such as put the form into database
     # ...
     ######
    
     else:
         continue

    conn.sendall(content)
    
     #close connection
    conn. close()

chmod +x httpServer.py,  并运行./httpServer.py

使用浏览器当做客户端访问服务器

在httpServer.py 所在目录有index.html, reg.html, T-mac.jpg 


1、访问目录:   http://192.168.56.188:8000/index.html

服务器输出:

Connect by:  ('192.168.56.1', 6274)
Request is:
GET /index.html HTTP/1.1
Host: 192.168.56.188:8000
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) 
Chrome/33.0.1750.146 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4


回顾代码可知我们给客户端的响应是头部+index.html, index.html如下:

 HTML Code 
1
2
3
4
5
6
7
8
9
10
<html>
     <head>
     <title>Jinan University </title>                                                                                                          
         </head>

     <body>
         <p>Python HTTP Server </p>
         <img  src= "T-mac.jpg"  />
     </body>                         
</html> 

进而进一步访问T-mac.jpg,由于我们在实现服务器时使用短连接,即响应一次就关掉连接,所以客户端会再发起一次连接,如下:

Connect by:  ('192.168.56.1', 6275)
Request is:
GET /T-mac.jpg HTTP/1.1
Host: 192.168.56.188:8000
Connection: keep-alive
Accept: image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) 
Chrome/33.0.1750.146 Safari/537.36
Referer: http://192.168.56.188:8000/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4


同样地,服务器响应头部+图片的二进制数据,如下图所示:


当然你也可以直接访问 http://192.168.56.188:8000/T-mac.jpg


2、访问目录:http://192.168.56.188:8000/reg.html

服务器输出:

Connect by:  ('192.168.56.1', 6282)
Request is:
GET /reg.html HTTP/1.1
Host: 192.168.56.188:8000
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) 
Chrome/33.0.1750.146 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4


同样地,我们把头部+reg.html 响应过去,reg.html 是注册表单如下:

 HTML Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE  HTML  PUBLIC  "-//W3C//DTD HTML 4.01//EN"  "http://www.w3.org/TR/html4/strict.dtd" >
<html>
     <head>
         <meta  http-equiv= "Content-Type"  content= "text/html; charset=GBK" >
         <!--meta http-equiv="refresh" content="3;url=http://www.sina.com.cn" /-->
         <title>register page </title>
     </head>
     <body>
        
         <form  action= "http://192.168.56.188:8000"  method= "post" >
             <table  border= "1" bordercolor= "#0000ff"  cellpadding= 10  cellspacing= 0  width= 600 >
                 <tr>
                     <th  colspan= "2" >注册表单 </th>
                 </tr>               
                 <tr>
                     <td>用户名称: </td>
                     <td><input  type= "text"  name= "user"  /></td>
                 </tr>               
                 <tr>
                     <td>输入密码: </td>
                     <td><input  type= "password"  name= "psw"  /></td>
                 </tr>               
                 <tr>
                     <td>确认密码: </td>
                     <td><input  type= "password"  name= "repsw"  /></td>
                 </tr>               
                 <tr>
                     <td>选择性别: </td>
                     <td>
                         <input  type= "radio"  name= "sex"  value= "nan"  />男 
                         <input  type= "radio"  name= "sex"  value= "nv"  />
                     </td>
                 </tr>               
                 <tr>
                     <td>选择技术: </td>
                     <td>
                         <input  type= "checkbox"  name= "tech"  value= "java"  />JAVA
                         <input  type= "checkbox"  name= "tech"  value= "html"  />HTML
                         <input  type= "checkbox"  name= "tech"  value= "css"  />CSS
                     </td>
                 </tr>               
                 <tr>
                     <td>选择国家: </td>
                     <td>
                         <select  name= "country" >
                             <option  value= "none" >--选择国家-- </option>
                             <option  value= "usa" >--美国-- </option>
                             <option  value= "en" >--英国-- </option>
                             <option  value= "cn" >--中国-- </option>
                         </select>
                     </td>
                 </tr>               
                 <tr>
                     <th  colspan= "2" >
                         <input  type= "reset"  value= "清除数据"  />
                         <input  type= "submit"  value= "提交数据"  />
                     </th>
                 </tr>               
             </table>
            
         </form>
        
        
     </body>
</html> 

我们随便填一些信息上去然后点击提交数据,如下图:


此时浏览器会访问 http://192.168.56.188:8000/

服务器输出为:

Connect by:  ('192.168.56.1', 6578)
Request is:
POST /  HTTP/1.1
Host: 192.168.56.188:8000
Connection: keep-alive
Content-Length: 59
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://192.168.56.188:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) 
Chrome/33.0.1750.146 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://192.168.56.188:8000/reg.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4

user=simba&psw=1990&repsw=1990&sex=nan&tech=java&country=cn


注意:即表单中的name=value,以&分隔。


回顾代码,我们只是将浏览器提交的数据体直接发回去,再输出register success!  浏览器输出如下图:



如果我们把 表单中的  <form action="http://192.168.56.188:8000" method="post">    method 改成get,会是怎样的呢?


此时浏览器会访问  http://192.168.56.188:8000/?user=simba&psw=1990&repsw=1990&sex=nan&tech=java&country=cn


服务器输出为:

Connect by:  ('192.168.56.1', 6382)
Request is:
GET /?user=simba&psw=1990&repsw=1990&sex=nan&tech=java&country=cn HTTP/1.1
Host: 192.168.56.188:8000
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) 
Chrome/33.0.1750.146 Safari/537.36
Referer: http://192.168.56.188:8000/reg.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4


因为我们应答回去的数据跟post一致,故浏览器看到的输出也是一样的。


在这里可以总结一下post 跟 get 提交的一些区别:

get提交,提交的信息都显示在地址栏中;对于敏感数据不安全;由于地址栏存储体积有限而不能提交大容量数据;将信息封装到了请求消息的请求行

中,而post 提交将信息封装到了请求体中。



二、CGIHTTPServer:使用静态文件或者CGI来回应请求




 

先看看什么是CGI (Common Gateway Interface)。CGI是服务器和应用脚本之间的一套接口标准。它的功能是当客户端访问cgi脚本文件时让服务

器程序运行此脚本程序,将程序的输出作为response发送给客户。总体的效果,是允许服务器动态的生成回复内容,而不必局限于静态文件。

支持CGI的服务器程序接收到客户的请求,根据请求中的URL,运行对应的脚本文件。服务器会将HTTP请求的信息和socket信息传递给脚本文件,并等

脚本的输出。脚本的输出封装成合法的HTTP回复,发送给客户。CGI可以充分发挥服务器的可编程性,让服务器变得“更聪明”。

服务器和CGI脚本之间的通信要符合CGI标准。CGI的实现方式有很多,比如说使用Apache服务器与Perl写的CGI脚本,或者Python服务器与shell写

的CGI脚本。

 

为了使用CGI,我们需要使用BaseHTTPServer包中的HTTPServer类来构建服务器。Python服务器的改动很简单。

CGIHTTPServer

复制代码
# 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()
复制代码

 

CGIHTTPRequestHandler默认当前目录下的cgi-binht-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>"
复制代码

(post.py需要有执行权限)

第一行说明了脚本所使用的语言,即Python。 cgi包用于提取请求中包含的表格信息。脚本只负责将所有的结果输出到标准输出(使用print)。

CGIHTTPRequestHandler会收集这些输出,封装成HTTP回复,传送给客户端。

对于POST方法的请求,它的URL需要指向一个CGI脚本(也就是在cgi-bin或者ht-bin中的文件)。CGIHTTPRequestHandler继承自

SimpleHTTPRequestHandler,所以也可以处理GET方法和HEAD方法的请求。此时,如果URL指向CGI脚本时,服务器将脚本的运行结果传送到客户

端;当此时URL指向静态文件时,服务器将文件的内容传送到客户端。


进一步,我可以让CGI脚本执行数据库操作,比如将接收到的数据放入到数据库中,以及更丰富的程序操作。相关内容从略。


参考:

http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm

http://www.cnblogs.com/vamei/archive/2012/10/30/2744955.html

http://www.cnblogs.com/vamei/archive/2012/10/31/2747885.html

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐