zoukankan      html  css  js  c++  java
  • http协议、web服务器-并发服务器1

    阅读目录:

    1.http协议

    2.Web静态服务器-1-显示固定的页面

    3.Web静态服务器-2-显示需要的页面

    4.Web静态服务器-3-多进程

    5.Web静态服务器-4-多线程

    一.HTTP协议简介

    1. 使用谷歌/火狐浏览器分析

    在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP,所以:

    • HTML是一种用来定义网页的文本,会HTML,就可以编写网页;

    • HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信。

    Chrome浏览器提供了一套完整地调试工具,非常适合Web开发。

    安装好Chrome浏览器后,打开Chrome,在菜单中选择“视图”,“开发者”,“开发者工具”,就可以显示开发者工具:

    说明

    • Elements显示网页的结构
    • Network显示浏览器和服务器的通信

    我们点Network,确保第一个小红灯亮着,Chrome就会记录所有浏览器和服务器之间的通信:

     

     

     

    2. http协议的分析

    当我们在地址栏输入www.sina.com时,浏览器将显示新浪的首页。在这个过程中,浏览器都干了哪些事情呢?通过Network的记录,我们就可以知道。在Network中,找到www.sina.com那条记录,点击,右侧将显示Request Headers,点击右侧的view source,我们就可以看到浏览器发给新浪服务器的请求:

    2.1 浏览器请求

     

     

    说明

    最主要的头两行分析如下,第一行:

        GET / HTTP/1.1

    GET表示一个读取请求,将从服务器获得网页数据,/表示URL的路径,URL总是以/开头,/就表示首页,最后的HTTP/1.1指示采用的HTTP协议版本是1.1。目前HTTP协议的版本就是1.1,但是大部分服务器也支持1.0版本,主要区别在于1.1版本允许多个HTTP请求复用一个TCP连接,以加快传输速度。

    从第二行开始,每一行都类似于Xxx: abcdefg:

      Host: www.sina.com

    表示请求的域名是www.sina.com。如果一台服务器有多个网站,服务器就需要通过Host来区分浏览器请求的是哪个网站。

    2.2 服务器响应

    继续往下找到Response Headers,点击view source,显示服务器返回的原始响应数据:

     

    HTTP响应分为Header和Body两部分(Body是可选项),我们在Network中看到的Header最重要的几行如下:

        HTTP/1.1 200 OK

     

    200表示一个成功的响应,后面的OK是说明。

    如果返回的不是200,那么往往有其他的功能,例如

    • 失败的响应有404 Not Found:网页不存在
    • 500 Internal Server Error:服务器内部出错
    • ...等等...
       Content-Type: text/html

    Content-Type指示响应的内容,这里是text/html表示HTML网页。

    请注意,浏览器就是依靠Content-Type来判断响应的内容是网页还是图片,是视频还是音乐。浏览器并不靠URL来判断响应的内容,所以,即使URL是http://www.baidu.com/meimei.jpg,它也不一定就是图片。

    HTTP响应的Body就是HTML源码,我们在菜单栏选择“视图”,“开发者”,“查看网页源码”就可以在浏览器中直接查看HTML源码:

     

    浏览器解析过程

    当浏览器读取到新浪首页的HTML源码后,它会解析HTML,显示页面,然后,根据HTML里面的各种链接,再发送HTTP请求给新浪服务器,拿到相应的图片、视频、Flash、JavaScript脚本、CSS等各种资源,最终显示出一个完整的页面。所以我们在Network下面能看到很多额外的HTTP请求。

     

    3. 总结

    3.1 HTTP请求

    跟踪了新浪的首页,我们来总结一下HTTP请求的流程:

    3.1.1 步骤1:浏览器首先向服务器发送HTTP请求,请求包括:

    方法:GET还是POST,GET仅请求资源,POST会附带用户数据;

    路径:/full/url/path;

    域名:由Host头指定:Host: www.sina.com

    以及其他相关的Header;

    如果是POST,那么请求还包括一个Body,包含用户数据

    3.1.1 步骤2:服务器向浏览器返回HTTP响应,响应包括:

    响应代码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器端处理时发生了错误;

    响应类型:由Content-Type指定;

    以及其他相关的Header;

    通常服务器的HTTP响应会携带内容,也就是有一个Body,包含响应的内容,网页的HTML源码就在Body中。

    3.1.1 步骤3:如果浏览器还需要继续向服务器请求其他资源,比如图片,就再次发出HTTP请求,重复步骤1、2。

    Web采用的HTTP协议采用了非常简单的请求-响应模式,从而大大简化了开发。当我们编写一个页面时,我们只需要在HTTP请求中把HTML发送出去,不需要考虑如何附带图片、视频等,浏览器如果需要请求图片和视频,它会发送另一个HTTP请求,因此,一个HTTP请求只处理一个资源(此时就可以理解为TCP协议中的短连接,每个链接只获取一个资源,如需要多个就需要建立多个链接)

    HTTP协议同时具备极强的扩展性,虽然浏览器请求的是http://www.sina.com的首页,但是新浪在HTML中可以链入其他服务器的资源,比如<img src="http://i1.sinaimg.cn/home/2013/1008/U8455P30DT20131008135420.png">,从而将请求压力分散到各个服务器上,并且,一个站点可以链接到其他站点,无数个站点互相链接起来,就形成了World Wide Web,简称WWW。

    3.2 HTTP格式

    每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。

    HTTP协议是一种文本协议,所以,它的格式也非常简单。

    3.2.1 HTTP GET请求的格式:

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

     

    每个Header一行一个,换行符是 。

    3.2.2 HTTP POST请求的格式:

        POST /path HTTP/1.1
        Header1: Value1
        Header2: Value2
        Header3: Value3
    
        body data goes here...

     

    当遇到连续两个 时,Header部分结束,后面的数据全部是Body。

    3.2.3 HTTP响应的格式:

        200 OK
        Header1: Value1
        Header2: Value2
        Header3: Value3
    
        body data goes here...

     

    HTTP响应如果包含body,也是通过 来分隔的。

    请再次注意,Body的数据类型由Content-Type头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。

    当存在Content-Encoding时,Body数据是被压缩的,最常见的压缩方式是gzip,所以,看到Content-Encoding: gzip时,需要将Body数据先解压缩,才能得到真正的数据。压缩的目的在于减少Body的大小,加快网络传输。

    二.Web静态服务器-1-显示固定的页面

    #coding=utf-8
    import socket
    
    
    def handle_client(client_socket):
        "为一个客户端进行服务"
        recv_data = client_socket.recv(1024).decode("utf-8")
        request_header_lines = recv_data.splitlines()
        for line in request_header_lines:
            print(line)
    
        # 组织相应 头信息(header)
        response_headers = "HTTP/1.1 200 OK
    "  # 200表示找到这个资源
        response_headers += "
    "  # 用一个空的行与body进行隔开
        # 组织 内容(body)
        response_body = "hello world"
    
        response = response_headers + response_body
        client_socket.send(response.encode("utf-8"))
        client_socket.close()
    
    
    def main():
        "作为程序的主控制入口"
    
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置当服务器先close 即服务器端4次挥手之后资源能够立即释放,这样就保证了,下次运行程序时 可以立即绑定7788端口
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(("", 7788))
        server_socket.listen(128)
        while True:
            client_socket, client_addr = server_socket.accept()
            handle_client(client_socket)
    
    
    if __name__ == "__main__":
        main()

     

    服务器端

    客户端

     

     

    三.Web静态服务器-2-显示需要的页面

    #coding=utf-8
    import socket
    import re
    
    
    def handle_client(client_socket):
        "为一个客户端进行服务"
        recv_data = client_socket.recv(1024).decode('utf-8', errors="ignore")
        request_header_lines = recv_data.splitlines()
        for line in request_header_lines:
            print(line)
    
        http_request_line = request_header_lines[0]
        get_file_name = re.match("[^/]+(/[^ ]*)", http_request_line).group(1)
        print("file name is ===>%s" % get_file_name)  # for test
    
        # 如果没有指定访问哪个页面。例如index.html
        # GET / HTTP/1.1
        if get_file_name == "/":
            get_file_name = DOCUMENTS_ROOT + "/index.html"
        else:
            get_file_name = DOCUMENTS_ROOT + get_file_name
    
        print("file name is ===2>%s" % get_file_name) #for test
    
        try:
            f = open(get_file_name, "rb")
        except IOError:
            # 404表示没有这个页面
            response_headers = "HTTP/1.1 404 not found
    "
            response_headers += "
    "
            response_body = "====sorry ,file not found===="
        else:
            response_headers = "HTTP/1.1 200 OK
    "
            response_headers += "
    "
            response_body = f.read()
            f.close()
        finally:
            # 因为头信息在组织的时候,是按照字符串组织的,不能与以二进制打开文件读取的数据合并,因此分开发送
            # 先发送response的头信息
            client_socket.send(response_headers.encode('utf-8'))
            # 再发送body
            client_socket.send(response_body)
            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(("", 7788))
        server_socket.listen(128)
        while True:
            client_socket, clien_cAddr = server_socket.accept()
            handle_client(client_socket)
    
    
    #这里配置服务器
    DOCUMENTS_ROOT = "./html"
    
    if __name__ == "__main__":
        main()

    服务器端

     

    客户端

     

    四.Web静态服务器-3-多进程

    #coding=utf-8
    import socket
    import re
    import multiprocessing
    
    
    class WSGIServer(object):
    
        def __init__(self, server_address):
            # 创建一个tcp套接字
            self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 允许立即使用上次绑定的port
            self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定
            self.listen_socket.bind(server_address)
            # 变为被动,并制定队列的长度
            self.listen_socket.listen(128)
    
        def serve_forever(self):
            "循环运行web服务器,等待客户端的链接并为客户端服务"
            while True:
                # 等待新客户端到来
                client_socket, client_address = self.listen_socket.accept()
                print(client_address)  # for test
                new_process = multiprocessing.Process(target=self.handleRequest, args=(client_socket,))
                new_process.start()
    
                # 因为子进程已经复制了父进程的套接字等资源,所以父进程调用close不会将他们对应的这个链接关闭的
                client_socket.close()
    
        def handleRequest(self, client_socket):
            "用一个新的进程,为一个客户端进行服务"
            recv_data = client_socket.recv(1024).decode('utf-8')
            print(recv_data)
            requestHeaderLines = recv_data.splitlines()
            for line in requestHeaderLines:
                print(line)
    
            request_line = requestHeaderLines[0]
            get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)
            print("file name is ===>%s" % get_file_name) # for test
    
            if get_file_name == "/":
                get_file_name = DOCUMENTS_ROOT + "/index.html"
            else:
                get_file_name = DOCUMENTS_ROOT + get_file_name
    
            print("file name is ===2>%s" % get_file_name) # for test
    
            try:
                f = open(get_file_name, "rb")
            except IOError:
                response_header = "HTTP/1.1 404 not found
    "
                response_header += "
    "
                response_body = "====sorry ,file not found===="
            else:
                response_header = "HTTP/1.1 200 OK
    "
                response_header += "
    "
                response_body = f.read()
                f.close()
            finally:
                client_socket.send(response_header.encode('utf-8'))
                client_socket.send(response_body)
                client_socket.close()
    
    
    # 设定服务器的端口
    SERVER_ADDR = (HOST, PORT) = "", 8888
    # 设置服务器服务静态资源时的路径
    DOCUMENTS_ROOT = "./html"
    
    
    def main():
        httpd = WSGIServer(SERVER_ADDR)
        print("web Server: Serving HTTP on port %d ...
    " % PORT)
        httpd.serve_forever()
    
    if __name__ == "__main__":
        main()

     

    五.Web静态服务器-4-多线程

    #coding=utf-8
    import socket
    import re
    import threading
    
    
    class WSGIServer(object):
    
        def __init__(self, server_address):
            # 创建一个tcp套接字
            self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 允许立即使用上次绑定的port
            self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定
            self.listen_socket.bind(server_address)
            # 变为被动,并制定队列的长度
            self.listen_socket.listen(128)
    
        def serve_forever(self):
            "循环运行web服务器,等待客户端的链接并为客户端服务"
            while True:
                # 等待新客户端到来
                client_socket, client_address = self.listen_socket.accept()
                print(client_address)
                new_process = threading.Thread(target=self.handleRequest, args=(client_socket,))
                new_process.start()
    
                # 因为线程是共享同一个套接字,所以主线程不能关闭,否则子线程就不能再使用这个套接字了
                # client_socket.close() 
    
        def handleRequest(self, client_socket):
            "用一个新的进程,为一个客户端进行服务"
            recv_data = client_socket.recv(1024).decode('utf-8')
            print(recv_data)
            requestHeaderLines = recv_data.splitlines()
            for line in requestHeaderLines:
                print(line)
    
            request_line = requestHeaderLines[0]
            get_file_name = re.match("[^/]+(/[^ ]*)", request_line).group(1)
            print("file name is ===>%s" % get_file_name) # for test
    
            if get_file_name == "/":
                get_file_name = DOCUMENTS_ROOT + "/index.html"
            else:
                get_file_name = DOCUMENTS_ROOT + get_file_name
    
            print("file name is ===2>%s" % get_file_name) # for test
    
            try:
                f = open(get_file_name, "rb")
            except IOError:
                response_header = "HTTP/1.1 404 not found
    "
                response_header += "
    "
                response_body = "====sorry ,file not found===="
            else:
                response_header = "HTTP/1.1 200 OK
    "
                response_header += "
    "
                response_body = f.read()
                f.close()
            finally:
                client_socket.send(response_header.encode('utf-8'))
                client_socket.send(response_body)
                client_socket.close()
    
    
    # 设定服务器的端口
    SERVER_ADDR = (HOST, PORT) = "", 8888
    # 设置服务器服务静态资源时的路径
    DOCUMENTS_ROOT = "./html"
    
    
    def main():
        httpd = WSGIServer(SERVER_ADDR)
        print("web Server: Serving HTTP on port %d ...
    " % PORT)
        httpd.serve_forever()
    
    if __name__ == "__main__":
        main()

     

  • 相关阅读:
    arcgis api 3.x for js 入门开发系列八聚合效果(附源码下载)
    arcgis api 3.x for js 入门开发系列七图层控制(附源码下载)
    arcgis api 3.x for js 入门开发系列六地图分屏对比(附源码下载)
    arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)
    arcgis api 3.x for js 入门开发系列四地图查询(附源码下载)
    Java里面获取当前服务器的IP地址
    Flutter at Google I/O 2018
    Modbus RTU 协议使用汇总
    plsql 创建表空间、用户、赋予权限
    Oracle:ODP.NET Managed 小试牛刀
  • 原文地址:https://www.cnblogs.com/Gaowaly/p/14485326.html
Copyright © 2011-2022 走看看