要寫一個類似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
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
|
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協議規范處理
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
|
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
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/jiashengmei/p/6346946.html