python-web服務器

在Web應用中形耗,服務器把網(wǎng)頁傳給瀏覽器闻妓,實際上就是把網(wǎng)頁的HTML代碼發(fā)送給瀏覽器双谆,讓瀏覽器顯示出來。而瀏覽器和服務器之間的傳輸協(xié)議是HTTP兴枯,所以:
HTML是一種用來定義網(wǎng)頁的文本血淌,會HTML,就可以編寫網(wǎng)頁财剖;
HTTP是在網(wǎng)絡上傳輸HTML的協(xié)議悠夯,用于瀏覽器和服務器的通信。
HTTP是Hyper Text Transfer Protocol(超文本傳輸協(xié)議)的縮寫躺坟。它的發(fā)展是萬維網(wǎng)協(xié)會(World Wide Web Consortium)和Internet工作小組IETF(Internet Engineering Task Force)合作的結果沦补,(他們)最終發(fā)布了一系列的RFC,RFC 1945定義了HTTP/1.0版本咪橙。其中最著名的就是RFC 2616夕膀。RFC 2616定義了今天普遍使用的一個版本——HTTP 1.1。
HTTP協(xié)議(HyperText Transfer Protocol美侦,超文本傳輸協(xié)議)是用于從WWW服務器傳輸超文本到本地瀏覽器的傳送協(xié)議产舞。它可以使瀏覽器更加高效,使網(wǎng)絡傳輸減少音榜。它不僅保證計算機正確快速地傳輸超文本文檔,還確定傳輸文檔中的哪一部分捧弃,以及哪部分內容首先顯示(如文本先于圖形)等赠叼。
HTTP是一個基于TCP/IP通信協(xié)議來傳遞數(shù)據(jù)(HTML 文件, 圖片文件, 查詢結果等)擦囊。
HTTP是一個應用層協(xié)議,由請求和響應構成嘴办,是一個標準的客戶端服務器模型瞬场。HTTP是一個無狀態(tài)的協(xié)議。
下面寫一個web靜態(tài)頁面涧郊,顯示固定頁面贯被。代碼如下:

import socket
import multiprocessing
import time
import os


def clientP(newSocket):
    recvData = newSocket.recv(1024).decode('gbk')
    print(recvData)

    responseLine = 'HTTP/1.1 200 OK' + os.linesep
    responseHeader = 'Server: wangzy' + os.linesep
    responseHeader += 'Date: %s' % time.ctime() + os.linesep
    responseBody = 'Hello World!'
    sendData = (responseLine + responseHeader + os.linesep + responseBody).encode('gbk')
    newSocket.send(sendData)
    newSocket.close()


def main():
    severSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    severSocket.bind(('', 7777))
    severSocket.listen(10)

    while True:
        newSocket, clientAddr = severSocket.accept()
        p = multiprocessing.Process(target=clientP, args=(newSocket,))
        p.start()
        newSocket.close()


if __name__ == '__main__':
    main()

結果如下:



第一行:
GET / HTTP/1.1
GET表示一個讀取請求,將從服務器獲得網(wǎng)頁數(shù)據(jù)妆艘,/表示URL的路徑彤灶,URL總是以/開頭,/就表示首頁批旺,最后的HTTP/1.1指示采用的HTTP協(xié)議版本是1.1幌陕。目前HTTP協(xié)議的版本就是1.1,但是大部分服務器也支持1.0版本汽煮,主要區(qū)別在于1.1版本允許多個HTTP請求復用一個TCP連接搏熄,以加快傳輸速度。



我們可以跟數(shù)據(jù)庫的CRUD增刪改查操作對應起來:
CREATE :PUT
READ:GET
UPDATE:POST
DELETE:DELETE

打開網(wǎng)頁暇赤,查看開發(fā)者工具:



200表示一個成功的響應心例,后面的OK是說明。
如果返回的不是200鞋囊,那么往往有其他的功能止后,例如
失敗的響應有404 Not Found:網(wǎng)頁不存在
500 Internal Server Error:服務器內部出錯
等等...
HTTP格式



HTTP GET請求的格式:
    GET /path HTTP/1.1
    Header1: Value1
    Header2: Value2
    Header3: Value3

每個Header一行一個,換行符是\r\n失暴。
HTTP POST請求的格式:

    POST /path HTTP/1.1
    Header1: Value1
    Header2: Value2
    Header3: Value3

    body data goes here...

當遇到連續(xù)兩個\r\n時坯门,Header部分結束,后面的數(shù)據(jù)全部是Body逗扒。
HTTP響應的格式:

    200 OK
    Header1: Value1
    Header2: Value2
    Header3: Value3

    body data goes here...

HTTP響應如果包含body古戴,也是通過\r\n\r\n來分隔的。
請再次注意矩肩,Body的數(shù)據(jù)類型由Content-Type頭來確定现恼,如果是網(wǎng)頁,Body就是文本黍檩,如果是圖片叉袍,Body就是圖片的二進制數(shù)據(jù)。
當存在Content-Encoding時刽酱,Body數(shù)據(jù)是被壓縮的喳逛,最常見的壓縮方式是gzip,所以棵里,看到Content-Encoding: gzip時润文,需要將Body數(shù)據(jù)先解壓縮姐呐,才能得到真正的數(shù)據(jù)。壓縮的目的在于減少Body的大小典蝌,加快網(wǎng)絡傳輸曙砂。
顯示需要的頁面

import socket
import re

from multiprocessing import Process

# 設置靜態(tài)文件根目錄
HTML_ROOT_DIR = "./html"


def handle_client(client_socket):
    """處理客戶端請求"""
    # 獲取客戶端請求數(shù)據(jù)
    request_data = client_socket.recv(1024)
    print("request data:", request_data.decode('utf-8'))
    request_lines = request_data.splitlines()

    # 解析請求報文
    # 'GET / HTTP/1.1'
    request_start_line = request_lines[0].decode('utf-8')
    # 提取用戶請求的文件名
    file_name = re.match(r"\w+ +(/[^ ]*) ", request_start_line).group(1)

    if "/" == file_name:
        file_name = "/index.html"

    # 打開文件,讀取內容
    try:
        file = None
        file = open(HTML_ROOT_DIR + file_name, "rb")
        file_data = file.read()
        # 構造響應數(shù)據(jù)
        response_start_line = "HTTP/1.1 200 OK\r\n"
        response_headers = "Server: My server\r\n"
        response_body = file_data.decode("utf-8")
    except FileNotFoundError:
        response_start_line = "HTTP/1.1 404 Not Found\r\n"
        response_headers = "Server: My server\r\n"
        response_body = "The file is not found!"
    finally:
        if file and (not file.closed):
            file.close()

    response = response_start_line + response_headers + "\r\n" + response_body
    print("response data:", response)

    # 向客戶端返回響應數(shù)據(jù)
    client_socket.send(bytes(response, "utf-8"))

    # 關閉客戶端連接
    client_socket.close()


def main():
    '作為程序的主控制入口'
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(("", 8000))
    server_socket.listen(128)

    while True:
        client_socket, client_address = server_socket.accept()
        # print("[%s, %s]用戶連接上了" % (client_address[0],client_address[1]))
        print("[%s, %s]用戶連接上了" % client_address)
        handle_client_process = Process(target=handle_client, args=(client_socket,))
        handle_client_process.start()
        client_socket.close()


if __name__ == "__main__":
    main()

結果如下:





使用類創(chuàng)建服務器并實現(xiàn)動態(tài)資源的申請

import socket
import multiprocessing
import re
import os
import time


class MyServer(object):
    def __init__(self, pork=8888):
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        serverSocket.bind(('', pork))
        self.serverSocket = serverSocket
        self.HTMLPATH = './html'

    def start(self):
        self.serverSocket.listen()
        while True:
            newSocket, clientAddr = self.serverSocket.accept()
            multiprocessing.Process(target=self.clientHander, args=(newSocket,)).start()
            newSocket.close()

    def clientHander(self, newSocket):
        try:
            recvData = newSocket.recv(1024).decode('gbk')
            fileName = re.split(r' +', recvData.splitlines()[0])[1]
            filePath = self.HTMLPATH
            if fileName.endswith('.py'):
                try:
                    pyname = fileName[1:-3]
                    print(pyname)
                    # 導入
                    pyModule = __import__(pyname)
                    env = {}
                    responseBody = pyModule.application(env, self.startResponse)
                    responseLine = self.responseLine
                    responseHeader = self.responseHeader
                except ImportError:
                    responseLine = 'HTTP/1.1 404 NOT FOUND'+os.linesep
                    responseHeader = 'Server: Wangzy' + os.linesep
                    responseBody = '<h1>找不到PY文件<h1>'

            else:
                if '/' == fileName:
                    filePath += '/index.html'
                else:
                    filePath += fileName
                try:
                    file = None
                    file = open(filePath, 'r', encoding='gbk')
                    responseBody = file.read()

                    responseLine = 'HTTP/1.1 200 OK' + os.linesep
                    responseHeader = 'Server: Wangzy' + os.linesep

                except FileNotFoundError:
                    responseLine = 'HTTP/1.1 404 NOT FOUND' + os.linesep
                    responseHeader = 'Server: Wangzy' + os.linesep
                    responseBody = '找不到html文件'

                finally:
                    if (file != None) and (not file.closed):
                        file.close()

        except Exception:
            responseLine = 'HTTP/1.1 500 ERROR' + os.linesep
            responseHeader = 'Server: Wangzy' + os.linesep
            responseBody = '服務器忙骏掀,稍后再試鸠澈。。截驮。'


        finally:
            sendData = (responseLine + responseHeader + os.linesep + responseBody).encode('gbk')
            newSocket.send(sendData)
            newSocket.close()

    def startResponse(self, status, responseHeaders):
        '''

            :param self:
            :param status:
            :param responseHeaders:
            :return:
        '''
        self.responseLine = status
        self.responseHeader = ''
        for k, v in responseHeaders:
            kv = (k + ':' + v + os.linesep)
            self.responseHeader += kv


if __name__ == '__main__':
    server = MyServer()
    server.start()

動態(tài)申請的py文件寫法:

import time


def application(env, startResponse):
    '''

    :param env:
    :param startResponse:
    :return:
    '''
    status = 'HTTP/1.1 200 OK'
    responseHeaders = [('Server', 'Wangzy'), ('Date', 'today'), ('Content-Type', 'text/plain')]
    startResponse(status, responseHeaders)
    responseBody = str(time.ctime())
    return responseBody

結果如下:



如果請求的路徑不存在



以上例子中就用到了WSGI接口笑陈。
上面的application()函數(shù)就是符合WSGI標準的一個HTTP處理函數(shù),它接收兩個參數(shù):

environ:一個包含所有HTTP請求信息的dict對象侧纯;
start_response:一個發(fā)送HTTP響應的函數(shù)新锈。
整個application()函數(shù)本身沒有涉及到任何解析HTTP的部分,也就是說眶熬,把底層web服務器解析部分和應用程序邏輯部分進行了分離妹笆,這樣開發(fā)者就可以專心做一個領域了
不過,等等娜氏,這個application()函數(shù)怎么調用拳缠?如果我們自己調用,兩個參數(shù)environ和start_response我們沒法提供贸弥,返回的str也沒法發(fā)給瀏覽器窟坐。
所以application()函數(shù)必須由WSGI服務器來調用。有很多符合WSGI規(guī)范的服務器绵疲。而我們此時的web服務器項目的目的就是做一個極可能解析靜態(tài)網(wǎng)頁還可以解析動態(tài)網(wǎng)頁的服務器哲鸳。
下面來編寫框架和服務器,實現(xiàn)功能的分離盔憨。
服務器代碼如下:

import socket
import multiprocessing
import re
import os
import myFramework


class MyServer(object):
    def __init__(self, application):
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serverSocket = serverSocket
        self.application = application

    def bind(self, port=9999):
        self.serverSocket.bind(('', port))

    def start(self):
        self.serverSocket.listen()
        while True:
            newSocket, clientAddr = self.serverSocket.accept()
            multiprocessing.Process(target=self.clientHander, args=(newSocket,)).start()
            newSocket.close()

    def clientHander(self, newSocket):
        recvData = newSocket.recv(1024).decode('gbk')
        fileName = re.split(r' +', recvData.splitlines()[0])[1]
        method = re.split(r' +', recvData.splitlines()[0])[0]
        env = {
            'PATH_INFO': fileName,
            'METHOD': method
        }
        for item in recvData:
            if item.count(':') != 0:
                k, v = item.split(':')
                env[k] = v
        responseBody = self.application(env, self.startResponse)
        sendData = (self.responseHeader + os.linesep + os.linesep + responseBody).encode('gbk')
        newSocket.send(sendData)
        newSocket.close()

    def startResponse(self, status, responseHeader):
        self.responseHeader = status + os.linesep
        for k, v in responseHeader:
            kv = (k + ':' + v + os.linesep)
            self.responseHeader += kv


if __name__ == '__main__':
    server = MyServer(myFramework.application)
    server.bind(8888)
    server.start()

框架代碼如下:

import time

class Application(object):
    def __init__(self, urls):
        self.urls = urls
        self.filePath = './html'

    def __call__(self, env, startResponse):
        # 從請求頭中獲取訪問的名字
        fileName = env.get('PATH_INFO')
        # 判斷是靜態(tài)訪問還是動態(tài)訪問
        # 靜態(tài)訪問方法
        if fileName.startswith('/static'):
            fileName = fileName[7:]
            if '/' == fileName:
                self.filePath += '/index.html'
            else:
                self.filePath += fileName

            try:
                file = None
                file = open(self.filePath, 'r', encoding='gbk')
                responseBody = file.read()
                status = 'HTTP/1.1 200 OK'
                responseHeaders = [('Server', 'Wangzy')]
            except FileNotFoundError:
                status = 'HTTP/1.1 404 NOT FOUND'
                responseHeaders = [('Server', 'Wangzy')]
                responseBody = 'HTML文件找不到徙菠!'
            finally:
                startResponse(status, responseHeaders)
                if (file != None) and (not file.closed):
                    file.close()
        # 動態(tài)訪問方法
        else:
            # 表示請求的名字是否在urls中,True:存在郁岩,F(xiàn)alse:不存在
            isIn = False
            for k, v in self.urls:
                if k == fileName:
                    responseBody = v(env, startResponse)
                    isIn = True
                    break
            if isIn == False:
                status = 'HTTP/1.1 404 NOT FOUND'
                responseHeaders = [('Server', 'Wangzy')]
                responseBody = 'py文件找不到婿奔!'
                startResponse(status, responseHeaders)
        return responseBody


def showtime(env, startResponse):
    status = 'HTTP/1.1 200 OK'
    responseHeaders = [('Server', 'Wangzy')]
    startResponse(status,responseHeaders)
    responseBody = time.ctime()
    return responseBody


def shownews(env, startResponse):
    status = 'HTTP/1.1 200 OK'
    responseHeaders = [('Server', 'Wangzy')]
    startResponse(status, responseHeaders)
    responseBody = '今天的新聞是。问慎。萍摊。。'
    return responseBody


urls = [
    ('/showtime', showtime),
    ('/shownews', shownews)
]

application = Application(urls)

結果如下:






這里就實現(xiàn)了框架和服務器的功能分離如叼,但是現(xiàn)在還存在一個問題冰木,我們在服務器中導入了框架,并且把需要調用的框架對象給寫死了笼恰,如果需要應用其他框架踊沸,還需要修改服務器代碼囚衔,這就違反了代碼的開閉原則。
為了使我們的服務器具有通用性雕沿,可以匹配任何符合wsgi的框架,我們修改服務器代碼如下:

import socket
import multiprocessing
import re
import os
import sys


class MyServer(object):
    def __init__(self, application):
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serverSocket = serverSocket
        self.application = application

    def bind(self, port=9999):
        self.serverSocket.bind(('', port))

    def start(self):
        self.serverSocket.listen()
        while True:
            newSocket, clientAddr = self.serverSocket.accept()
            multiprocessing.Process(target=self.clientHander, args=(newSocket,)).start()
            newSocket.close()

    def clientHander(self, newSocket):
        recvData = newSocket.recv(1024).decode('gbk')
        fileName = re.split(r' +', recvData.splitlines()[0])[1]
        method = re.split(r' +', recvData.splitlines()[0])[0]
        env = {
            'PATH_INFO': fileName,
            'METHOD': method
        }
        for item in recvData:
            if item.count(':') != 0:
                k, v = item.split(':')
                env[k] = v
        responseBody = self.application(env, self.startResponse)
        sendData = (self.responseHeader + os.linesep + os.linesep + responseBody).encode('gbk')
        newSocket.send(sendData)
        newSocket.close()

    def startResponse(self, status, responseHeader):
        self.responseHeader = status + os.linesep
        for k, v in responseHeader:
            kv = (k + ':' + v + os.linesep)
            self.responseHeader += kv


def main():
    print(sys.argv)
    moduleName, attrName = sys.argv[1].split(':')
    print(moduleName)
    print(attrName)
    myModule = __import__(moduleName)
    server = MyServer(getattr(myModule, attrName))
    server.bind(8888)
    server.start()

if __name__ == '__main__':
    main()

框架代碼如下:

import time


class Application(object):
    def __init__(self, urls):
        self.urls = urls
        self.filePath = './html'

    def __call__(self, env, startResponse):
        # 從請求頭中獲取訪問的名字
        fileName = env.get('PATH_INFO')
        # 判斷是靜態(tài)訪問還是動態(tài)訪問
        # 靜態(tài)訪問方法
        if fileName.startswith('/static'):
            fileName = fileName[7:]
            if '/' == fileName:
                self.filePath += '/index.html'
            else:
                self.filePath += fileName

            try:
                file = None
                file = open(self.filePath, 'r', encoding='gbk')
                responseBody = file.read()
                status = 'HTTP/1.1 200 OK'
                responseHeaders = [('Server', 'Wangzy')]
            except FileNotFoundError:
                status = 'HTTP/1.1 404 NOT FOUND'
                responseHeaders = [('Server', 'Wangzy')]
                responseBody = 'HTML文件找不到猴仑!'
            finally:
                startResponse(status, responseHeaders)
                if (file != None) and (not file.closed):
                    file.close()
        # 動態(tài)訪問方法
        else:
            # 表示請求的名字是否在urls中审轮,True:存在,F(xiàn)alse:不存在
            isIn = False
            for k, v in self.urls:
                if k == fileName:
                    responseBody = v(env, startResponse)
                    isIn = True
                    break
            if isIn == False:
                status = 'HTTP/1.1 404 NOT FOUND'
                responseHeaders = [('Server', 'Wangzy')]
                responseBody = 'py文件找不到辽俗!'
                startResponse(status, responseHeaders)
        return responseBody


def showtime(env, startResponse):
    status = 'HTTP/1.1 200 OK'
    responseHeaders = [('Server', 'Wangzy')]
    startResponse(status, responseHeaders)
    responseBody = time.ctime()
    return responseBody


def shownews(env, startResponse):
    status = 'HTTP/1.1 200 OK'
    responseHeaders = [('Server', 'Wangzy')]
    startResponse(status, responseHeaders)
    responseBody = '今天的新聞是疾渣。。崖飘。榴捡。'
    return responseBody


urls = [
    ('/showtime', showtime),
    ('/shownews', shownews)
]

application = Application(urls)

此時,我們如果運行服務器會報錯:



在終端用命令運行服務器



最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末朱浴,一起剝皮案震驚了整個濱河市吊圾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翰蠢,老刑警劉巖项乒,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異梁沧,居然都是意外死亡檀何,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門廷支,熙熙樓的掌柜王于貴愁眉苦臉地迎上來频鉴,“玉大人,你說我怎么就攤上這事恋拍《饪祝” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵芝囤,是天一觀的道長似炎。 經(jīng)常有香客問我,道長悯姊,這世上最難降的妖魔是什么羡藐? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮悯许,結果婚禮上仆嗦,老公的妹妹穿的比我還像新娘。我一直安慰自己先壕,他們只是感情好瘩扼,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布谆甜。 她就那樣靜靜地躺著,像睡著了一般集绰。 火紅的嫁衣襯著肌膚如雪规辱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天栽燕,我揣著相機與錄音罕袋,去河邊找鬼。 笑死碍岔,一個胖子當著我的面吹牛浴讯,可吹牛的內容都是我干的。 我是一名探鬼主播蔼啦,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼榆纽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捏肢?” 一聲冷哼從身側響起奈籽,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸵赫,沒想到半個月后唠摹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡奉瘤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年勾拉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盗温。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡藕赞,死狀恐怖,靈堂內的尸體忽然破棺而出卖局,到底是詐尸還是另有隱情斧蜕,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布砚偶,位于F島的核電站批销,受9級特大地震影響,放射性物質發(fā)生泄漏染坯。R本人自食惡果不足惜均芽,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望单鹿。 院中可真熱鬧掀宋,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至镣奋,卻和暖如春币呵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侨颈。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工富雅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人肛搬。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像毕贼,于是被迫代替她去往敵國和親温赔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容

  • HTTP協(xié)議介紹: HTTP是Hyper Text Transfer Protocol(超文本傳輸協(xié)議)的縮寫 H...
    界面大叔閱讀 203評論 0 0
  • Web靜態(tài)服務器 顯示靜態(tài)的頁面 首頁.html 客戶端瀏覽器 服務端 Web服務器動態(tài)資源請求 瀏覽器請求動態(tài)頁...
    PythonMaO閱讀 986評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理鬼癣,服務發(fā)現(xiàn)陶贼,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 一待秃、Web開發(fā) Browser/Server模式目前最流行拜秧,簡稱BS架構。在BS架構下章郁,客戶端只需要瀏覽器枉氮,應用程...
    時間之友閱讀 854評論 0 0
  • Python Web服務器并發(fā)性能測試 測試條件 測試機器:四核,4GB內存 系統(tǒng)環(huán)境:Ubuntu 14.04 ...
    林銳波閱讀 6,277評論 1 3