运输层

注:本文的图片均来源于谢希仁《计算机网络》第六版的课件PPT

1.重点内容

(1)运输层为相互通信的应用进程提供逻辑通信

(2)端口和套接字的意义

(3)无连接的UDP的特点

(4)面向连接的TCP的特点

(5)在不可靠的网络上实现可靠传输的工作原理,停止等待协议和ARQ协议

(6)TCP的滑动窗口、流量控制、拥塞控制和连接管理

2.运输层协议概述

在之前我们说过了,网络层是为在网络中的主机之间提供逻辑通信,但严格的说起来主机之间的通信其实是主机中的应用进程互相通信。而我们这一部分要讲的运输层,就是为应用进程之间提供端到端的逻辑通信。此外,运输层还要对收到的报文进行差错检测,对比网络层中,IP数据报中只对首部进行检验,不对数据部分进行检验。

运输层向上面的应用层提供通信服务,是通信部分的最高层,同时也是用户功能中的最低层。它向高层用户评比了下面网络核心的细节,使应用进程看见的好像就是在两个运输层实体之间有一条端到端的逻辑通信信道。

运输层协议与网络层协议的区别如图1所示。

图1

运输层有两个主要协议:

(1)用户数据报协议UDP,通信时传输的数据单位叫做UDP用户数据报

(2)传输控制协议TCP,传输的数据单位叫做TCP报文段

TCP:提供面向连接的服务。传输数据之前需要建立连接,传输结束需要释放连接。尽管下面的网络是不可靠的,但是使用TCP协议使得这种逻辑信道相当于是一条全双工的可靠信道。TCP不提供广播或多播服务。

UDP:无连接的协议。在传送数据之前不需要建立连接,目的主机收到UDP报文后也不需要给出任何确认。使用UDP协议的逻辑信道仍然是一条不可靠信道。UDP支持一对一,一对多,多对一,多对多通信。

应用层协议主要使用的运输层协议:

TCP:SMTP,TELNET,HTTP,FTP

UDP:DNS,TFTP,RIP,DHCP,SNMP,NFS,IGMP

运输层在从IP层收到数据之后,要将数据交给指定的应用进程。但是要怎么交给指定的应用进程呢?在计算机中,进程的创建和撤销都是动态的,发送方几乎无法识别目的机器上的进程。另外,我们往往需要利用目的主机提供的功能来识别重点,而不需要知道实现这个功能的进程。解决这个问题的办法就是在运输层使用协议端口号

虽然,在通信过程中,通信的终点是应用进程,但是我们可以把端口想象成是通信的重点。在把报文交给目的主机的某一个合适的目的端口后,剩下的工作就由TCP来完成。

端口用一个16位端口号来进行标志。端口号只具有本地意义。

运输层的端口号有三类:

(1)熟知端口:0~1023

(2)登记端口号:1024~49151,使用这个范围的端口号必须在IANA登记,避免重复。

(3)客户端口号(短暂端口号):49152~65535,留给客户进程选择暂时使用。

3.用户数据报协议UDP

UDP只在IP的数据报服务至上增加了很少一点功能,这就是端口的功能和差错检测的功能。虽然UDP提供的是不可靠服务,但是在某些方面有其特殊的优点。

UDP的特点:

(1)UDP是无连接的,即发送数据前不需要建立连接,减少了开销和发送数据之前的时延。

(2)UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的连接状态表。

(3)UDP是面向报文的。UDP对应用层交下来的报文,既不合并也不拆分,而是保留这些报文的边界。接收方的UDP对IP层交上来的UDP数据报,去除首部后原封不动的交付给应用层。也就是说UDP一次交付一个完整的报文,由应用程序来选择合适大小的报文的长度。

(4)UDP没有拥塞控制。网络的拥塞不会使源主机的发送速率降低,对于某些实时应用很重要。

(5)UDP支持一对一、一对多、多对一以及多对多的交互通信。

(6)UDP的首部开销小,只有8个字节,而TCP的首部有20个字节。

用户数据报UDP有两个字段:数据字段和首部字段。

UDP的首部格式:如图2所示。

图2

其中伪首部只是在计算检验和的时候使用,并不向上交付也不向下传送。计算UDP检验和的例子如图3所示。

图3

当运输层从IP层收到UDP数据报时,就根据首部中的目的端口,将UDP数据报通过相应的端口上交到应用进程,如图4所示。如果接收方的UDP发现收到的报文中目的端口号不正确,就丢弃该报文,并有ICMP发送“端口不可达”差错报文给发送方。

图4

4.传输控制协议TCP概述

由于TCP协议比较复杂,先简要概述一下TCP,之后再详细讨论TCP的可靠传输、流量控制和拥塞控制等问题。

TCP最主要的特点:

(1)TCP是面向连接的运输层协议。这就意味着,应用程序在使用TCP协议之前,必须建立TCP连接,使用完TCP协议之后,必须释放TCP连接。

(2)每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。

(3)TCP提供可靠交付的服务。

(4)TCP提供全双工通信。TCP的两端都设有发送缓存和接收缓存,用来临时存放双向通信时的数据。发送数据时,应用程序在把数据传送给发送缓存之后就可以做其他事情了。接收数据时,TCP将收到的数据放入缓存,应用程序在合适的时候读取缓存中的数据。

(5)面向字节流。TCP中的流指的是流入到进程或从进程流出的字节序列。TCP面向流的概念如图5所示。

图5

图5表明,TCP并不关心应用程序一次把多长的报文发送到TCP的发送缓存中,而是根据接收方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节。如果应用进程传送到发送缓存的数据块太长,TCP可以把它划分短一些再传送,如果太短,可以等待积累到足够多的字节后再构成报文段发送出去。

TCP的连接:

TCP把连接作为最基本的抽象。每一条TCP连接有两个端点,这个端点叫做套接字(socket)或插口。套接字由端口号拼接到IP地址组成:

                                                socket=(IP地址:端口号)

每一条TCP连接唯一的被通信两端的两个端点所确定:

                                TCP连接::={socket1,socket2}={(IP1:port1),(IP2:port2)}

5.可靠传输的工作原理

TCP提供的是可靠的交付服务,但是TCP报文需要向下交付给IP层,而IP层提供的是尽最大努力服务,不保证可靠性。因此TCP必须采取适当的措施来保证通信的可靠。

理想的传输条件:

(1)传输信道不产生差错

(2)不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据。

然而实际中的网络并不具备这样的理想条件,但可以使用一些可靠的传输协议,使得传送出现差错时让发送方重传出错的数据,在接收方来不及处理收到的数据时使发送方降低发送数据的速度。这样便能达到可靠通信。

停止等待协议:

停止等待就是每发送完一个分组就停止发送,等待对方确认,收到确认后再发送下一个分组。以A为发送方,B为接收方,仅考虑A向B发送数据,停止等待协议有以下三种情况:

(1)无差错情况

如图6(a)所示,A在发送完分组后,等待接收B的确认分组,B在接收到分组后就向A发送确认分组,A在收到确认分组后再发送下一个分组。

(2)出现差错

如图6(b)所示,分组在传输过程中出现差错。B在接收M1时出现差错,丢弃M1,什么也不做。A在一定时间后仍未接收到M1的确认分组,认为M1分组丢失,重传M1分组(超时重传)。要实现超时重传,需要设定一个超时计时器。A为每一个已发送的分组都设置了一个超时计时器,如果在计时到期之前收到了相应确认,就撤销计时器,否则进行超时重传。

这里需要注意三点:

(i)A在发送完一个分组后,必须暂时保留已发送分组的副本,以备超时重传使用。

(ii)分组和确认分组都必须进行编号。

(iii)超时计时器设置的重传时间应当比数据在分组传输的平均往返时间更长一些。

图6

(3)确认丢失和确认迟到

如图7(a)所示,B接收到M1分组后向A发送的M1确认分组中途丢失,这时A在计时器到期之前未收到M1分组确认,因此超时重传。这个时候B端已经有M1分组了,直接丢弃重复的M1分组,重传M1确认分组。

如图7(b)所示,B在接收到M1分组后向A发送的M1确认分组因为某种原因迟到了,而A在计时器到期之前还是未收到M1分组确认,因此超时重传。这个时候B丢弃重复的M1分组,重传确认分组,A在收到迟到的确认分组之后什么也不做,直接丢弃重复的确认。

使用这样的确认和重传机制,便可在不可靠的网络上实现可靠传输,将这种可靠传输协议称为自动重传请求ARQ

图7

停止等待协议的优点很明显:简单。缺点也很明显:信道利用率太低。信道利用率如图8所示:

因此,信道利用率:                    U=TD/(TD+RTT+TA)

图8

因此,为了提供信道利用率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输。流水线传输使得发送方可以连续发送多个分组,不必每发完一个分组就停下来等待对方确认。如图9所示:

图9

连续ARQ协议:

连续ARQ协议的工作原理如图10所示。这里发送窗口的意义是位于发送窗口内的5个分组都可以连续发送出去,而不需要等待对方的确认。接收方一般采用累积确认的方式,不必对每一个分组发送确认,对按序到达的最后一个分组发送确认。

累积确认的优点是容易实现,即便确认丢失也不必重传。但缺点是不能向发送方反映出接收方已经正确收到的所有分组的信息。

Go-back-N:表示需要再退回来重传已发送的N个分组。

图10

6.TCP报文段的首部格式

TCP报文段分为首部和数据两部分,TCP的全部功能都体现在它首部中各字段的作用,TCP报文段首部的前20个字节是固定的。TCP报文段的格式如图11所示:

图11

(1)源端口和目的端口:TCP的分用功能也是通过端口实现的。

(2)序号:指的是本报文段所发送的数据的第一个字节的序号。

(3)确认号:期望收到对方的下一个报文的第一个数据字节的序号。

(4)数据偏移:指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。

(5)保留:保留为今后使用,但目前应置为0。

(6)紧急URG:URG=1时表明紧急指针字段有效。告诉系统,此报文段中有紧急数据,应当尽快传送。

(7)确认ACK:当ACK=1时,确认号字段才有效。

(8)推送PSH:接收端的TCP在收到PSH=1的报文时,尽快交付给接收应用进程。

(9)复位RST:RST=1时,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立运输连接。

(10)同步SYN:SYN=1表示这是一个连接请求或连接接受报文。

(11)终止FIN:用来释放一个连接。FIN=1时表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。

(12)窗口:用来让对方设置发送窗口的依据。

(13)检验和:同UDP一样,计算时需要在TCP报文段的前面加上12字节的伪首部。

(14)紧急指针:指出本报文段中紧急数据共有多少字节。

(15)选项:长度可变。最大报文段长度MSS,窗口扩大选项,时间戳选项,选择确认选项。

(16)填充:为了使整个首部长度是4字节的整数倍。

7.TCP可靠传输的实现

在讲述了可靠传输的工作原理以及TCP报文段的首部格式之后,我们接下来就要介绍TCP可靠传输的实现。假定数据传输只在一个方向传输(方便讨论问题),即A为发送方,B为接收方。

以字节为单位的滑动窗口:

TCP的滑动窗口是以字节为单位的。假定A收到了B发来的确认报文段,其中窗口是20,确认号是31。根据这两个数据,A构造出自己的发送窗口,如图12所示:

图12

先来讨论A的发送窗口。发送窗口表示:在未收到B的确认的情况下,A可以连续的把窗口内的数据都发送出去。凡是已发送出去但未收到确认的数据都要暂时保留,以备超时重传时使用。

发送窗口的位置由前沿和后沿的位置共同确定。后沿的变化有两种:前移(收到了新的确认),不动(未收到新的确认)。前沿通常是向前移动的,但也可能不动,这对应着两种情况:未收到新的确认,对方通知的窗口大小不变;收到了新的确认,但通知的窗口减小。对于前沿,理论上是可以后移,但TCP标准强烈不赞成这么做,因为可能会引发错误。

A发送数据时的示意如图13所示,可以看到,描述一个发送窗口的状态需要三个指针:P1,P2,P3。指针都指向字节的序号。

图13

再来看一下B的接收窗口。B的接收窗口大小是20,到30号为止的数据都已经发送给确认并已交付主机,因此可不必保留。接收窗口内的序号是允许接收的数据。B只能对按序到达的数据中的最高序号给出确认。

假定B收到了并把序号为31~33的数据交付主机,然后B删除了这些数据,接收窗口向前移动3个序号,同时给A发送确认,确认号是34,如图14所示。未按序到达的数据先暂存在接收窗口中。而A在经过一段时间后仍未收到B的确认,那么就会启动超时重传。

图14

之前说到过应用进程将数据传送到TCP的发送缓存,从TCP的接收缓存中读取数据。接下来进一步讨论发送缓存和接收缓存。

发送缓存和接收缓存:

先来看一下发送缓存,如图15所示,发送缓存用来暂时存放:

(1)发送应用程序传送给发送方TCP准备传送的数据

(2)TCP已发送但尚未收到确认的数据

图15

接下来再看一下接收缓存,如图16所示,接收缓存用来暂时存放:

(1)按序到达的、但尚未被应用程序读取的数据

(2)未按序到达的数据

图16

根据以上的讨论,还应注意以下三点:

(1)在同一时刻,A的发送窗口并不总是和B的接收窗口一样大。

(2)对于未按序到达的数据并无明确规定。通常对未按序到达的数据时先暂时存放在接收窗口,等待缺失的字节收到后,再按序交付上层的应用进程。

(3)TCP要求接收方必须有累积确认的功能,这样可以减小传输开销。

超时重传时间的选择:

之前说到,A在规定时间内未收到B发来的确认就要启动超时重传。重传时间的选择是TCP最复杂的问题。

TCP采用一种自适应的算法,记录一个报文段的发出时间,以及收到相应确认的时间,两者之差就是报文段的往返时间RTT。TCP保留了RTT的一个加权平均往返时间RTTS。

RTTS的计算:第一次测量到RTT样本时,RTTS值就取为所测量到的RTT样本值,之后的RTTS按照下式进行计算:(a的推荐值为0.125)

                                        新的RTTS=(1-a)*旧的RTTS + a*新的RTT样本

超时重传时间RTO按照下式计算:

                                                    RTO=RTTS + 4*RTTD

RTT的偏差加权平均值RTTD的计算:第一次测量时,取为RTT样本值的一半,以后的测量中,按下式进行计算:

                                新的RTTD=(1-b)*旧的RTTD + b*| RTTS - 新的RTT样本 |

公式虽然简单,但是实现起来相当复杂,如下图所示。发出一个报文段,但未收到确认,于是超时重传,那么接下来收到的确认是对哪一个报文段的确认呢?

图17

针对这个问题,有一种Karn算法:在计算加权平均RTTS时,只要报文段重传了,就不采用其往返时间样本。但是这种算法会导致超时重传时间无法更新。

于是提出了修正的Karn算法:报文每重传一次,就把超时重传时间RTO增大一些,典型做法是取为旧的重传时间的2倍。当不再发送报文重传时,再根据上述的公式进行计算RTTS和RTO。

选择确认SACK:

之前说到有一些数据未按序到达接收方,被暂时保存下来了。那么有没有办法只传送缺少的数据而不重传已经正确到达接收方的数据呢?选择确认SACK就是一种可行的办法。

下面以一个例子来说明SACK的工作原理。TCP的接收方在接收对方发过来的数据字节流的序号不连续,形成一些不连续的字节快,如图18所示。从图中看到,和前后字节不连续的每一个字节块都有两个边界。使用SACK能够描述这些字节块的边界信息。

如果需要使用SACK,那么在建立TCP时,就要在首部的选项中加入允许SACK的选项,并且双方必须事先商定好。由于选项的长度最多只有40字节,指明一个边界需要4字节,最多只能指明4个字节块的边界信息。

图18

8.TCP的流量控制

所谓的流量控制就是让发送方的发送速率不要太快,要让接收方有时间接收。

利用滑动窗口实现流量控制:

以图19为例,接收方B进行了三次流量控制,第一次将窗口减小到300,第二次将窗口减小到100,最后减小到0,也就是不让发送方发送数据了。

如果在B将窗口设为0的一段时间后,再次将窗口设为400,而这个报文段在传送过程中丢失了,那么A就会一直等待B发送窗口大小的报文,B一直等待A发送数据。为了解决这个问题,TCP为每一个连接设置一个持续计时器。只要TCP连接的一方收到对方的零窗口通知,就启动计时器,若时间到期还未收到非零窗口通知,就发送一个零窗口探测报文段。而对方在确认这个探测报文段时给出现在的窗口值。若窗口值仍为零,重置计时器,若不为零,死锁就解开了。

图19

在TCP传输过程中,必须考虑传输效率的问题,可以采用不同的机制控制TCP报文段的发送时机:

(1)TCP维持一个变量,等于最大报文段长度MSS,只要缓存中的数据达到MSS就组装成一个TCP报文段发送出去。

(2)由发送方的应用进程指明要求发送报文段,即PUSH操作。

(3)设置一个计时器,到期时就把已缓存的数据装入报文段(小于MSS)发送出去。

9.TCP的拥塞控制

拥塞控制的一般原理:

拥塞:在某段时间内,对网络中的某资源需求超过了其最多能提供的可用部分,网络性能就会变坏,这时就会产生拥塞。即产生拥塞的条件:

                                            对资源需求的总和 > 可用资源

拥塞问题的实质往往是整个系统的各个部分不匹配,只有各部分平衡了,问题才会得到解决。

拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不过载。

拥塞控制有一个前提:就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有主机、路由器以及与降低网络传输性能有关的所有因素。

流量控制:往往指点对点通信量的控制,是个端到端的问题。

拥塞控制所起的作用如图20所示。

图20

拥塞控制是一个动态的问题,很难设计,有时甚至正是拥塞控制机制本身成为引起网络性能恶化甚至发生死锁的原因。从控制理论的角度来看,拥塞控制可以分为开环控制和闭环控制。

开环控制:在设计网络时事先将有关发生拥塞的因素考虑周到,力求在工作时不发生拥塞。

闭环控制:基于反馈回路的概念,有以下几种措施:

(1)监测网络系统以便检测拥塞在何时何处发生

(2)将拥塞发生的信息传送到可采取行动的地方

(3)调整网络系统的运行以解决出现的问题

几种拥塞控制方法

假定数据时单方向传送,接收方有足够大的缓存空间。

慢开始和拥塞避免

发送方维持一个叫拥塞窗口cwnd的状态变量,cwnd取决于网络的拥塞程度。发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,cwnd就再增大一些,如果网络出现拥塞,cwnd就减小一些。

慢开始算法的原理:如图21所示。

(1)主机刚开始发送报文段时,可以先设置cwnd=1,即设置为一个最大报文段MSS的数值。

(2)每收到一个对新的报文段的确认,将cwnd加1。即每经过一个传输轮次,cwnd就加倍。

(3)用这样的方法逐步增大发送端的cwnd,可以使分组注入网络的速率更加合理。

传输轮次:把cwnd所允许的报文段都连续发送出去,并收到对已发送的最后一个字节的确认。

图21

为了防止cwnd增长过大引起网络拥塞,还需要设置一个慢开始门限ssthresh,用法如下:

(1)cwnd < ssthresh,使用慢开始算法

(2)cwnd > ssthresh,停止使用慢开始算法,改用拥塞避免算法。

(3)cwnd = ssthresh,既可用慢开始算法,也可用拥塞避免算法。

拥塞算法的思路:让cwnd缓慢增大,即经过一个轮次,cwnd增加1,而不是加倍,使cwnd按线性规律缓慢增长。

无论是慢开始算法还是拥塞避免算法阶段,只要发送方判断网络出现拥塞,就要把ssthresh设置为出现拥塞时发送方窗口的一半(但不小于2)。然后将cwnd重新设置为1,执行慢开始算法。

慢开始和拥塞避免算法的实现举例如图22所示。

图22

乘法减小:不论在慢开始阶段还是在拥塞避免阶段,只要网络出现拥塞,就将ssthresh设置为发送方窗口值得一半。

加法增大:执行拥塞避免算法后,在收到对所有报文段的确认后,就把cwnd增加1,使cwnd缓慢增大。

快重传和快恢复:

快重传算法:要求接收方在接收到一个失序报文时,就立即发出重复确认,使发送方及早知道有报文段未到接收方。发送方只要一连收到三个重复确认就立即重传接收方尚未收到的报文段,如图23所示。

图23

快恢复算法:当发送端连续收到三个重复确认后,就执行乘法减小算法,把ssthresh减半,但接下来不执行慢开始算法。因为发送方认为现在网络很可能没有发生拥塞,因此将cwnd设置为ssthresh减半后的值,再开始执行拥塞避免算法。

快重传与快恢复如图24所示。

图24

发送窗口的上限值 = Min [ rwnd , cwnd ]

随机早期检测RED:

路由器的队列维持两个参数,队列长度最小门限THmin和最大门限THmax。RED对每一个到达的数据报先计算平均队列长度Lav,然后按图25所示操作。

图25

瞬时队列长度和平均队列长度的区别如图26所示。

图26

10.TCP的运输连接管理

运输连接有三个阶段:连接建立,数据传输,连接释放

连接建立过程中要解决以下三个问题:

(1)要使每一方能够确知对方的存在

(2)要允许双方协商一些参数

(3)能够对运输实体资源进行分配

TCP连接的建立都是采用客户服务器方式

TCP的连接建立:(三次握手)

在建立连接之前,B的TCP服务器进程先创建传输控制块TCB,准备接受客户进程的连接请求,然后就处于LISTEN状态,等待客户的连接请求。A的TCP服务器进程先创建传输控制块TCB,然后向B发出连接请求报文段。

以图27为例说明TCP建立连接的过程:

(1)A向B发出连接请求报文段,其中报文首部SYN=1,seq=x,表明传送数据时的第一个数据字节的序号为x。TCP规定SYN报文段不能携带数据但要消耗一个序列号。

(2)B的TCP收到连接请求报文后,如果同意就发回确认。确认报文段中SYN=1,ACK=1,seq=y,ack=x+1。

(3)A收到此报文段后,还要向B发出确认。ACK=1,seq=x+1,ack=y+1。A的TCP通知应用进程,连接已经建立。B的TCP进程收到A的确认后,也通知应用进程,连接已经建立。

图27

为什么要使用三次握手?

防止已失效的连接请求报文段突然又传到B,因而产生错误。

TCP的连接释放:(四次挥手)

以图28为例说明四次挥手。

数据传输结束后,通信双方都可释放连接。

(1)A的应用进程向其TCP发出连接释放报文,并停止发送数据,主动关闭TCP连接。连接释放报文段首部的FIN=1,seq=u,等待B的确认。TCP规定,FIN报文段即使不携带数据,也消耗掉一个序号。

(2)B收到连接释放报文后即发出确认,ack=u+1,seq=v,ACK=1。TCP服务器进程通知应用进程,A到B方向的连接释放了,TCP处于半关闭状态。B若继续发生数据,A仍要接收。

(3)B没有数据要发送了,就通知TCP释放连接。FIN=1,ACK=1,ack=u+1,seq=w。

(4)A收到连接释放报文段后,必须发出确认。ACK=1,seq=u+1,ack=w+1。现在TCP还没有释放掉,必须等待经过时间等待计时器设置的时间2MSL后才真正释放掉。

图28

为什么必须等待2MSL的时间呢?

(1)保证A发生的最后一个ACK报文段能够到达B。

(2)防止已失效的连接请求报文段出现在本连接中。

TCP还设有一个保活计时器,若在规定时间内没有收到过客户的数据,就发送一个探测报文段,若连续发送10个探测报文段后仍无客户想要,就任务客户出现故障,关闭连接。

TCP的有限状态及如图29所示。

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

推荐阅读更多精彩内容