首页 > 开发 > Python > 正文

自己写一个Web服务器(1)

2016-04-14 21:14:14  来源:极客头条

  本文由码农网 – 王坚原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划
  有天一个女人出去散步,她经过建筑工地时看到三个人正在干活。她上去问第一个人,你在干什么呢?第一个人觉得这问题很恼人,厉声道,你看不到我在砌砖头吗?不甚满意的女人又问第二个人他在做什么。第二个人答道,我在砌一道砖墙。然后他看了下第一个人,喊道,嘿,你超过墙的长度了,把最后一块砖拿下来。女人还是不满意这个答案,他问第三个人。这个人呢,他一边看着天一边跟她说,我在建这世上从未有过的大教堂。在他抬头望天的时候,另两个人在为砖头的对错争吵不休。这人转向那两人说,伙计们,别为那一块砖当心了。这是个内墙,它会被粉刷没人能看到砖头的。把它放到另一层去吧。
  这个故事的寓意是,当你知道整个系统,了解不同组件如何相互配合(砖,墙壁,教堂),你能快速找到和快速解决问题(砖)。
  它对你从头开始建web服务器有什么启示呢?
  我相信要成为好的开发者,你必须对日常使用的软件底层系统有更好的理解,这包括编程语言,编译器和解释器,数据库和操作系统,web服务器和web框架。而为了能更好更深的理解这些系统,你必须从头开始重建他们,从一砖一瓦开始。
  老夫子有言曰:
  我听见了,我就忘了;
  
  我看见了,我就记得了;
  
  我做过了,我就理解了。
  
  我希望你同意这点,我们重新建构软件系统是学习他们怎样运作的好方法。
  在这个分为三部分的系列中,我将展示给你怎样搭建你自己的web服务器。我们开始吧。
  
  简而言之,这是一个运行在物理服务器上的网络服务器,它等待客户端发送的请求。当它收到一个请求,它会生成一个回复并传回到客户端。一个客户端和服务器的通信时通过HTTP协议实现。客户端可以是你的浏览器或任何其他应用HTTP的软件。
  一个简单的web服务器是什么样呢?这是我给出的答案。这个例子是用Python的,但即使你不懂Python也能通过下面的代码和解释理解这些概念。

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()
    request = client_connection.recv(1024)
    print request

    http_response = """\
HTTP/1.1 200 OK

Hello, World!
"""
    client_connection.sendall(http_response)
    client_connection.close()

  将上面代码保存为’websever1.py’,或者直接在GitHub上下载,然后想下面那样在命令行中运行
$ python webserver1.py Serving HTTP on port 8888 …   现在在浏览器地址栏输入http://localhost:8888/hello,按Enter键,奇迹就发生率。你应该能在浏览器看到“Hello,World”,如图所示:
  
  看到了吧。让我们来看看它到底是怎么做到的。
  从你键入的网址开始。它是一个URL下面是他的基本结构:
  
  这是你告诉浏览器找寻web服务器和连接的地址,也是你要获取的服务器上的页面(路径)。在你的浏览器发送HTTP请求前,它需要和web服务器建立一个TCP连接。然后它通过TCP连接发出HTTP请求,接着等待服务器返回一个HTTP响应。当浏览器收到响应后显示出来,在这个例子中它显示为“Hello World!”
  再来详细看看客户端和服务器怎样在HTTP请求响应之前建立TCP连接的。要做到这个,他们都用到了socket。不要直接用浏览器,你用telnet命令行来手动模拟浏览器。
  在同一台电脑上通过telnet会话运行web服务器,指定localhost和8888端口,按下Enter:
$ telnet localhost 8888 Trying 127.0.0.1 … Connected to localhost.   这样你就已经跟服务器建立了TCP连接,它运行在本地主机准备好发送和接收HTTP消息。下面的图片中你可以看到服务器必须经过一个标准的程序才可能接受一个新的TCP连接。
  
  在同一telnet会话中键入GET /hello HTTP/1.1,按下Enter:
$ telnet localhost 8888 Trying 127.0.0.1 … Connected to localhost. GET /hello HTTP/1.1 HTTP/1.1 200 OK Hello, World!   你刚才手动模拟了浏览器!你发送了一个HTTP请求然后收到了一个HTTP响应。这就是HTTP请求的基本结构:
  
  HTTP请求由HTTP方法(GET,因为我们要求服务器返回给我们写东西),路径/hello指向服务器的一个“页面”和协议版本。
  为了简单的找到我们的web服务器,这个例子完全无视上面的命令行。你可以用任何没意义的东西替换“GET /hello HTTP/1.1”,还是会得到“Hello, World!”
  一旦你输入了请求而且按下了Enter,客户端就像服务器发出了请求,服务器读取请求,打印它然后做出适当的响应。
这是HTTP响应,服务器传送到你的客户端的过程(这里是telnet):
  
  我们来剖析它。响应由一个状态行 HTTP/1.1 200 OK, 接着一个空白行,然后是HTTP响应主体。
  状态行 HTTP/1.1 200 OK, 由HTTP版本,HTTP状态代码和HTTP状态代码指示短语 OK 组成。当浏览器收到响应,它显示出响应主体,这就是为什么你在浏览器中看到“Hello, World!”
  这就是一个web服务器运行的基本模型。总结起来:web服务器创建一个监听socket持续地接受新的连接。客户端发起一个TCP连接,然后成功建立连接,客户端发出一个HTTP请求给服务器,服务器用HTTP响应来做回复,最后呈现给用户。建立TCP连接的过程中客户端和服务器都使用了 socket。
  现在你有了一个基本的服务器,可以在浏览器和其他HTTP客户端去测试。如你所见,你也可以做个人肉HTTP客户端,用telnet同时手动键入hTTP请求就行。
  那么问题来了:你怎么在你刚建立的web服务器上运行一个Django应用,Flask应用和Pyramid应用,如何不做任何改变而适应不同的web架构呢?
  我会在这个系列的第二篇告诉你。敬请关注!