Python實現簡單的HttpServer伺服器示例

NO IMAGE

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

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

角色A向角色B請求資料,這時可以把A視為客戶端,B視為服務端。客戶端的主要職責是傳送請求和接收服務端根據自己傳送的請求返回的請求資訊,而服務端的主要職責是接收請求和返回請求資料。

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

我們常說B/S,C/S架構,所謂的B/S指browser/server,C/S指Client/Server,B/S架構其實就是應用於瀏覽器的程式,只要最後在瀏覽器上展現的都是 B/S架構,而非在瀏覽器上展現的都是C/S架構,如常見的英雄聯盟遊戲。但是本質上只有C/S架構,因為瀏覽器是一種特殊的客戶端。

瀏覽器的特殊之處是有一下三大引擎:

DOM解析引擎:即瀏覽器可以解析HTML
樣式解析引擎:即瀏覽器可以解析CSS
指令碼解析引擎:即瀏覽器可以解析JAVASCRIPT

3. Socket

上面提到的客戶端服務端,他們之間是怎樣實現連線及資料傳遞的,這就是Socket,每一門程式語言都有Socket程式設計,Socket的作用就是提供了網路通訊的能力

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規定了編碼格式。不止以上這些,非常多,關於這些引數含義這裡就不去一一介紹

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地址

弄清楚以上五點之後開始來寫程式碼

webServer.py


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協議規範處理


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_httpServer