一天,一位正在走路的女士偶然路过一个建筑工地,看到三个工人在工作。她问第一个人:“你在做什么?”“你在干什么?”第二个人回答:“我在砌砖墙。”然后他转向第一个人说:“嘿,你把墙盖得太高了。把那块砖拿下来!”但是,女人还是不满意这个答案,又问了第三个人同样的问题。第三个人抬头看着天空,对她说:“我正在建造世界上最大的教堂。”他一开口,第一个人和第二个人正在为刚刚放错的砖头争论不休。他转头对两人道:“那块砖不用担心,这堵墙是室内的,里面会灌满水泥,不会有人看到的,你们去盖下一层吧。”这个故事告诉我们:如果你能理解整个系统是如何构建的,系统的各个部分是如何组合在一起的(砖头、墙壁和整个大教堂),你就能更快地定位和解决问题(那块错误的砖块)。如果你想从头开始创建一个网络服务器,你需要做什么?我相信如果你想成为一个更好的开发者,你必须对你每天使用的软件系统的内部结构有更深入的了解,包括编程语言、编译器和解释器、数据库和操作系统、网络服务器和网络框架。而且,为了更好更深入地理解这些系统,您必须从头开始,一砖一瓦地重建系统。荀子曾用这几个词来表达这种思想:“闻而忘之”。“我听到了,我忘记了。”“我看到了,我记住了。”“知道我知道,我就明白。”我希望你现在意识到,重建一个软件系统以了解它是如何工作的是个好主意。在这个由三部分组成的系列中,我将教您如何构建自己的Web服务器。开始吧~我们先从第一个问题开始:什么是Web服务器?简而言之,它就是一个运行在物理服务器(啊,服务器中的服务器)上的web服务器,等待客户端向它发送请求。当它收到请求时,它会生成响应并将其发送回客户端。客户端和服务器通过HTTP协议相互通信。客户端可以是您的浏览器,也可以是任何其他使用HTTP协议的软件。最简单的Web服务器实现应该是什么样的?这里我给出我的实现。这个例子是用Python写的,即使你没有听说过Python(这是一门超级好用的语言,试试看吧!),你应该可以从代码和注释中理解思路:importsocketHOST,PORT='',8888listen_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'ServingHTTPonport%s...'%PORTwhileTrue:client_connection,client_address=listen_socket.accept()request=client_connection.recv(1024)printrequesthttp_response="""\HTTP/1.1200OKHello,World!"""client_connection.sendall(http_response)client_connection.close()把上面的代码保存为webserver1.py,或者直接从GitHub上下载这个文件。然后,在命令行上运行该程序。像这样:$pythonwebserver1.pyServingHTTPonport8888...现在,在您的Web浏览器的地址栏中输入URL:http://localhost:8888/hello,按回车键,然后见证魔法。你应该看到“你好,世界!”显示在您的浏览器中,如下图所示:Seriously,giveitatry。你做实验的时候我在等你。完毕?好的!现在让我们谈谈它的实际工作原理。首先,我们从您刚刚输入的网址开始。它叫做URL,它的基本结构是这样的:URL是一个网络服务器的地址,浏览器用它来查找和连接网络服务器,并返回上面的内容给你。在您的浏览器可以发送HTTP请求之前,它需要与Web服务器建立TCP连接。然后它在TCP连接上发送HTTP请求并等待来自服务器的HTTP响应。当您的浏览器收到响应时,它会显示其内容,在上面的示例中,它会显示“Hello,World!”。让我们进一步探讨客户端在发送HTTP请求之前与服务器建立TCP连接的过程。为了建立连接,他们使用所谓的“套接字”。我们现在不直接使用浏览器发送请求,而是在命令行中使用telnet来人为模拟这个过程。在运行Web服务器的计算机上,在命令行设置telnet会话,指定本地域名,使用端口8888,然后按Enter:$telnetlocalhost8888Trying127.0.0.1...Connectedtolocalhost。此时,您已经连接到本地主机上的您的服务器建立了TCP连接。在下图中,您可以看到一个服务器从头开始并能够建立TCP连接的基本过程。在同一telnet会话中,键入GET/helloHTTP/1.1,然后按Enter:$telnetlocalhost8888Trying127.0.0.1…Connectedtolocalhost.GET/helloHTTP/1.1HTTP/1.1200OK你好,世界!您只是手动模拟了您的浏览器(工作)!您发送了一个HTTP请求并收到了一个HTTP响应。下面是HTTP请求的基本结构:HTTP请求的第一行由三部分组成:HTTP方法(GET,因为我们希望我们的服务器返回一些内容),以及指示所需页面的路径/hello,以及协议版本。为了简单起见,我们刚刚搭建的web服务器完全忽略了上面请求的内容。你也可以尝试输入一些垃圾而不是“GET/helloHTTP/1.1”,但你仍然会得到一个“Hello,World!”回复。输入请求行并按回车键后,客户端将请求发送到服务器;服务器读取请求行并返回相应的HTTP响应。这是服务器返回给客户端(上例中的telnet)的响应:让我们解析它。响应由三部分组成:状态行HTTP/1.1200OK,后跟空行,然后是响应文本。HTTP响应HTTP/1.1200OK的状态行包含HTTP版本号、HTTP状态代码和HTTP状态短语“OK”。当浏览器收到响应时,它会显示响应文本,这就是您看到“Hello,World!”的原因。在浏览器中。以上就是一个web服务器的基本工作模型。总结一下:web服务器创建一个监听状态的socket,循环接受新的连接。客户端建立TCP连接成功后,会向服务器发送HTTP请求,然后服务器响应HTTP响应,客户端将HTTP响应内容显示给用户。为了建立TCP连接,客户端和服务器都使用套接字。现在您了解了Web服务器工作原理的基础知识,您可以尝试使用浏览器或其他HTTP客户端。如果您尝试过并观看过,您应该也能够使用telnet,手工制作HTTP请求,并成为一个“类人”HTTP客户端。现在留下一个小问题:“如何在不对程序做任何改动的情况下,适配刚刚搭建好的Web服务器上的Django、Flask或Pyramid应用程序?”我将在本系列的第二部分中对其进行详细解释。敬请关注。顺便说一下,我正在写一本书,叫做《搭个 Web 服务器:从头开始》。这本书解释了如何从头开始编写一个基本的Web服务器,比本文更详细。通过订阅邮件列表,您可以获得本书的最新进展,以及发布日期。如何搭建Web服务器(二)
