自己动手开发网络服务器(一)

这个读书笔记是学习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/,可以看到浏览器中反馈的信息如下

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

我们来看你所输入的网络地址。它的名字叫URL(Uniform 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连接,客户端和服务器都使用了套接字。

原文地址:https://www.cnblogs.com/zhanghongfeng/p/8451968.html

时间: 2024-11-03 21:59:28

自己动手开发网络服务器(一)的相关文章

iOS开发网络篇—搭建本地服务器

iOS开发网络篇—搭建本地服务器 一.简单说明 说明:提前下载好相关软件,且安装目录最好安装在全英文路径下.如果路径有中文名,那么可能会出现一些莫名其妙的问题. 提示:提前准备好的软件 apache-tomcat-6.0.41.tar eclipse-jee-kepler-SR2-macosx-cocoa-x86_64.tar.gz jdk-8u5-macosx-x64.dmg 二.安装和配置本地服务器环境(java)步骤: (1)在文档路径下,新建一个文件夹(NetWord),解压eclips

iOS开发网络篇—发送json数据给服务器以及多值参数

iOS开发网络篇—发送json数据给服务器以及多值参数 一.发送JSON数据给服务器 发送JSON数据给服务器的步骤: (1)一定要使用POST请求 (2)设置请求头 (3)设置JSON数据为请求体 代码示例: 1 #import "YYViewController.h" 2 3 @interface YYViewController () 4 5 @end 6 7 @implementation YYViewController 8 9 - (void)viewDidLoad 10

Python服务器开发 -- 网络基础

Python服务器开发 -- 网络基础 网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层.HTTP是高层协议,而TCP/IP是个协议集,包过许多的子协议.... 网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. HTTP是高层协议,而TCP/IP是个协议集,包过许多的子协议.包括:传输层的 FTP,UDP,TCP协议等,网络层的ip协议等,高层协议如HTTP,telnet协议等,HTTP是TCP/IP的一个子协议. socket是对TCP/I

iOS开发网络篇—发送GET和POST请求(使用NSURLSession)

iOS开发网络篇—发送GET和POST请求(使用NSURLSession) 说明: 1)该文主要介绍如何使用NSURLSession来发送GET请求和POST请求 2)本文将不再讲解NSURLConnection的使用,如有需要了解NSURLConnection如何发送请求. 详细信息,请参考:http://www.cnblogs.com/wendingding/p/3813706.html 3)本文示例代码发送的请求均为http请求,已经对info.plist文件进行配置. 如何配置,请参考:

iOS开发网络篇—网络编程基础

iOS开发网络篇—网络编程基础 一.为什么要学习网络编程 1.简单说明 在移动互联网时代,移动应用的特征有: (1)几乎所有应用都需要用到网络,比如QQ.微博.网易新闻.优酷.百度地图 (2)只有通过网络跟外界进行数据交互.数据更新,应用才能保持新鲜.活力 (3)如果没有了网络,也就缺少了数据变化,无论外观多么华丽,终将变成一潭死水 移动网络应用 = 良好的UI + 良好的用户体验 + 实时更新的数据 新闻:网易新闻.新浪新闻.搜狐新闻.腾讯新闻 视频:优酷.百度视频.搜狐视频.爱奇艺视频 音乐

自己动手打造WEB服务器 Windows + Apache + PHP + MySQL

XWAMP并不打算打造一个多功能,零配置,方便调试的工具.XWAMP只是把原程序简单的组合在一起,利用CMD命令控制,真正的绿色版,只为了多学习点Windows + Apache + PHP + MySQL相关的知识. 官方网站:http://www.xwamp.com/. Windows下的Apache+Mysql/MariaDB+Perl/PHP/Python,一组常用来搭建动态网站或者服务器的开源软件,本身都是各自独立的程序,但是因为常被放在一起使用,拥有了越来越高的兼容度,共同组成了一个

iOS开发网络篇—大文件的多线程断点下载(转)

http://www.cnblogs.com/wendingding/p/3947550.html iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分). 项目中用到的主要

iOS开发网络篇—GET请求和POST请求

iOS开发网络篇—GET请求和POST请求 一.GET请求和POST请求简单说明 创建GET请求 1 // 1.设置请求路径 2 NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/MJServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text]; 3 NSURL *url=[NSURL URLWithString:u

iOS开发网络篇—NSURLConnection基本使用

iOS开发网络篇—NSURLConnection基本使用 一.NSURLConnection的常用类 (1)NSURL:请求地址 (2)NSURLRequest:封装一个请求,保存发给服务器的全部数据,包括一个NSURL对象,请求方法.请求头.请求体.... (3)NSMutableURLRequest:NSURLRequest的子类 (4)NSURLConnection:负责发送请求,建立客户端和服务器的连接.发送NSURLRequest的数据给服务器,并收集来自服务器的响应数据 二.NSUR