這篇文章想寫得是一個(gè)簡(jiǎn)單的web服務(wù)器掏婶。
首先可能需要了解一點(diǎn)網(wǎng)絡(luò)編程崔列,web服務(wù)器让禀,簡(jiǎn)單的說(shuō),運(yùn)行在一臺(tái)機(jī)器上的一個(gè)進(jìn)程(通常不在本地)庙洼,瀏覽器也是運(yùn)行在機(jī)器上的一個(gè)進(jìn)程(本地的)顿痪。也就是說(shuō)镊辕,他的本質(zhì)其實(shí)就是一個(gè)跨機(jī)器的進(jìn)程通信。
然后蚁袭,實(shí)現(xiàn)了跨機(jī)器的通信之后征懈,服務(wù)器和瀏覽器通信的數(shù)據(jù)格式,其實(shí)就是http協(xié)議揩悄。也就是說(shuō)卖哎,瀏覽器和服務(wù)器必須按照http協(xié)議規(guī)定的格式發(fā)送數(shù)據(jù),要不然的話删性,對(duì)方就聽不懂了亏娜。
關(guān)于網(wǎng)絡(luò)編程的知識(shí),你可以參考:
http://www.reibang.com/p/8f1941c4a549
關(guān)于http協(xié)議的知識(shí)蹬挺,你可以參考:
超簡(jiǎn)潔的實(shí)例 ——關(guān)于HTTP協(xié)議分析 http://www.reibang.com/p/f5a5db039737
關(guān)于實(shí)現(xiàn)一個(gè)web服務(wù)器
初步的功能是這樣维贺,運(yùn)行服務(wù)器之后,在局域網(wǎng)內(nèi)的設(shè)備可以訪問(wèn)這個(gè)服務(wù)器巴帮。比如溯泣,我在服務(wù)器下面放了一個(gè)class.html 文件,這是我的課表晰韵。當(dāng)我用手機(jī)訪問(wèn)這個(gè)網(wǎng)頁(yè)的時(shí)候,在手機(jī)的瀏覽器中顯示這個(gè)頁(yè)面熟妓。如下圖:(隨便寫得一個(gè)html文件雪猪,雖然丑,但是模擬一下這個(gè)功能就好)
這篇文章只寫了一個(gè)靜態(tài)的web服務(wù)器起愈,下一篇文章寫了 模擬實(shí)現(xiàn)cgi只恨,支持腳本語(yǔ)言:
Python實(shí)現(xiàn)簡(jiǎn)單的Web服務(wù)器 Part2
http://www.reibang.com/p/d28395655bc0
python 下有一個(gè)庫(kù)BaseHTTPServer,這個(gè)庫(kù)封裝了一個(gè)常用的處理http協(xié)議的請(qǐng)求和響應(yīng)的函數(shù)抬虽,如果用這個(gè)庫(kù)官觅,基本上就不需要考慮網(wǎng)絡(luò)層的實(shí)現(xiàn)了,只需要了解http協(xié)議就好阐污。
但是也可以從網(wǎng)絡(luò)層寫起休涤,利用socket編程。這樣你需要考慮的一個(gè)很重要的問(wèn)題就是:服務(wù)器處理并發(fā)笛辟,是多線程還是多進(jìn)程功氨,還是異步模型,或者是多進(jìn)程+多線程∈执保現(xiàn)在感覺網(wǎng)絡(luò)編程真的是博大精深捷凄。
這樣一個(gè)最簡(jiǎn)單的服務(wù)器就寫好了,但是他不能對(duì)瀏覽器請(qǐng)求的頁(yè)面做出響應(yīng)围来,對(duì)于所有的請(qǐng)求跺涤,服務(wù)器回復(fù) “to-do”
import BaseHTTPServer
#RequestHandler 繼承 BaseHTTPRequestHandler
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
'''處理請(qǐng)求并返回頁(yè)面'''
# 頁(yè)面模板
Page = "to do "
#do_GET的函數(shù)的名字 是不能改的
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(self.Page)))
self.end_headers()
self.wfile.write(self.Page)
if __name__ == '__main__':
serverAddress = ('', 8080)
server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
server.serve_forever()
demo2 依然不可以響應(yīng)不同的html文件請(qǐng)求匈睁,但是可以利用基類的數(shù)據(jù)成員來(lái)輸出http請(qǐng)求的時(shí)間,端口桶错,請(qǐng)求的html文件航唆。
#-*- coding:utf-8 -*-
import BaseHTTPServer
#RequestHandler 繼承 BaseHTTPRequestHandler ,所以他自身就有一個(gè)path的數(shù)據(jù)成員
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
#Page="to doing wakakak "
Page='''
<html>
<body>
<table>
<tr> <td>Header</td> <td>Value</td> </tr>
<tr> <td>Date and time</td> <td>{date_time}</td> </tr>
<tr> <td>Client host</td> <td>{client_host}</td> </tr>
<tr> <td>Client port</td> <td>{client_port}</td> </tr>
<tr> <td>Command</td> <td>{command}</td> </tr>
<tr> <td>Path</td> <td>{path}</td> </tr>
</table>
</body>
</html>
'''
#
def do_GET(self):
#self.send_response(200)
#self.send_header('Content-Type','text/html')
#self.send_header('Content-Length',str(len(self.Page)))
#self.end_headers()
#self.wfile.write(self.Page)
page=self.create_page()
self.send_content(page)
def send_content(self,page):
self.send_response(200)
self.send_header('Content-Type','text/html')
self.send_header('Content-Length','text.html')
self.end_headers()
self.wfile.write(page)
#self.date_time_string 和 client_address[0/1]等等都是父類的書據(jù)成員
def create_page(self):
values={
'date_time':self.date_time_string(),
'client_host':self.client_address[0],
'client_port':self.client_address[1],
'command':self.command,
'path':self.path
}
page=self.Page.format(**values)
return page
if __name__ == '__main__':
serverAddress = ('', 8080)
server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
server.serve_forever()
運(yùn)行結(jié)果:
對(duì)于html 請(qǐng)求的響應(yīng)牛曹,其實(shí)就是拿去這個(gè)url請(qǐng)求后面的路徑佛点,然后在服務(wù)器的目錄下面去找這個(gè)文件,如果有黎比,就返回給瀏覽器超营。如果沒(méi)有,就返回404狀態(tài)碼阅虫。
下面是demo3:
#coding:utf-8
import sys,os,BaseHTTPServer
class ServerException(Exception):
pass
#RequestHandler 繼承 BaseHTTPRequestHandler 演闭,所以他自身就有一個(gè)path的數(shù)據(jù)成員
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
Error_Page="""\
<html>
<body>
<h1>Error accessing {path}</h1>
<p>{msg}</p>
</body>
</html>
"""
def do_GET(self):
try:
print 'get'
full_path=os.getcwd()+self.path
print full_path
print os.getcwd()
if not os.path.exists(full_path):
raise ServerException("'{ 0 }' not found ",format(self.path))
elif os.path.isfile(full_path):
self.handle_file(full_path)
else:
raise ServerException("unkown object '{ 0 }'",format(self.path))
except Exception as msg:
self.handle_error(msg)
def handle_file(self,full_path):
try:
with open(full_path,'rb') as reader:
content=reader.read()
self.send_content(content)
except IOError as msg:
msg="'{0}' cannot be read :{1} ".format(self.path,msg)
self.handle_error(msg)
def handle_error(self,msg):
content=self.Error_Page.format(path=self.path,msg=msg)
self.send_content(content,404)
def send_content(self,page,status=200):
#print 'send_content function '
self.send_response(status)
self.send_header('Content-Type','text/html')
self.send_header('Content-Length','text.html')
self.end_headers()
self.wfile.write(page)
if __name__ == '__main__':
serverAddress = ('', 8081)
server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
server.serve_forever()
運(yùn)行結(jié)果就是第一張圖那法樣,實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的靜態(tài)服務(wù)器颓帝。
git鏈接:https://github.com/zhaozhengcoder/Python
下一步米碰,試著去模擬實(shí)現(xiàn)一些cgi
//to-do 吃飯去了 好餓
后續(xù)可以參考:
Python實(shí)現(xiàn)簡(jiǎn)單的Web服務(wù)器 Part2 http://www.reibang.com/p/d28395655bc0