Python實現簡單的HttpServer

要寫一個相似tomcat的簡易服務器,首先需弄清楚這幾點:

1. 客戶端(Client)和服務端(Server)的角色及做用

角色A向角色B請求數據,這時能夠把A視爲客戶端,B視爲服務端。客戶端的主要職責是發送請求和接收服務端根據本身發送的請求返回的請求信息,而服務端的主要職責是接收請求和返回請求數據。javascript

2. 瀏覽器是什麼及工做原理

咱們常說B/S,C/S架構,所謂的B/S指browser/server,C/S指Client/Server,B/S架構其實就是應用於瀏覽器的程序,只要最後在瀏覽器上展示的都是 B/S架構,而非在瀏覽器上展示的都是C/S架構,如常見的英雄聯盟遊戲。可是本質上只有C/S架構,由於瀏覽器是一種特殊的客戶端。
瀏覽器的特殊之處是有一下三大引擎:
DOM解析引擎:即瀏覽器能夠解析HTML
樣式解析引擎:即瀏覽器能夠解析CSS
腳本解析引擎:即瀏覽器能夠解析JAVASCRIPTcss

3. Socket

上面提到的客戶端服務端,他們之間是怎樣實現鏈接及數據傳遞的,這就是Socket,每一門編程語言都有Socket編程,Socket的做用就是提供了網絡通訊的能力html

4. HTTP協議及HTTP與TCP/TP的區別

客戶端和服務端經過Socket實現了網絡通訊的能力,能夠實現數據傳遞。而協議是規範數據傳輸,也就是說客戶端和服務端之間傳輸數據要按照必定得規範和標準傳輸,不能瞎傳。
TCP/IP(Transmission Control Protocol/Internet Protocol):傳輸控制協議/網間協議
HTTP(HyperText Transfer Protocol):超文本傳輸協議。
TCP/TP的區別:
作一個形象的比喻,TCP/TP是馬路,HTTP則是馬路上的汽車,因此HTTP必定是在TCP/TP的基礎上的。
HTTP主要是應用在web程序上,設計之初就是爲了提供一種發佈和接收HTML頁面的方法,這樣說可能很抽象很難理解。具體的說當咱們去訪問一個網站時,只須要拿到基於這個網站的內容(好比html,css,JavaScript)。但咱們抓取瀏覽器接收到的資源包(能夠用Fiddler工具)發現除了網頁須要的實體內容還有一些下面信息:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 24 Jan 2017 03:25:23 GMT
Connection: close
Content-Length: 661
這就是http協議規範,好比Content-Type就是說傳輸的時候文件的格式,Content-Encoding規定了編碼格式。不止以上這些,很是多,關於這些參數含義這裏就不去一一介紹java

5. URL的含義

URL(統一資源定位符),就是咱們常說的網址,直接來解析一個URL來講明他
http://198.2.17.25:8080/webapp/index.html
這個含義是找到IP爲198.2.17.25的服務器下目錄爲webapp的index.html
可是咱們常看到的是這樣的URL:
http://goodcandle.cnblogs.com/archive/2005/12/10/294652.aspx
其實這個和上面的同樣,不過這裏存在一個域名解析,能夠將goodcandle.cnblogs.com解析成對應的IP地址python

弄清楚以上五點以後開始來寫代碼

webServer.pygit

import socket
import sys
import getFileContent
#聲明一個將要綁定的IP和端口,這裏是用本地地址
server_address = ('localhost', 8080)
class WebServer():
    def run(self):
        print >>sys.stderr, 'starting up on %s port %s' % server_address
        #實例化一個Socket
        sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        #綁定IP和端口
        sock.bind(server_address)
        #設置監聽
        sock.listen(1)
        #這裏首先給個死循環,其實這裏是須要多線程的,再後續版本將會實現
        while True:
            #接受客戶端的請求並獲得請求信息和請求的端口信息
            connection, client_address = sock.accept()
            print >>sys.stderr, 'waiting for a connection'
            try:
                #獲取請求信息
                data = connection.recv(1024)
                if data:
                    #發送請求信息
                    connection.sendall(getFileContent.getHtmlFile(data))
            finally:
                connection.close()

if __name__ == '__main__':
    server=WebServer()
    server.run()

webServer.py很清晰簡潔,connection.sendall()服務端返回信息給瀏覽器,可是發送的數據必須遵循HTTP協議規範
getFileContent.py是對發送的數據進行HTTP協議規範處理github

import sys
import os

#獲得要發送的數據信息
def getHtmlFile(data):
    msgSendtoClient=""
    requestType=data[0:data.find("/")].rstrip()
    #判斷是GET請求仍是POST請求
    if requestType=="GET":
        msgSendtoClient=responseGetRequest(data,msgSendtoClient)
    if requestType=="POST":
        msgSendtoClient=responsePostRequest(data,msgSendtoClient)
    return msgSendtoClient

#打開文件,這裏不直接寫,二是去取要發送的文件再寫
def getFile(msgSendtoClient,file):
        for line in file:
          msgSendtoClient+=line
        return msgSendtoClient

#篩選出請求的一個方法
def getMidStr(data,startStr,endStr):
    startIndex = data.index(startStr)
    if startIndex>=0:
        startIndex += len(startStr)
        endIndex = data.index(endStr)
        return data[startIndex:endIndex]

#獲取要發送數據的大小,根據HTTP協議規範,要提早指定發送的實體內容的大小
def getFileSize(fileobject):
    fileobject.seek(0,2)
    size = fileobject.tell()
    return size

#設置編碼格式和文件類型
def setParaAndContext(msgSendtoClient,type,file,openFileType):
    msgSendtoClient+="Content-Type: "+type+";charset=utf-8"
    msgSendtoClient+="Content-Length: "+str(getFileSize(open(file,"r")))+"\n"+"\n"
    htmlFile=open(file,openFileType)
    msgSendtoClient=getFile(msgSendtoClient,htmlFile)
    return msgSendtoClient

#GET請求的返回數據
def responseGetRequest(data,msgSendtoClient):
    return responseRequest(getMidStr(data,'GET /','HTTP/1.1'),msgSendtoClient)

#POST請求的返回數據
def responsePostRequest(data,msgSendtoClient):
    return responseRequest(getMidStr(data,'POST /','HTTP/1.1'),msgSendtoClient)

#請求返回數據
def responseRequest(getRequestPath,msgSendtoClient):
    headFile=open("head.txt","r")
    msgSendtoClient=getFile(msgSendtoClient,headFile)
    if getRequestPath==" ":
        msgSendtoClient=setParaAndContext(msgSendtoClient,"text/html","index.html","r")
    else:
        rootPath=getRequestPath
        if os.path.exists(rootPath) and os.path.isfile(rootPath):
            if ".html" in rootPath:
                msgSendtoClient=setParaAndContext(msgSendtoClient,"text/html",rootPath,"r")
            if ".css" in rootPath:
                msgSendtoClient=setParaAndContext(msgSendtoClient,"text/css",rootPath,"r")
            if ".js" in rootPath:
                msgSendtoClient=setParaAndContext(msgSendtoClient,"application/x-javascript",rootPath,"r")
            if ".gif" in rootPath:
                msgSendtoClient=setParaAndContext(msgSendtoClient,"image/gif",rootPath,"rb")
            if ".doc" in rootPath:
                msgSendtoClient=setParaAndContext(msgSendtoClient,"application/msword",rootPath,"rb")
            if ".mp4" in rootPath:
                msgSendtoClient=setParaAndContext(msgSendtoClient,"video/mpeg4",rootPath,"rb")
        else:
            msgSendtoClient=setParaAndContext(msgSendtoClient,"application/x-javascript","file.js","r")
    return msgSendtoClient

Github源碼下載:https://github.com/Jiashengp/Python_httpServerweb

相關文章
相關標籤/搜索