用Python socket實現一個簡單的http服務器(post 與get 的區別)、CGIHTTPServer 簡單應用

預備知識:

關於http協議的基礎請參考這裏。 關於socket基礎函數請參考這裏。 關於python網絡編程基礎請參考這裏

一、python socket 實現的簡單http服務器

廢話不多說,前面實現過使用linux c 或者python 充當客戶端來獲取http 響應,也利用muduo庫實現過一個簡易http服務器,現在來實現一個python版

的簡易http服務器,代碼改編自http://www.cnblogs.com/vamei/ 

httpServer.py

#!/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>
    <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 是註冊表單如下:

<!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-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>"

(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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章