Python實現(xiàn)簡易web服務(wù)器

1.請自行了解HTTP協(xié)議

2.創(chuàng)建Socket服務(wù)赞季,監(jiān)聽指定IP和端口

Paste_Image.png

3.以阻塞方式等待客戶端連接

Paste_Image.png

4.讀取客戶端請求數(shù)據(jù)并進行解析

Paste_Image.png

5.準(zhǔn)備服務(wù)器運行上下文

Paste_Image.png

6.處理客戶端請求數(shù)據(jù)

Paste_Image.png

7.根據(jù)用戶請求路徑讀取文件

Paste_Image.png

8.返回響應(yīng)結(jié)果給客戶端

Paste_Image.png

9.程序入口

Paste_Image.png

10.目錄結(jié)構(gòu)

Paste_Image.png

11.運行

python wsgiserver.py app:run

12.源碼:

a.wsgiserver.py文件

#encoding:utf-8

import socket
import StringIO
import sys
import logging
from datetime import datetime

logger = logging.getLogger(__name__)

class WSGIServer(object):
    
    address_family = socket.AF_INET
    socket_type = socket.SOCK_STREAM
    request_queue_size = 30
    recv_size = 1024

    def __init__(self, server_address):
        self._listen_socket = _listen_socket = socket.socket(self.address_family,
                                                              self.socket_type)
        _listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        _listen_socket.bind(server_address)
        _listen_socket.listen(self.request_queue_size)
        _host, _port = _listen_socket.getsockname()
        self._server_name = socket.getfqdn(_host)
        self._server_port = _port
        self._headers_set = []
        self._application = None
        self._client = None
        self._request_data = None
        self._request_method = None
        self._path = None
        self._request_version = None
        self._start_response = None

    def set_application(self, application):
        self._application = application

    def server_forever(self):
        _listen_socket = self._listen_socket
        logger.info('listen on %s:%s', self._server_name, self._server_port)
        while 1:
            try:
                self._client, _addr = _listen_socket.accept()
                self._handle_request(_addr)
            except KeyboardInterrupt as e:
                logger.info('interrupt')
                break
            except BaseException as e:
                logger.error(e)

    def _handle_request(self, client_addr):
        self._request_data = _request_data = self._client.recv(self.recv_size)
        self._parse_request_data(_request_data)
        _env = self._get_environment(client_addr)
        _result = self._application(_env, self.start_response)
        self._finish_response(_result)

    def _parse_request_data(self, request_data):
        _request_line = str(request_data.splitlines()[0]).rstrip('\r\n')
        (self._request_method, self._path, self._request_version) = _request_line.split()

    def _get_environment(self, client_addr):
        _env = {}
        _env['wsgi.version'] = (1, 0)
        _env['wsgi.url_scheme'] = 'http'
        _env['wsgi.input'] = StringIO.StringIO(self._request_data)
        _env['wsgi.errors'] = sys.stderr
        _env['wsgi.multithread'] = False
        _env['wsgi.multiprocess'] = False
        _env['wsgi.run_once'] = False
        _env['REQUEST_METHOD'] = self._request_method.upper()
        _env['PATH_INFO'] = self._path
        _env['SERVER_NAME'] = self._server_name
        _env['SERVER_PORT'] = self._server_port
        _env['HTTP_CLIENT_IP'] = client_addr[0]

        logger.info('%s %s %s %s', _env['HTTP_CLIENT_IP'], datetime.now().strftime('%Y-%m-%d %H:%M:%S'), _env['REQUEST_METHOD'], _env['PATH_INFO'])

        return _env

    def start_response(self, status, response_headers, exc_info=None):
        _server_headers = [
            ('Date', 'Sun, 7 Jun 2015 23:07:04 GMT'),
            ('Server', 'WSGIServer 0.1')
            ]
        self._headers_set = [status, response_headers + _server_headers]

    def _finish_response(self, result):
        _status, _response_headers = self._headers_set
        _response = 'HTTP/1.1 {status}\r\n'.format(status=_status)
        for _header in _response_headers:
            _response += '{0}:{1}\r\n'.format(*_header)
        _response += '\r\n'
        for _data in result:
            _response += _data

        self._client.sendall(_response)
        self._client.close()


def make_server(server_address, application):
    server = WSGIServer(server_address)
    server.set_application(application)
    return server


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    
    server_addr= ('0.0.0.0', 43002)

    app_path = sys.argv[1]
    module, application = app_path.split(':')
    module = __import__(module)
    application = getattr(module, application)
    httpd = make_server(server_addr, application)
    httpd.server_forever()

b.app.py文件

#encoding:utf-8

import os

class PageNotFoundException(BaseException):
    pass

def render(filename, dirname='html'):
    _path = os.path.join(dirname, filename)
    if os.path.exists(_path):
        with open(_path, 'rb') as handler:
            return handler.read()

    raise PageNotFoundException('file not found:%s' % _path)

def run(env, start_response):
    _path = env.get('PATH_INFO')
    response = ''
    try:
        _path = 'index.html' if _path == '/' else _path[1:]
        if _path.endswith('.css'):
            start_response('200 OK', [('Content-Type', 'text/css')])
        elif _path.endswith('.js'):
            start_response('200 OK', [('Content-Type', 'text/javascript')])
        elif _path.endswith('.html'):
            start_response('200 OK', [('Content-Type', 'text/html')])
        else:
            start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Disposition', 'attachment; filename=%s' % os.path.basename(_path))])
        response = render(_path) 
    except PageNotFoundException as e:
        response = render('404.html')

    return [response, '\r\n']
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撵摆,一起剝皮案震驚了整個濱河市悯衬,隨后出現(xiàn)的幾起案子十偶,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異埃跷,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門弥雹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垃帅,“玉大人,你說我怎么就攤上這事缅糟⊥χ牵” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵窗宦,是天一觀的道長赦颇。 經(jīng)常有香客問我,道長赴涵,這世上最難降的妖魔是什么媒怯? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮髓窜,結(jié)果婚禮上扇苞,老公的妹妹穿的比我還像新娘。我一直安慰自己寄纵,他們只是感情好鳖敷,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著程拭,像睡著了一般定踱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上恃鞋,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天崖媚,我揣著相機與錄音,去河邊找鬼恤浪。 笑死畅哑,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的水由。 我是一名探鬼主播荠呐,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砂客!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤晌姚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唤锉,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡蝙寨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了靠胜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菠赚。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枣购,我是刑警寧澤涩堤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站白魂,受9級特大地震影響碧聪,放射性物質(zhì)發(fā)生泄漏捆等。R本人自食惡果不足惜谒养,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望明郭。 院中可真熱鬧买窟,春花似錦、人聲如沸薯定。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽话侄。三九已至亏推,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間年堆,已是汗流浹背吞杭。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留变丧,地道東北人芽狗。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像痒蓬,于是被迫代替她去往敵國和親童擎。 傳聞我的和親對象是個殘疾皇子曼月,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)柔昼,斷路器哑芹,智...
    卡卡羅2017閱讀 134,701評論 18 139
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 11,005評論 6 13
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司捕透,掛了不少聪姿,但最終還是拿到小米、百度乙嘀、阿里末购、京東、新浪虎谢、CVTE盟榴、樂視家的研發(fā)崗...
    時芥藍閱讀 42,275評論 11 349
  • 管住嘴,邁開腿婴噩,減肥的六字真言擎场。 對于吃貨的我來說,管住嘴太難了几莽。 但是看著自己身上的這一堆肉迅办,真想馬上變回以前的...
    九悠閱讀 321評論 1 2
  • 第一話 開學(xué) 初中畢業(yè)的我,馬上就要去到一所新的學(xué)校章蚣,我被分配到A市的巧迪高中站欺,因為是在另一個城市,所以我的父母不...
    檸檬ion閱讀 286評論 0 0