zoukankan      html  css  js  c++  java
  • 自己动手开发网络服务器(一)

    这个读书笔记是学习Let’s Build A Web Server系列。原文地址:
    https://ruslanspivak.com/lsbaws-part1/  包含3个部分
     
    python有很多web框架,django,flask,tornodo,web.py。我们可以基于这些框架来开发我们的网站。这些框架其实是给我们封装了很多底层的实现。比如WSGI,模板映射等功能。为了更好的理解和开发web服务器。我们必须了解这些实现的细节和原理,这样才能更好的理解和优化
    作者还专门通过写了一个小故事来说明这个道理:
    有一天,一位女士散步时经过一个工地,看见有三个工人在干活。她问第一个人,“你在做什么?”第一个人有点不高兴,吼道“难道你看不出来我在砌砖吗?”女士对这个答案并不满意,接着问第二个人他在做什么。第二个人回答道,“我正在建造一堵砖墙。”然后,他转向第一个人,说道:“嘿,你砌的砖已经超过墙高了。你得把最后一块砖拿下来。”女士对这个答案还是不满意,她接着问第三个人他在做什么。第三个人抬头看着天空,对她说:“我在建造这个世界上有史以来最大的教堂”。就在他望着天空出神的时候,另外两个人已经开始争吵多出的那块砖。他慢慢转向前两个人,说道:“兄弟们,别管那块砖了。这是一堵内墙,之后还会被刷上石灰的,没人会注意到这块砖。接着砌下层吧。”
    这个故事的寓意在于,当你掌握了整个系统的设计,明白不同的组件是以何种方式组合在一起的(砖块,墙,教堂)时候,你就能够更快地发现并解决问题(多出的砖块)。
    但是,这个故事与从头开发一个网络服务器有什么关系呢?
    在我看来,要成为一名更优秀的程序员,你必须更好地理解自己日常使用的软件系统,而这就包括了编程语言、编译器、解释器、数据库与操作系统、网络服务器和网络开发框架。而要想更好、更深刻地理解这些系统,你必须从头重新开发这些系统,一步一个脚印地重来一遍。
     
    我们每天都在用浏览器上网。当我们打开浏览器输入网址的时候,浏览器上会显示网页的内容,在这个过程中,上网是如何发生的呢。作者用了下面这个图展示了一个简单的数据流程图。

    简单点说,在web server上搭建了第一个网络服务器,永久的等待客户发起的请求,当服务器收到请求后,它会产生响应并反馈给客户端。客户端和服务器之间的通信,是以HTTP协议进行的,浏览器就是收到这些HTTP响应数据后,解析完毕然后展示出来

     当然整个交互过程比这张图要复杂得多。更加复杂的过程可以参考下面这个图。这其中包含了TCP三次握手。HTTP消息交互流程等。比上面的图更加细化了一些

    那么我们继续往下刨根问底的问下,这些数据是如何组装和解析的呢。这就需要了解TCP/IP协议结构,HTTP协议就是基于TCP/IP协议模型来传输消息的。协议架构如下。HTTP协议就位于应用层。

    有了上面的层次结构,再来看下数据的组装以及协议,在每个协议层都解析出各自的头以及信息。并把剩下的消息递交给上层继续解析。这就好比我们现在的包裹快递,在每个投递站打上各自的投递信息。最终达到的时候我们就可以查到一个完整的包裹传递路线。

    当然如果还要更具体的话,还有ARP查询过程,DNS查询过程等等。这些就不在这里一一介绍了。前面介绍了整个HTTP协议报文的传输以及结构,下面就来看下如何实现具体的实例。

    import socket

     

    HOST,PORT='',8888

     

    listen_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    listen_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

    listen_socket.bind((HOST,PORT))

    listen_socket.listen(1)

     

    print 'Serving HTTP on port %s....' % PORT

     

    while True:

        client_connection,client_address=listen_socket.accept()

        print 'client_connection is %s,the connection from %s' % (client_connection,client_address)

        request=client_connection.recv(1024)

        print request

        http_response="""

        HTTP/1.1 200 OK

        

        Hello world!"""

        client_connection.sendall(http_response)

        client_connection.close()

     

    运行该文件并在浏览器中输入http://localhost:8888/,可以看到浏览器中反馈的信息如下

    我们来分析下背后运行的原理:

    我们来看你所输入的网络地址。它的名字叫URLUniform Resource Locator,统一资源定位符),其基本结构如下:

    通过URL,你告诉了浏览器它所需要发现并连接的网络服务器地址,以及获取服务器上的页面路径。不过在浏览器发送HTTP请求之前,它首先要与目标网络服务器建立TCP连接。然后,浏览器再通过TCP连接发送HTTP请求至服务器,并等待服务器返回HTTP响应。当浏览器收到响应的时候,就会在页面上显示响应的内容,而在上面的例子中,浏览器显示的就是“Hello, World!”这句话。

    那么,在客户端发送请求、服务器返回响应之前,二者究竟是如何建立起TCP连接的呢?要建立起TCP连接,服务器和客户端都使用了所谓的套接字(socket我们来看下socket的使用过程。整个过程可以参考下图:

    (1)首先是初始话了HOST地址和端口,这里如果不指明地址的话。那么就是默认的本地地址127.0.0.1

    HOST,PORT='',8888

    listen_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    socket.socket(socket.AF_INET,socket.SOCK_STREAM)生成一个socket实例,里面使用的参数分别是使用的地址族,套接字类型,协议编号。详细的参数定义参考下表

    (2)设置socket选项setsockopt(level,optname,value)level一般都是socket.SOL_SOCKET。optname的参数定义参考下表

    listen_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

    (3) 绑定IP与端口到套接字并开始监听

    listen_socket.bind((HOST,PORT))

    listen_socket.listen(1)

    (4) 收到客户端发来的数据

    client_connection,client_address=listen_socket.accept()

    accept接受连接并返回(conn,address,其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。接收TCP 客户的连接(阻塞式)等待连接的到来

    request=client_connection.recv(1024)

    接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略

    我们通过打印可以看到接受的内容。request就是整个客户端发送的数据,包含请求方式:

    GET /hello HTTP/1.1, 以及对应的头消息。

    /usr/bin/python2.7 /home/zhf/py_prj/web_server/webserver1.py

    Serving HTTP on port 8888....

    client_connection is <socket._socketobject object at 0x7fc30eae36e0>,the connection from ('127.0.0.1', 38412)

    request的打印内容:

    GET /hello HTTP/1.1

    Host: localhost:8888

    User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

    Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2

    Accept-Encoding: gzip, deflate

    Cookie: Pycharm-35cf7131=702c9c37-5112-486b-a03b-3cebfe91963b

    Connection: keep-alive

    Upgrade-Insecure-Requests: 1

    下面这幅图展示的是HTTP请求的基本结构:

    (5) 发送反馈

    http_response="""

        HTTP/1.1 200 OK

        

        Hello world!"""

        client_connection.sendall(http_response)

    sendall将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。下面这张图显示的是服务器返回至客户端的HTTP响应详情:

    响应中包含了状态行HTTP/1.1 200 OK,之后是必须的空行,然后是HTTP响应的正文。

    响应的状态行HTTP/1.1 200 OK中,包含了HTTP版本、HTTP状态码以及与状态码相对应的原因短语(Reason Phrase)。浏览器收到响应之后,会显示响应的正文,这就是为什么你会在浏览器中看到“Hello, World!”这句话。

    这就是网络服务器基本的工作原理了。简单回顾一下:网络服务器首先创建一个侦听套接字(listening socket),并开启一个永续循环接收新连接;客户端启动一个与服务器的TCP连接,成功建立连接之后,向服务器发送HTTP请求,之后服务器返回HTTP响应。要建立TCP连接,客户端和服务器都使用了套接字。

  • 相关阅读:
    January 25th, 2018 Week 04th Thursday
    January 24th, 2018 Week 04th Wednesday
    January 23rd, 2018 Week 04th Tuesday
    January 22nd, 2018 Week 04th Monday
    January 21st, 2018 Week 3rd Sunday
    January 20th, 2018 Week 3rd Saturday
    January 19th, 2018 Week 3rd Friday
    January 18th, 2018 Week 03rd Thursday
    January 17th, 2018 Week 03rd Wednesday
    January 16th, 2018 Week 03rd Tuesday
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/8451968.html
Copyright © 2011-2022 走看看