用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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据库新发现

思科CEO钱伯斯:我非常尊敬华为

--------------------------------------------------------------------------------...

32110
来自专栏计算机编程

2018-03-14 致敬霍金

  在黑洞领域,没有人知道这个怪兽是个什么东西,自从霍金给出“霍金辐射”与“奇点定理”过后,人们终于逐渐揭开了这宇宙中最为神秘的天体。在这里再次缅怀一下虽然坐着...

15320
来自专栏黄成甲

怎样成为解决问题的高手(连载一)

什么是问题?一言以蔽之,问题来源于现实与目标的差距。因此,问题产生的原因可能是不清楚目标是什么;还可能是不知道差距产生的原因是什么;或者虽然知道差距产生的原因,...

35340
来自专栏進无尽的文章

什么才是优秀的网站用户界面设计

16720
来自专栏黄成甲

怎样科学地和人相处?

按照约定俗成的说法,一般跟人打交道有两个规则,一个是“黄金法则”,一个是“白银法则”。黄金法则并不适合现代社会对陌生人使用。因为你觉得好的东西别人不一定觉得好。...

16020
来自专栏一个会写诗的程序员的博客

服务网格 Pattern: Service Mesh

自从几十年前首次引入以来,我们了解到分布式系统能够实现我们之前甚至无法思考的用例,但它们也会引入各种新问题。

14520
来自专栏数据库新发现

第十三届搞笑诺贝尔奖(IgNobel)新鲜出炉

第十三届搞笑诺贝尔奖(IgNobel)新鲜出炉 中广网 10月04日 09:48

19230
来自专栏Leetcode名企之路

【Leetcode】59. 螺旋矩阵 II

给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

16030
来自专栏進无尽的文章

益思维-早期苹果员工胸牌背面写的11条“成功法则”

早期苹果员工胸牌背面写的11条“成功法则”这个胸牌的背面写着11条成功法则,其中每一条文字都充满了正能量。从中我们可以看到一些触动人心的感觉。

9820
来自专栏知道一点点

20种新颖的按钮风格和效果【附源码】

Codrops 给我们分享了一组新鲜的按钮样式和效果的集合。它们中的大部分效果都使用了 CSS3 过渡和伪元素,他们都有一个共同点,那就是都具有简单性,没有太多...

16910

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励