《网络是怎样连接的》-读书笔记

《网络是怎样连接的》

作者 户根勤

image

探索之旅路线图

协议栈:网络控制软件;

  1. 位于浏览器应用和硬件网卡中间的数据转换层;
  2. 职责:负责将从浏览器接受到的数据打包,然后加上目的地址等控制信息;其中最先出场的是协议栈(网络控制软件叫作协议栈)。这个软件会将从浏览器接收到的消息打包,然后加上目的地址等控制信息

网卡:负责以太网或无线网络通信的硬件;
功能:将协议栈发送来的数据包转换为电信号,并通过网线发送出去——>网络;

网卡会将包转换为电信号并通过网线发送出去。这样一来,包就进入到网络之中了。

*路由器后面链接着互联网的入口——接入网;
而接入网连接到签约的网络运营商——接入点设备;

接入网连接到签约的网络运营商,并接入被称为接入点(Point of

浏览器在访问某个页面的数据时,请求可能直接会被WEB服务器侧的缓存服务器响应;

检查完之后,网络包接下来可能还会遇到缓存服务器。网页数据中有一部分是可以重复利用的,这些可以重复利用的数据就被保存在缓存服务器中。如果要访问的网页数据正好在缓存服务器中能够找到,那么就可以不用劳烦Web服务器,直接从缓存服务器读出数据。此外,在大型网站中,可能还会配备将消息分布到多台Web服务器上的负载均衡器,还有可能会使用通过分布在整个互联网中的缓存服务器来分发内容的服务。经过这些机制之后,网络包才会到达Web服务器

第1章 浏览器生成消息——探索浏览器内部

了解浏览器是如何将消息委托给操作系统发送给Web服务器的,有什么意义?

  1. 理解了向操作系统进行委托时的规则,我们就能明白做出某个委托时操作系统会给我们怎样的反馈,我们就了解了网络的潜力;为理解了向操作系统进行委托时的规则,我们就能够明白做出某个委托时操作系统会给我们怎样的反馈,这可以说是相当于具体地理解了网络的潜在能力。

1.1 生成HTTP请求消息

URL中没有携带文件名时,如何确认访问的文件?
服务器上可以事先设置好文件名省略时要访问的默认文件名,默认为index.html或default.html不过,没有文件名,服务器怎么知道要访问哪个文件呢?其实,我们会在服务器上事先设置好文件名省略时要访问的默认文件名。这个设置根据服务器不同而不同,大多数情况下是index.html或者default.htm之类的文件名

浏览器客户端向服务器发送请求的内容?
URI(对什么)+方法(进行怎样的操作)

客户端会向服务器发送请求消息(图1.4)。请求消息中包含的内容是“对什么”和“进行怎样的操作”两个部分。

*Web服务器响应消息的内容?

  1. web服务器完成自己的工作后,将结果存在响应消息中;
  2. 响应消息 = 开头(状态码) + body(响应内容)*

收到请求消息之后,Web服务器会对其中的内容进行解析,通过URI和方法来判断“对什么”“进行怎样的操作”,并根据这些要求来完成自己的工作,然后将结果存放在响应消息中。在响应消息的开头有一个状态码,它用来表示操作的执行结果是成功还是发生了错误。

*GET方法访问流程?

  1. 在request中写上GET方法;
  2. 在URI中写上存放网页数据的文件名“/dir1/file1.html”;
  3. Web服务器接收到消息后,打开file1.html文件并读出里面的数据,然后将数据存放在reponse中,并返回给客户端;*

最常用的一个就是GET方法了。一般当我们访问Web服务器获取网页数据时,使用的就是GET方法。所谓一般的访问过程大概就是这样的:首先,在请求消息中写上GET方法,然后在URI中写上存放网页数据的文件名“/dir1/file1.html”,这就表示我们需要获取/dir1/file1.html文件中的数据。当Web服务器收到消息后,会打开/dir1/file1.html文件并读取出里面的数据,然后将读出的数据存放到响应消息中,并返回给客户端。最后,客户端浏览器会收到这些数据并显示在屏幕上。

*POST使用场景?

  1. 需要在表单中填写数据,并将其发送给web服务器时;
    何为表单?
    ——带有输入框的网页,可以输入信息的部分都叫表单;
    POST方法访问流程?
  2. URI指定Web服务器中运行的一个应用程序文件名,如“index。php”;
  3. 请求消息中添加方法和URI,以及表单的数据;
  4. Web服务器收到消息后,会将表单的数据发送给URI指定的应用程序;
  5. Web服务器从应用程序接收输出的结果,把“状态码 + 输出结果”放到响应消息中返回给客户端;

还有一个经常使用的方法就是POST。我们在表单[插图]中填写数据并将其发送给Web服务器时就会使用这个方法。当我们在网上商城填写收货地址和姓名,或者是在网上填写问卷时,都会遇到带有输入框的网页,而这些可以输入信息的部分就是表单。

*1条请求消息中只能填写1个URI。如果需要获取多个文件,必须对每个文件单独发送1条请求;

  1. 因此如果请求的网页中包含图片时,服务器在响应的网页消息中相应位置嵌入表示图片文件标签的控制信息;
  2. 浏览器在显示文字时搜索相应的标签,当遇到图片相关的标签时,会在屏幕上预留出显示图片的空间;
  3. 浏览器判断所需的文件后,将文件或图片名填写在URI中,然后依次向Web服务器访问;(一个URI一个请求)*

判断所需的文件,然后获取这些文件并显示在屏幕上,这一系列工作的整体指挥也是浏览器的任务之一,而Web服务器却毫不知情。Web服务器完全不关心这4条请求获取的文件到底是1个网页上的还是不同网页上的,它的任务就是对每一条单独的请求返回1条响应而已。

1.2 向DNS服务器查询Web服务器的IP地址

向DNS查询IP地址操作——域名解析
本地执行查询、解析的客户端——Socket库提供的接口向DNS服务器发出查询,也就是向DNS服务器发送查询消息,并接收服务器返回的响应消息。换句话说,对于DNS服务器,我们的计算机上一定有相应的DNS客户端,而相当于DNS客户端的部分称为DNS解析器,或者简称解析器。通过DNS查询IP地址的操作称为域名解析,因此负责执行解析(resolution)这一操作的就叫解析器(resolver)了。

调用Socket库对应的解析器时,解析器会生成发送给DNS服务器的查询消息;
但是发送消息还是要委托给操作系统内部的协议栈来执行;

发送消息这个操作并不是由解析器自身来执行,而是要委托给操作系统内部的协议栈[插图]来执行。这是因为和浏览器一样,解析器本身也不具备使用网络收发数据的功能。

*关于协议栈:

  1. 这里可以看到协议栈数属于操作系统的一部分,它负责将应用层数据打包,加上IP地址消息头后,发送给硬件网卡;*

解析器调用协议栈后,控制流程会再次转移,协议栈会执行发送消息的操作,然后通过网卡将消息发送给DNS服务器(图1.12④⑤)。

DNS解析器委托协议栈发送查询域名IP请求,走UDP?

接下来,消息经过网络到达客户端,再经过协议栈被传递给解析器(图1.12⑦⑧),然后解析器读取出消息取出IP地址

1.3 全世界DNS服务器的大接力

域名存在层级关系,如“www.baidu.com”:

  1. 域名中被.分割的每一段都是一个域标识,而且越靠右域层级越高,如“baidu.com”域就是“www.baidu.com”的上级域;
  2. 因此,储存了域信息的DNS服务器也就有了上下级关系;
  3. 另外,需要注意的是一个域的信息是作为一个整体存放在DNS服务器中的,也可以是多个域信息存放在一个DNS服务器中;一个域的信息是作为一个整体存放在DNS服务器中的,不能将一个域拆开来存放在多台DNS服务器中。不过,DNS服务器和域之间的关系也并不总是一对一的,一台DNS服务器中也可以存放多个域的信息。

将管理下级域的DNS服务器IP地址注册到它的上级DNS服务器中:
这样就可以通过上级DNS服务器查询到下级DNS服务器的IP地址;

我们可以采用下面的办法。首先,将负责管理下级域的DNS服务器的IP地址注册到它们的上级DNS服务器中,然后上级DNS服务器的IP地址再注册到更上一级的DNS服务器中,以此类推

真是互联网中加快DNS服务器响应方式1:
一台DNS服务器管理多个上下级域;

在真实的互联网中,一台DNS服务器可以管理多个域的信息

真实互联网中加快DNS服务器响应方式2:
DNS服务器可以缓存之前查过的域名,从而减少查询时间;

因为DNS服务器有一个缓存[插图]功能,可以记住之前查询过的域名。如果要查询的域名和相关信息已经在缓存中,那么就可以直接返回响应,接下来的查询可以从缓存的位置开始向下进行。相比每次都从根域找起来说,缓存可以减少查询所需的时间。

*如何应对域名信息被缓存后,原来域名的注册信息发生了改变?

  1. DNS服务器会对缓存的信息设置一个有效期,如果过期,数据将会从缓存中删除;
  2. 对于那些没来得及删除的域名,DNS服务器在对查询响应时,会告知客户端结果是来自缓存还是管理该域名的服务器;*

那就是信息被缓存后,原本的注册信息可能会发生改变,这时缓存中的信息就有可能是不正确的。因此,DNS服务器中保存的信息都设置有一个有效期,当缓存中的信息超过有效期后,数据就会从缓存中删除。而且,在对查询进行响应时,DNS服务器也会告知客户端这一响应的结果是来自缓存中还是来自负责管理该域名的DNS服务器。

1.4 委托协议栈发送消息

套接字描述符:
用来识别同一台计算机内部的套接字;
端口号:
用来帮助识别不同计算机上的套接字;如果说描述符是用来在一台计算机内部识别套接字的机制,那么端口号就是用来让通信的另一方能够识别出套接字的机制[插图]。

*IP地址和端口号:客户端和服务器之间用来识别对方套接字的机制;

  1. 建立链接时,服务端端口是根据应用的种类事先规定好的,如web是80,电子邮件是25;
  2. 建立链接时,客户端在创建套接字时,协议栈会为这个套接字随便分配一个端口;*

,首先,客户端在创建套接字时,协议栈会为这个套接字随便分配一个端口号[插图]。接下来,当协议栈执行连接操作时,会将这个随便分配的端口号通知给服务器

*客户端是如何接受消息的?

  1. 调用read接口指定用户空间的内存地址作为接收缓冲区;
  2. 当Web服务器返回响应后,read委托协议栈完成接收消息;
  3. read将接收到的响应消息存放到接收缓冲区(用户空间);*

调用read时需要指定用于存放接收到的响应消息的内存地址,这一内存地址称为接收缓冲区。于是,当服务器返回响应消息时,read就会负责将接收到的响应消息存放到接收缓冲区中。由于接收缓冲区是一块位于应用程序内部的内存空间,因此当消息被存放到接收缓冲区中时,就相当于已经转交给了应用程序。

*浏览器向Web请求消息后,断开链接过程如下:

  1. 根据Web使用的HTTP协议规定,Web服务器应该主动执行断开操作;
  2. 断开操作传达到客户端后,客户都安的套接字也会执行断开;
  3. 浏览器调用read再次接收数据时,会被告知连接已断开,浏览器也会进入断开阶段;*

Web使用的HTTP协议规定,当Web服务器发送完响应消息之后,应该主动执行断开操作[插图],因此Web服务器会首先调用close来断开连接。断开操作传达到客户端之后,客户端的套接字也会进入断开阶段。接下来,当浏览器调用read执行接收数据操作时,read会告知浏览器收发数据操作已结束,连接已经断开。浏览器得知后,也会调用close进入断开阶段。

2.1 创建套接字

传输层:

  1. 对数据进行拆包,加上TCP消息头
  2. 涉及协议:TCP、UDP
    网络层:
  3. 对传输层下发的数据帧进行进一步切分成网络包,加上IP消息头
  4. 涉及协议:ICMP、ARP
  5. ICMP:用于告知网络包传送过程中产生的错误以及各种控制消息;
    在互联网上传送数据时,数据会被切分成一个一个的网络包[插图],而将网络包发送给通信对象的操作就是由IP来负责的。此外,IP中还包括ICMP[插图]协议和ARP[插图]协议。ICMP用于告知网络包传送过程中产生的错误以及各种控制消息,ARP用于根据IP地址查询相应的以太网MAC地址[插图]。

套接字概念:
协议栈内部有一块存放控制信息的内存空间,控制信息包括:通信对象的IP地址、端口号、通信操作的进行状态等;

在协议栈内部有一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的IP地址、端口号、通信操作的进行状态等。

2.3 收发数据

协议栈将应用层传来的数据存放在内部的发送缓冲区中?

  1. 协议栈不决定应用程序如何发来全部数据;
  2. 应用程序只需要指定发来的每一次数据的长度即可;协议栈并不是一收到数据就马上发送出去,而是会将数据存放在内部的发送缓冲区中,并等待应用程序的下一段数据。这样做是有道理的

MTU:以太网中每个网络包能容纳的最大数据长度:1500字节;
MSS:MTU是包含了头部的总长度,因此减去头部的长度才是一个网络包能容纳的最大数据长度;

MTU是包含头部的总长度,因此需要从MTU减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫作MSS[插图]

*应用程序的数据一般都比较大,因此TCP会按照网络包的大小对数据进行拆分:

  1. 网络包数据大小:MSS;
  2. 网络包内容大小:MTU = IP头部 + TCP头部 +MSS;
  3. 数据在传输层就已经按照MSS拆分了;*

应用程序的数据一般都比较大,因此TCP会按照网络包的大小对数据进行拆分。

*TCP头部中seq和ack的作用:
seq:

  1. TCP在拆分数据块时,会先计算好每一个数据块的头部相对域数据开始的偏移(字节);
    ack:
  2. 接收方在接收到每一包数据后,会根据整个网络包的长度 - 头部的长度 得到每一包接收数据的长度;
  3. 接收方会计算到目前为止接收到的数据总长度,然后将这个数据写入TCP头部的ACK中,发送给发送方,用以确认响应;

我们先来看一下确认的原理(图2.7)。首先,TCP模块在拆分数据时,会先算好每一块数据相当于从头开始的第几个字节,接下来在发送这一块数据时,将算好的字节数写在TCP头部中,“序号”字段就是派在这个用场上的

客户端发起建立连接时,将SYN置1发送给服务器过程中,同时对seq设置了随机的初始值?
——担心有人利用seq = 1代表建立连接发起攻击

大家应该还记得在我们刚才讲过的连接过程中,有一个将SYN控制位设为1并发送给服务器的操作,就是在这一步将序号的初始值告知对方的。实际上,在将SYN设为1的同时,还需要同时设置序号字段的值,而这里的值就代表序号的初始值[插图]。

TCP支持无限重传吗?
——不支持,TCP在几次重传无效后强制结束进程,并向应用程序报错;

因此TCP会在尝试几次重传无效之后强制结束通信,并向应用程序报错。

返回ACK号的等待时间——又叫超时时间

TCP采用了动态调整等待时间的方法,这个等待时间是根据ACK号返回所需的时间来判断的。具体来说,TCP会在发送数据的过程中持续测量ACK号的返回时间,如果ACK号返回变慢,则相应延长等待时间;相对地,如果ACK号马上就能返回,则相应缩短等待时间[插图]。

*滑动窗口的基本思路:

  1. 发送方在等待接收方ACK时,会在窗口期内继续发送seq;
  2. 接收方通过TCP头部中的窗口字段告诉发送方当前最多能接收的数据,然后发送方根据该值对发送操作进行控制;
  3. 上面提到的能够接收的最大数据量称为窗口大小;*

前面提到的能够接收的最大数据量称为窗口大小[插图],它是TCP调优参数中非常有名的一个。

接送放更新窗口大小的时机?
当接收方将数据传递给应用程序,导致接收缓冲区剩余容量增加时;

因此,更新窗口大小的时机应该是接收方从缓冲区中取出数据传递给应用程序的时候。这个操作是接收方应用程序发出请求时才会进行的,而发送方不知道什么时候会进行这样的操作,因此当接收方将数据传递给应用程序,导致接收缓冲区剩余容量增加时,就需要告知发送方,这就是更新窗口大小的时机。

*接收方在发送ACK号和窗口更新时,等待一段时间,解决了哪些问题?

  1. 把ACK号和窗口更新放在一个包里发送,减少了包数量;
  2. 当需要连续发送多个ACK号时,只需要发送最后一个ACK号(ACK号——告诉发送方目前已接收数据的最后位置);
  3. 当需要连续发送多个窗口更新时,最发送最终的缓冲区剩余空间即可;*

接收方在发送ACK号和窗口更新时,并不会马上把包发送出去,而是会等待一段时间,在这个过程中很有可能会出现其他的通知操作,这样就可以把两种通知合并在一个包里面发送了。

2.4 从服务器断开并删除套接字

收发数据结束的时间点
——应用程序判断所有数据都已经发送完毕的时候;
——此时数据发送完毕的一方会发起断开过程;收发数据结束的时间点应该是应用程序判断所有数据都已经发送完毕的时候。这时,数据发送完毕的一方会发起断开过程,但不同的应用程序会选择不同的断开时

服务端发送数据完毕,调用close关闭连接后,
如果应用程序调用read来读取数据,此时协议栈会告知应用程序来自服务器的数据已经全部收到了,客户端会发送一FIN包给服务端;

过了一会儿,应用程序就会调用read来读取数据[插图]。这时,协议栈不会向应用程序传递数据[插图],而是会告知应用程序(浏览器)来自服务器的数据已经全部收到了

*通信结束后,立即删除套接字?

  1. 例客户端发送数据结束后,如果关闭连接过程给服务端的最后一个ack丢失了,理论上客户端发送完FIN包后,就可以删除套接字;
  2. 但是后面服务端会重发FIN包,如果此时客户端新建立的连接复用了上一次的套接字,那么新的连接将会被错误关闭;
  3. 综上,客户端发送完数据后,需要等待一段时间(time_wait),等待服务端重传结束,再删除套接字;*

和服务器的通信结束之后,用来通信的套接字也就不会再使用了,这时我们就可以删除这个套接字了。不过,套接字并不会立即被删除,而是会等待一段时间之后再被删除。

2.5 IP与以太网的包收发操作

TCP数据段是如何通过路由器和集线器发送到对方的?

  1. 路由器——是按照IP规则传输包的设备,它作用于IP头部,IP协议根据目的地址决定下一跳路由的IP地址;
  2. 集线器——是按照以太网规则传输包的设备,它作用于MAC头部,IP层找到下一跳路由的IP后,IP协议会查找该路由器的MAC地址,写入MAC头部后委托以太网协议将包传输过去;实际上,集线器是按照以太网规则传输包的设备,而路由器是按照IP规则传输包的设备,因此我们也可以作如下理解。

*TCP发送方如何确认发送地址?

  1. 对于多网卡计算机,每个网卡对应一个IP地址,实际连接不同的路由网络;
  2. route表里,Network Destination可以匹配接收方IP,对应的interface就是网卡IP,Gateway对应下一跳路由IP;
    所以,根据接受发IP匹配到的interface就是发送IP;*

这样一来,我们就可以判断出应该使用哪块网卡来发送包了,然后就可以在IP头部的发送方IP地址中填上这块网卡对应的IP地址。

IP层将TCP报文分别搭上IP头部、MAC头部:
——这样网卡只需要将大号的包发送出去,使得网卡能够适配IP以外的其他类型的包;

将MAC头部加在IP头部的前面,整个包就完成了。到这里为止,整个打包的工作是由IP模块负责的。有人认为,MAC头部是以太网需要的内容,并不属于IP的职责范围,但从现实来看,让IP负责整个打包工作是有利的。

2021.12.05——书签;

2.5.6 以太网的基本知识

*如何定义以太网:

  1. MAC头部的接收方MAC地址——代表目的地;
  2. MAC头部的发送方MAC地址——识别发送方;
  3. MAC头部的以太网类型识别包内容;
    具有上面这3个性质的网络就是以太网;*

尽管以太网经历了数次变迁,但其基本的3个性质至今仍未改变,即将包发送到MAC头部的接收方MAC地址代表的目的地,用发送方MAC地址识别发送方,用以太类型识别包的内容。因此,大家可以认为具备这3个性质的网络就是以太网[插图]。

*以太网给IP包添加上报头、起始帧分界符和FCS:

  1. 报头——按照时钟信号周期排列的数字信息,用来获取时钟信号周期;
  2. 起始帧分界符——固定顺序配列的比特序列,用来标记包起始位置;
  3. 末尾的FCS(帧校验序列)——类似CRC校验码;*

加上报头、起始帧分界符和FCS之后,我们就可以将包通过网线发送出去了(图2.24)。发送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工[插图]模式。

以太网的包收发操作1:
使用集线器的半双工模式:
为了避免信号碰撞,网卡中的PHY模块会等待一段时间后,尝试重新发送信号;
等待时间——根据MAC地址生成一个随机数计算出来的;

为了通知其他设备当前线路已发生碰撞,还会发送一段时间的阻塞信号[插图],然后所有的发送操作会全部停止。等待一段时间之后,网络中的设备会尝试重新发送信号。但如果所有设备的等待时间都相同,那肯定还会发生碰撞,因此必须让等待的时间相互错开。具体来说,等待时间是根据MAC地址生成一个随机数计算出来的。

*以太网的包手法操作2:
当网络拥塞时,信号碰撞的概率增加,重试发送可能又会与另外一台设备的发送操作冲突:
解决办法:

  1. 每发生碰撞,等待时间延长一倍,最多重试10次;
  2. 最后报告通信错误;*

当网络拥塞时,发生碰撞的可能性就会提高,重试发送的时候可能又会和另外一台设备的发送操作冲突,这时会将等待时间延长一倍,然后再次重试。以此类推,每次发生碰撞就将等待时间延长一倍,最多重试10次,如果还是不行就报告通信错误。

*网卡接收返回包后的处理:
以集线器的半双工模式为例,

  1. PHY模块会将信号转换成通用格式并发给MAC模块;
  2. MAC模块从头开始将信号转换为数字信息,并放到换缓冲区;
  3. 如果MAC头部的MAC地址与本网卡一致,则网卡向CPU发送中断信号;
  4. CPU调用网卡驱动中断处理程序,从网卡的缓冲区中取出收到的包,根据MAC头部中的以太类型,将数据包交给上层协议栈;*

网卡将包转换为电信号并发送出去的过程到这里就结束了,既然讲到了以太网的工作方式,那我们不妨继续看看接收网络包时的操作过程[插图]。在使用集线器的半双工模式以太网中,一台设备发送的信号会到达连接在集线器上的所有设备。这意味着无论是不是发给自己的信号都会通过接收线路传进来,因此接收操作的第一步就是不管三七二十一把这些信号全都收进来再说。

IP层接收到数据包操作1:
如果IP层发现数据包中的接收方IP不是自己,则IP模块会通过ICMP消息将错误告知发送方;

如果接收方IP地址不是自己的地址,那一定是发生了什么错误。客户端计算机不负责对包进行转发,因此不应该收到不是发给自己的包[插图]。当发生这样的错误时,IP模块会通过ICMP消息将错误告知发送方(图2.1)。ICMP规定了各种类型的消息,如表2.4所示。当我们遇到这个错误时,IP模块会通过表2.4中的Destination unreachable消息通知对方。从这张表的内容

3.1 信号在网线和集线器中传输

以太网的基本架构:
——即将数据包发到所有设备,然后由设备根据接收方MAC地址来判断应该接收哪些包;
——忠实体现:集线器以太网的基本架构[插图]就是将包发到所有的设备,然后由设备根据接收方MAC地址来判断应该接收哪些包,而集线器就是这一架构的忠实体现,它就是负责按照以太网的基本架构将信号广播出去。下面来看看它的工作方式。

集线器会将信号发送给所有连接在它上面的线路;

接下来,信号从所有接口流出,到达连接在集线器上的所有设备。然后,这些设备在收到信号之后会通过MAC头部中的接收方MAC地址判断是不是发给自己的,如果是发给自己的就接受,否则就忽略[插图]。这样,网络包就能够到达指定MAC地址的接收方了。

3.2 交换机的包转发操作

交换机的包转发操作1:

  1. 交换机上的每个端口就相当于一块网卡,但不具备MAC地址,它接收所有包并存放到缓冲区中;网线接口和后面的电路部分加在一起称为一个端口,也就是说交换机的一个端口就相当于计算机上的一块网卡[插图]。但交换机的工作方式和网卡有一点不同。网卡本身具有MAC地址,并通过核对收到的包的接收方MAC地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方MAC地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,交换机的端口不具有MAC地址[插图]。

*交换机的包转发操作2:

  1. 交换机内部有一张MAC地址表:{接收方的MAC地址, 端口号};
  2. 这样,当数据包通过集线器流入交换机的某个端口后,存入缓冲区,然后查询以太网头部的接收方MAC地址,根据MAC地址表确认将数据包转发到对应端口上;
  3. 2步中交换机内部通过交换电路将包转发到对应端口上;*

交换机根据MAC地址表查找MAC地址,然后将信号发送到相应的端口。

*交换机的包转发操作3:
如果接收方的MAC地址比较特殊:

  1. 交换机查询地址后的目标端口与包的源端口是同一个端口,则交换机直接丢弃该包;
  2. 交换机通过MAC地址表查不到指定MAC地址,则交换机将包转发到除了源端口外的所有端口上;*

此外,如果接收方MAC地址是一个广播地址[插图],那么交换机会将包发送到除源端口之外的所有端口。

3.3 路由器的包转发操作

路由器的包转发操作1:
路由器——基于IP设计,位于网络层;
交换机——基于以太网设计,位于数据链路层;。因为路由器是基于IP设计的,而交换机是基于以太网设计的[插图]。IP和以太网的区别在很多地方都会碰到,我们稍后再具体讲,现在先来看看路由器的概况。

*路由器的包转发操作2:

  1. 路由器分为转发模块和端口模块;
  2. 路由器的端口不仅具有MAC地址,而且还有IP地址,因此路由器查询转发目标后,会由相应端口作为发送方将以太网包发送出去;
  3. 而交换机MAC地址和IP地址都不具备,它只是通过查询MAC地址表,将数据包转发到对应端口;*

端口还具有IP地址,从这个意义上来说,它和计算机的网卡是一样的。当转发包时,首先路由器端口会接收发给自己的以太网包[插图],然后查询转发目标,再由相应的端口作为发送方将以太网包发送出去。这一点和交换机是不同的,交换机只是将进来的包转发出去而已,它自己并不会成为发送方或者接收方。

*路由器的包转发操作3:

  1. 路由表最左侧时记录的是接收方的网络号信息,即路由器会忽略主机号,只匹配网络号;
  2. 路由器通过目标地址个子网掩码匹配到某条记录后,根据路由表中的网关和接口列,将网络包交给指定的网络接口,并转发到网关列中指定的IP地址;*

路由器会忽略主机号,只匹配网络号。

*路由器的包转发操作4:

  1. 通过路由器转发的网络包,其接收方MAC地址为路由器端口的MAC地址;(MAC头部的作用就是将包送达路由器)
  2. 但是网络包中的接收方IP地址仍为目标IP地址,而非当前路由器IP;*

通过路由器转发的网络包,其接收方MAC地址为路由器端口的MAC地址。

*路由器的包转发操作5:

  1. 跟据路由表匹配目标地址时,如果匹配到多条记录,按照网络号越长 > 跃点计数越小的顺序匹配记录;
  2. 如果根据路由表的目标地址网络号和子网掩码没有匹配到对应记录,则路由器需要直接丢弃该网络包;(对比交换机,由于连接的设备规模小,可以选择将包发送到所有端口上)*

如果只有几千台设备,遇到不知道应该转发到哪里的包,交换机可以将包发送到所有的端口上,虽然这个方法很简单粗暴,但不会引发什么问题。然而,路由器工作的网络环境就是互联网,它的规模是远远大于以太网的,全世界所有的设备都连接在互联网上,而且规模还在持续扩大,未来的互联网里到底会有多少设备,我们谁都说不准。在如此庞大的网络中,如果将不知道应该转发到哪里的包发送到整个网络上,那就会产生大量的网络包,造成网络拥塞。因此,路由器遇到不知道该转发到哪里的包,就会直接丢弃。

*路由器和交换机的关系:

  1. IP(路由器)负责将包发送给目标对象这一整体过程;
  2. 以太网(交换机)负责将包传输到下一个路由器;*

到这里我们已经梳理了路由器与交换机之间的关系。简单来说,IP (路由器)负责将包发送给通信对象这一整体过程,而其中将包传输到下一个路由器的过程则是由以太网(交换机)来负责的。

3.4 路由器的附加功能

局域网内的私有地址设备如何范围跟互联网?

  1. 原始数据包先通过地址转换设备,地址转换设备会将发送的私有IP地址即端口号改写为本设备的互联网接入端口地址和随机端口号;
  2. 服务器返回应答后,再经地址转换设备,通过地址转换表中的端口号确定源私有IP地址和端口号;通过这样的机制,具有私有地址的设备就也可以访问互联网了。从互联网一端来看,实际的通信对象是地址转换设备(这里指的是路由器)。

*路由器的附加功能之一:地址转换

  1. 在公司内网和互联网之间有一层地址转换设备——路由器;
  2. 内网访问互联网的包,经过地址转换设备时,发送方私有地址会被改成地址转换设备自身的地址,端口号随机分配,这一对地址映射关系会被记录下来;
  3. 后续互联网给地址转换设备应答时,地址转换设备根据接收方端口号查表获取转换后的私有地址和私有端口号,最后将包转发给私有内网地址;*

对于从公司内网访问互联网的包,即便其发送方私有地址和端口号没有保存在对应表中也是可以正常转发的,因为用来改写的公有地址就是地址转换设备自身的地址,而端口号只要随便选一个空闲的端口就可以了,这些都可以由地址转换设备自行判断。

如何实现互联网访问公司内网?
1、对于公司内网,就算地址转换设备中没有对应内网IP的记录,也可以现场通过地址转换设备访问;
2、但是反过来,如果地址转换设备中没有对应{端口, 内网IP+端口}记录,则不能从互联网访问公司内网;

不过,有时候我们希望能够从互联网访问公司内网,这需要进行一些设置才能实现。之所以无法从互联网访问内网,是因为对应表里没有相应的记录,那么我们只要事先手动添加这样的记录就可以了(图3.19)

所谓的地址转换设备,其实就是在局域网的公有网络和私有网络中间添加的一个路由器;

只要事先将地址和端口的关联信息添加到地址转换设备的对应表中,就可以从互联网访问内网中的设备了。

4.2 光纤接入网(FTTH)

单模光纤技术:

  1. 直径位于8~10um之间;
  2. 该直径是按照只允许相位一致的最小角度的光进入而设计的;
  3. 这样只有角度最小且前进的光和反射回来的光相位保持一致,该角度入射光既满足全反射又满足相位相同干涉增强的原理,它才能在光纤种传导下去;
  4. 入射角越小,意味着传输时反射次数越少,衰减越少,即单模光纤传输距离远比多模光纤长;根据纤芯直径,光纤可以划分成几种类型,大体上包括较细的单模光纤(8~10 μm)和较粗的多模光纤(50 μm或62.5 μm)。单模光纤的纤芯很细,只有入射角很小的光线才能进入,因此在能够保持相位一致的角度中,只有角度最小的光线能进入光纤。反过来可以说,单模光纤的纤芯直径就是按照只允许相位一致的最小角度的光进入而设计的。

*单模光纤 vs 多模光纤:

  1. 多模光纤对光源和光敏元件性能要求较低,成本低;
  2. 信号失真与光在纤芯传导时反射的次数相关,多模光纤较大的入射角,意味着反射次数较多,会导致信号失真较大;*

相对地,单模光纤则不会出现这样的问题。因为在纤芯传导的光线只有一条,不会因为行进距离的差异产生时间差,所以即便光纤很长,也不会产生严重的失真。光纤的最大长度也是由上述性质决定的。单模光纤的失真小,可以比多模光纤更长,因此多模光纤主要用于一座建筑物里面的连接,单模光纤则用于距离较远的建筑物之间的连接。FTTH属于后者,因此主要使用单模光纤。

*波分复用技术:

  1. 同一条光纤传输多个不同波长的光信号;
  2. 接收端可以通过棱镜原理将波长不同的光信号分离;*

前往互联网的上行光信号和前往用户的下行光信号在光纤中混合在一起,信号会变得无法识别,因此我们需要对它们进行区分,办法是上行和下行信号采用不同波长的光。波长不同的光混合后可通过棱镜原理进行分离,因此光纤中的上行和下行信号即便混合起来也可以识别。像这样在一条光纤中使用不同的波长传输多个光信号的方式叫作波分复用。

4.3 接入网中使用的PPP和隧道

ADSL和FTTH使用PPP消息的目的:

  1. 通过PPP消息验证用户登录时使用的用户名和密码;
  2. 也可以通过用户名切换不同的运营商;ADSL和FTTH中,用户和BAS之间是通过电缆或光纤固定连接在一起的,因此没有必要验证用户身份,所以实际上并不需要PPP的所有这些功能。然而,通过用户名和密码登录的步骤可以根据用户名来切换不同的运营商,这很方便[插图]。因此,接入运营商在ADSL和FTTH中一般也会使用PPP[插图]。

*隧道 vs TCP链接:

  1. 将包含头部在内的整个包从隧道一头扔进去,该包将原封不动地从隧道另一头出来;
  2. BAS : 用户认证窗口 + 传输网络包地隧道;*

BAS除了作为用户认证的窗口之外,还可以使用隧道方式来传输网络包。所谓隧道,就类似于套接字之间建立的TCP连接。在TCP连接中,我们从一侧的出口(套接字)放入数据,数据就会原封不动地从另一个出口出来,隧道也是如此。也就是说,我们将包含头部在内的整个包从隧道的一头扔进去,这个包就会原封不动地从隧道的另一头出来,就好像在网络中挖了一条地道,网络包从这个地道里穿过去一样。

*BSA在校验用户密码后,

  1. 会向用户下发TCP/IP配置信息,包括:
    1.1 分配给上网设备地IP地址;(分配给路由器BAS端的端口)
    1.2 DNS服务器地IP地址;
    1.3 默认网关地IP地址;(存储于接入互联网端路由器地路由表中)*

在校验密码之后BAS如何向用户下发TCP/IP配置信息。这里下发的配置信息包括分配给上网设备的IP地址[插图]、DNS服务器的IP地址以及默认网关的IP地址。当使用路由器连接互联网时,路由器会根据这些信息配置自身的参数。这样一来,路由器的BAS端的端口就有了公有地址[插图],路由表中也配置好了默认网关[插图],接下来就可以将包转发到互联网中了。

*1. 用户侧接入互联网路由器 -发送用户名和密码-到BAS窗口-转发给认证服务器;

  1. 认证服务器认证过后,将TCP/IP配置信息通过BAS下发给接入路由器;
  2. 接入路由器将网路包加上PPP和PPPoE头部后-转发给BAS;
  3. BAS将MAC头部和PPPoE头部去掉后-取出PPP消息-通过隧道-发送到运营商的路由器;*

接下来,网络包会到达BAS,而BAS会将MAC头部和PPPoE头部去掉,取出PPP头部以及后面的部分,然后通过隧道机制将包发送出去。最后,PPP包会沿隧道到达另一端的出口,也就是网络运营商的路由器。

5.2 防火墙的结构和原理

防火墙的基本原理防火墙的基本思路刚才已经介绍过了,即只允许发往特定服务器中的特定应用程序的包通过,然后屏蔽其他的包。

防火墙的基本原理

防火墙的基本思路刚才已经介绍过了,即只允许发往特定服务器中的特定应用程序的包通过,然后屏蔽其他的包。

*网络包能否通过包过滤规则?

  1. 根据网络包中的包头部控制位,去包过滤规则中一条一条匹配,如果匹配到,且规则允许通过,则,通过,否则,阻止;
  2. 如果所有包过滤规则都没匹配到,那包默认通过。*

从互联网访问Web服务器时,第一个包是接收方为Web服务器,符合图5.2表中的第1行,因此允许通过。第二个包的发送方是Web服务器,但TCP控制位的规则与第二行不匹配[插图],因此符合第三行的规则,允许通过。随后的所有包要么符合第一行,要么符合第三行,因此从互联网访问Web服务器的所有包都会被允许通过。

*内置包过滤规则的普通路由器作为防火墙有以下缺点:

  1. 当规则比较复杂时,普通路由器难以维护;
  2. 路由器的内存一般比较小,难以记录被阻止的那些网络包信息;*

实际上,在防火墙允许包通过之后,就没有什么特别的机制了,因此包过滤并不是防火墙专用的一种特殊机制,而是应该看作在路由器的包转发功能基础上附加的一种功能。只不过当判断规则比较复杂时,通过路由器的命令难以维护这些规则,而且对阻止的包进行记录对于路由器来说负担也比较大,因此才出现了专用的硬件和软件。如果规则不复杂,也不需要记录日志,那么用内置包过滤功能的普通路由器来充当防火墙也是可以的。

5.4 使用缓存服务器分担负载

缓存服务器工作要点:

  1. 客户端发送req->缓存服务器->web服务器;其中缓存服务器会以客户端的身份发送请求,标识就是在http头部添加“via 缓存服务器地址”;
  2. 如果缓存服务器本地有缓存,则在http头部添加“if-modified-since:上次保存的时间”,用来询问web服务器这段时间书记是否变更?
  3. web服务器给缓存服务器应答后,缓存服务器将对应缓存内容应答给客户端;Web服务器会根据If-Modified-Since的值与服务器上的页面数据的最后更新时间进行比较,如果在指定时间内数据没有变化,就会返回一个像图5.7(b)一样的表示没有变化的响应消息(图5.5(b)③)。这时,Web服务器只要查询一下数据的最后更新时间就好了,比返回页面数据的负担要小一些。

所谓代理机制:
应该就是指将DNS服务器中的客户端IP地址替换为代理对象IP地址;

实际上,缓存服务器使用的代理机制最早就是放在客户端一侧的,这才是代理的原型,称为正向代理[插图](forward proxy)。

*客户端正向代理 vs 防火墙:

  1. 正向代理可以看到内网用户发出的请求内容,在次基础上可以决策是否允许访问互联网;
  2. 而防火墙只是根据IP地址,端口号,以后包头部控制位决定是否允许/禁止包通过;*

此外,由于代理在转发过程中可以查看请求的内容,所以可以根据内容判断是否允许访问。也就是说,通过代理可以禁止员工访问危险的网站,或者是与工作内容无关的网站。包过滤方式的防火墙只能根据IP地址和端口号进行判断,因此无法实现这一目的。

*正向代理 vs服务端缓存服务器:

  1. 正向代理也即客户端缓存服务器,它支持内网用户访问任意web服务器;
  2. 服务端缓存服务器,只支持指定的web服务器;*

使用正向代理时,URI部分为http://...这样的完整网址,因此可以根据这个网址来转发,不需要像服务器端的缓存服务器一样实现设置好转发目标Web服务器,而且可以发给任意Web服务器。而服务器端的缓存服务器只能向事先设置好的目标进行转发,这就是两者不同的地方。

5.5 内容分发服务

正向代理 vs 反向代理:

  1. 缓存服务器放在服务端时,反向代理,通过本地缓存可以降低web服务的压力;
  2. 缓存服务器在客户端时,正向代理,查看用户请求决策转发,可以降低互联网的流量;缓存服务器部署在服务器端还是客户端,其效果是有差别的。如图5.10(a)所示,当缓存服务器放在服务器端时,可以减轻Web服务器的负载,但无法减少互联网中的流量。这一点上,将缓存服务器放在客户端更有效(图5.10(b))。

*客户端如何访问最近的缓存服务器?
重定向法:

  1. 将重定向服务器注册到Web服务器端的DNS服务器上;
  2. 客户端访问Web服务器后,再次将HTTP请求发送到重定向服务器上;
  3. 重定向服务器上收集了来自各路由器的路由信息,根据这些信息能找到距客户端最近的缓存服务器;
  4. 重定向服务器给客户端响应,将缓存服务器的地址放到Location中;
  5. 客户端重新访问指定的缓存服务器;*

当使用重定向告知客户端最近的缓存服务器时,首先需要将重定向服务器注册到Web服务器端的DNS服务器上。这样一来,客户端会将HTTP请求消息发送到重定向服务器上。重定向服务器和刚才一种方法中的DNS服务器一样,收集了来自各个路由器的路由信息,并根据这些信息找到最近的缓存服务器,然后将缓存服务器的地址放到Location字段中返回响应。这样,客户端就会重新去访问指定的缓存服务器了

*使用缓存服务器的注意事项:

  1. 客户端的第一次访问是无效的;
  2. 客户端的后续访问都需要向原始服务器查询数据有没有发生变化?
    2.1 客户端req->缓存服务器->原始Web服务器,缓存服务器会模拟客户端身份向服务端发起请求,通过HTTP头部 "via 缓存服务器IP"标识;
    2.2 如果缓存服务器本地有缓存,则在http头部添加"if modified since 上次保存时间"向Web服务器查询数据是否有变化?没有,则通知缓存服务器直接应答缓存数据;有,则Web服务器给缓存服务器应答,缓存服务器再给客户端应答;*

不过,这种方法对于第一次访问是无效的,而且后面的每次访问都需要向原始服务器查询数据有没有发生变化,如果遇到网络拥塞,就会使响应时间恶化。

6.1 服务器概览

服务端如何同时支持多个套接字链接?

  1. 服务器端每接收一个新的链接后,协议栈会为原等待链接的套接字复制一个新的副本,然后让客户端链接到这个新的副本套接字上;
  2. 新的副本套接字保持了源服务器端的IP和端口号,为了区分新老套接字,统一使用服务端IP+服务端端口号+客户端IP+客户端端口号来标识套接字;
  3. 考虑到套接字刚创建时还没建立链接,上述4种信息不全,协议栈使用了描述符来标识套接字;说句题外话,既然通过客户端IP地址、客户端端口号、服务器IP地址、服务器端口号这4种信息可以确定某个套接字,那么要指代某个套接字时用这4种信息就好了,为什么还要使用描述符呢?这个问题很好,不过我们无法用上面4种信息来代替描述符。原因是,在套接字刚刚创建好,还没有建立连接的状态下,这4种信息是不全的。此外,为了指代一个套接字,使用一种信息(描述符)比使用4种信息要简单。出于上面两个原因,应用程序和协议栈之间是使用描述符来指代套接字的。

6.2 服务器的接收操作

如果服务器端将接收到的电信号还原成数字信息后:

  1. 根据校验公式计算刚刚接收到的数字信息,得到FCS值;
  2. 若该FCS值与数据包末尾的FCS值不一致,则说明接收数据错误,包丢弃;FCS值是在发送时根据转换成电信号之前的数字信息进行计算得到的,因此如果根据信号还原出的数字信息与发送前的信息一致,则计算出的FCS也应该与包末尾的FCS一致。如果两者不一致,则可能是因为噪声等影响导致信号失真,数据产生了错误,这时接收的包是无效的,因此需要丢弃

*当服务端接收到客户数据包时:

  1. 网卡的MAC模块将网络包还原为数字信息;(根据报头\起始帧分界符)
  2. 根据包末尾的FCS校验数据包错误;
  3. 若FCS一致,还要检查MAC头部的接收方MAC地址;(以太网基本工作方式:将数据广播到整个网络上,只有指定的接收者才接收数据,否则丢弃);
  4. 将还原后的数字信息保存到网卡内部的缓冲区中;*

到这里,接收信号并还原成数字信息的操作就完成了,还原后的数字信息被保存在网卡内部的缓冲区中。上面这些操作都是由网卡的MAC模块[插图]来完成的。网卡的MAC模块将网络包从信号还原为数字信息,校验FCS并存入缓冲区。

*网卡还原数据包后,

  1. 通过中断通知CPU数据包到达,然后CPU会从网卡缓冲区中将接收到的包读取出来,并根据MAC头部判断协议类型,并将包交给相应的协议栈;*

协议栈的IP模块会检查IP头部,(1)判断是不是发给自己的;(2)判断网络包是否经过分片;(3)将包转交给TCP模块或UDP模块。

*服务端收到客户端发起链接的包时:

  1. 服务端检查包的接收方端口号,确认在该端口上有处于等待链接状态的套接字;
  2. 服务端为当前套接字分配新的副本,并将发送方IP+端口号+序号初始值+窗口大小填入;
  3. 然后服务端生成ACK号+本段的序号初始值+接收缓冲区窗口大小,将这些信息生成TCP头部,委托IP模块发送给客户端;*

如果存在等待连接的套接字,则为这个套接字复制一个新的副本,并将发送方IP地址、端口号、序号初始值、窗口大小等必要的参数写入这个套接字中,同时分配用于发送缓冲区和接收缓冲区的内存空间。然后生成代表接收确认的ACK号,用于从服务器向客户端发送数据的序号初始值,表示接收缓冲区剩余容量的窗口大小,并用这些信息生成TCP头部,委托IP模块发送给客户端[插图]。

*进入数据收发阶段后,TCP模块的操作流程:

  1. TCP根据发送方Ip+发送端口+接收方IP+接收方端口号匹配套接字;
  2. 根据套接字保存的上一个序号+数据长度计算下一个序号,然后检查收到包的TCP头部中的序号是否匹配?匹配,则从包中提取数据放到接收缓冲区中;
  3. TCP根据接收包的序号和数据长度计算处ACK号,然后委托IP模块发送给客户端;*

收到的数据块进入接收缓冲区,意味着数据包接收的操作告一段落了。接下来,应用程序会调用Socket库的read(图6.7③)来获取收到的数据,这时数据会被转交给应用程序

6.3 Web服务器程序解释请求消息并作出响应

web服务器收到请求消息后,

  1. web服务器先检查URI指定的文件名,判断是否时CGI程序?
    1.1 如果是文件,则web服务器先查询URI对应虚拟目录与实际目录的关系,转换成实际文件名后,才读取数据并返回;
    1.2 如果服务器通过文件扩展名等信息判断是程序,则服务器先从请求消息中取出参数,然后委托操作系统运行程序;
    1.2.1 程序输出的数据会先返回给服务器;
    1.2.2 web服务器将输出的数据嵌入到HTML中,直接作为响应消息返回给客户端;Web服务器程序在组装网络包、还原数据之后,会运行其中指定的程序(实际是委托操作系统来运行),然后将数据传递给已运行的程序。接下来,运行的程序收到数据后会进行一系列处理,并将输出的数据返回给Web服务器

*使用一般请求访问设置了用户名和密码保护的页面时:

  1. web服务器会返回一个要求提供用户名和密码的头部字段(WWW-Authenticate)消息;
  2. 用户填完名称和密码后,客户端会在HTTP消息中添加包含用户名和密码的头部字段(Authorization);*

当访问设置了用户名和密码保护的页面时,需要在HTTP请求消息中添加包含用户名和密码的头部字段(Authorization)。否则,Web服务器不会返回请求的页面内容,而是会返回一个要求提供用户名和密码的头部字段(WWW-Authenticate)消息。


使用 小悦记 导出 | 2022年2月23日

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容