《计算机网络:自顶向下方法》Chapter2:应用层

摘要:本章节主要介绍一些常见的、基本的应用层协议。包括与web浏览器相关的http协议,与文件传送相关的ftp协议,与邮件服务相关的协议以及与域名解析相关的协议。最后介绍了https协议。
关键词:HTTP;FTP;SMTP;POP3;IMAP;Exchange;DNS;HTTPS
目录
  1.http协议
    1.1 什么是http协议
    1.2 http协议报文格式
    1.3 持续连接和非持续连接
    1.4 Web缓存
    1.5 Cookies and Session
  2.ftp协议
  3.邮件传输与邮件访问协议
    3.1 邮件传输协议:SMTP
    3.2 邮件访问协议:POP3、IMAP、HTTP
    3.3 http协议
  4.DNS协议
    4.1 DNS协议机理
    4.2 DNS报文格式
  5.P2P应用
    5.1 P2P协议:Bittorrent
  6.扩展:https协议

1 http协议

1.1 什么是http协议

http协议:超文文本传输协议(HyperText Transfer Protocol)。是web应用的核心,客户端和服务器之间通过交换http报文进行会话。
http是无状态协议:[RFC]规范中http报文中不会包含有任何关于客户状态的信息。但对实际应用中,通常会对规范做一些改进,来记录用户状态(cookies and session)
http协议的版本:http有不同的版本,其经历了http0.9->http1.0->http1.1->http2.0版本。不同的版本有不同的特性,我们主要讨论的是http1.1版本,这也是目前http版本中使用最广泛的版本。

1.2 http协议报文格式

1.2.1 请求报文

对于一个协议的他应该包含什么样的信息与其需要提供的服务有关。因此按照提供的服务来说明http报文结构。其报文结构如下表所示:

报文结构 具体内容 用途
请求行 方法;
URL;
版本;
指名请求方法,所请求的文件,以及使用何种http协议会话;
常用的请求方法包括:
GET:获取服务器对象
POST:提交用户表单,比如要你注册账户填表
PUT:上传文件到服务器
HEAD:只获取响应头而不获取实体
DELETE:删除服务器上的对象
首部行 字段名称:值 例如:
User-agent:Mozillia/5.0(使用的浏览器及版本)
Connection:keep-alive(使用持续连接)
还有很多字段,涉及到编码方式,压缩方式,实体内容类型等等,你需要做的就是,遇到不懂的字段,google一下
空行
实体 有些请求方法,比如POST,他将上传文件到服务器,实体内容即需要上传的内容 用于向服务器提交用户数据

1.2.1 响应报文

报文结构 具体内容 用途
状态行 版本;状态码;短语 表明响应状态
常用的状态码包括:
200:请求成功
404:对象不在服务器上
505:服务器不支持请求报文中使用的http协议版本
首部行 字段名称:值 例如:
Server:Apache/1.3.0
Date:请求发送的时间
空行
实体行 响应数据,例如html文件 返回用户所需要的响应数据

对于http协议的请求和响应中的首部行而言,他们包含将包含哪些字段,是很多因素和具体的会话情况而定,提供的服务不同,协议的版本不同,包含的内容都会不一样,具体问题应该具体分析。

1.3 持续连接与非持续连接

非持续连接:一个TCP连接只用于一个http请求,用Conection:Close表示
持续连接:一个TCP连接可以用于多个http请求,用Conection:keep-alive表示。持续连接的多个http请求都是对同一个host的请求,对百度的TCP连接是不能用于对google的请求的。复用TCP连接,可以减少新建一个TCP连接产生的时间消耗,从而提高会话效率,http1.1默认使用持续连接。此外http1.1还配合使用了并行连接,即可以对一个html页面中的其他资源在同时用多个TCP连接并行发出请求,即使用多个TCP连接请求页面中的各种资源来进一步加速效率。在http1.1中,持续连接虽然可以让一个TCP给多个请求复用,但他们不是并发的或者管道化的,而是一个请求完成后开始另一个请求。当然http1.1是支持管道化连接的,也就是在一个TCP连接中请求1的响应还没送回来,就开始发送请求2了,由于这个还不成熟,浏览器默认是关闭管道化连接的。
是TCP提供持续连接服务还是Http?
不同的版本的http有不同的功能,这些功能都由应用层来实现。否则,出一个新的http协议,比如https,岂不是要频繁的更改TCP协议,那分层体系就没有意义了。至于http协议是如何在TCP完全不知情的情况下实现了TCP的持续连接的,那就要看TCP提供了什么样的接口给http,http又是如何利用这些既定的接口实现了持续连接的。由此看来,最丰富多彩的还是应用层协议,也是最值得我们去研究的吧。

1.4 Web缓存

对于一个内容没有发送变化的对象进行再次请求,无疑是对传输资源的浪费。如果能将得到的对象缓存起来,每次请求时,先检查缓存的对象与服务器当前的对象是否有不同,如果有则将从服务器将对象传送至客户,如果没有改变,则直接使用缓存。

Web缓存器:用于存储服务器上对象副本的代理服务器。
用户的请求不会直接达到目标服务器,而是先送达代理服务器,让代理服务器代理客户端浏览器发起的请求。如果代理服务器是第一次代理请求某个对象,那么服务器发回给代理服务器的响应中将包含Last-Modified字段,指明该对象最近一次更新的时间,如果代理服务器再次遇到需要代理该请求对象时,由于再代理服务器上缓存了该对象,代理服务器将会在代理的请求头中添加If-Modified-Since字段,该字段的值就是Last-Modified的值,他询问服务器该对象自从上次更新后是否又发生了更新,如果发生了更新,服务器将会发送新的对象过来,如果没有发生更新,那么Web服务器将会向代理服务器发送304状态码,告诉代理服务器,对象没有更新的更新。如此一来,代理服务器就会将缓存的对象发送至客户端。
从代理服务器发送对象至客户端相比从服务器发送有许多优点:

  • 减轻服务器压力
  • 代理服务器分布在全球各个位置,客户端将选择离自己近的代理服务器从而缩小延迟
  • 很多请求被代理服务器拦截下来,减轻了网络的拥塞

1.5 Cookies and Session

http协议是一种无状态协议,这意味着,服务器难以知道客户的状态。使用cookies和session可以做到这一点。
cookies:服务器生成后发送给客户端,客户端保存
session:服务器生成后,由服务器保存
cookies中包含用户信息,比如用户名密码,请求头中包含cookies后,服务器验证其能匹配对应的session后,将返回该客户特有的响应。
要想获得对http/https中cookies和session的更多细节,最好的方式就是去手动搭建一个网站(使用Python的Django框架或者Java的SSM架构),那么你就可以对其有十分形象的认识了,然后找一份后端的工作,做更多深入的研究

2 FTP

ftp是一种文件传输协议,由于通常能接触到ftp的客户并不多,因此只做简单的高层次的描述,而不涉及其具体的报文格式等。
ftp在传输文件时,也使用TCP传输,只不过其需要两个TCP连接,一个20端口用于控制,一个21端口用于真正的数据传输。在会话期间,控制端连接全程保持连接,而数据传输连接时非持续连接,每传输一个文件就需要重启该连接。ftp报文头部的格式简单,不会尽力比如数据压缩等传输前的预处理操作,因此有说法当传输小文件的时候,ftp比http快,因为对于小文件而言,一个报文端估计就可以了,时间更多的是在预处理上,而传输大文件的时候,反正都是基于TCP传输,时间都差不多,如果传输多个文件,由于HTTP可以采用持续连接配合并行连接,因此省去了很多建立连接的时间开销,这也就是为什么不用ftp来提供web服务而用http,因为网页包含很多小文件。
百度云网盘的文件上传和下载使用什么协议?
https,ftp首先由安全风险,其次https支持断点下载等其他功能,所以说百度云使用https。至于P2P,留到P2P那一节讨论。

3 邮件传输与邮件访问协议

虽然都是客户端-服务器模型,但一个邮件的发送和接收过程与网页的请求和响应过程不同。邮件系统主要包括三个组件:用户代理邮箱服务器传输协议。一封邮件从A的邮箱发送至B的邮箱时,A先将邮件发送至服务器,服务器有一个发送队列,会把来自不同的用户的信建加入该队列,然后发送到B的邮箱服务器,B的邮箱服务器接收到A的邮件后将其放入B的邮箱,然后B登陆其邮箱就可以访问来自A的邮件了。这其中就涉及到了两类协议:一是邮件发送到目的邮箱的邮件传输协议,二是用户访问自己邮箱内容的邮件访问协议

3.1 邮件传输协议:SMTP

SMTP是一个PUSH协议,也就是把用户的报文推出去。该协议用于将用户发送邮件到邮箱服务器,以及邮箱服务器到邮箱服务器之间的邮件发送。

SMTP是基于TCP连接的,因此用SMTP发送邮件之前先要进行TCP的三次握手(运输层握手),接着进行SMTP握手,也就是应用层握手,交换应用层的准备数据,例如发送方是谁,接收方是谁等准备信息,然后才会开始传输真正的邮件报文。SMTP也使用TCP持续连接,发完一封邮件可以接着使用同一个TCP连接发送第二封邮件。具体的握手过程和报文格式可以参考相关协议。

SMTP协议中,两个用户之间发送邮件的时候两个邮箱服务器是直接相连的,没有中间无服务器,即使向美国服务器发送邮件也是直接同美国的邮箱服务器直接建立TCP连接

3.2 邮件访问协议:POP3、IMAP

SMTP协议只是PUSH协议,他只具有推送邮件出去的功能,而不具有访问邮件的功能,也就是你无法使用SMTP协议去访问你邮箱服务器中的邮件。而要使用POP3或者IMAP来和你的邮箱服务器交互(不包括发送邮件)来访问你的邮箱,下载相关邮件。

其中POP3协议是极为简单功能也极为有限的协议。
IMAP则功能相对丰富也比POP3更为复杂,典型就是可以为你的邮箱建立文件夹来分类管理你的邮箱,而POP3协议不能提供该服务。

对于POP3和IMAP的运作过程,可以参考相关的协议规定。

3.3 http协议

目前,越来越多的公司提供Web邮箱,即通过Web来访问邮箱,而不用客户端。使用Web邮箱是通过http来发送和接收邮箱的,注意http协议既可以发送邮件也可以接收邮件,但是只是在客户端和服务器之间,在邮箱服务器和邮箱服务器之间仍然使用SMTP协议发送邮件。

4 DNS

DNS是Domain Name System的缩写,也就是域名服务系统,主要用来解析域名的Ip地址。服务器采用域名来标识自己主要是为了方便用户记忆,http的报文需要通过TCP协议传输,TCP协议需要的是目的服务器的ip地址而不是其对应的域名,因此发起http请求之前,需要先进行DNS查询,以获得目标服务器域名的ip地址,值得注意的DNS报文是基于UDP协议传输的。

由于大型的网站例如Google,其服务器遍布全球各地,因此一个域名可能对应很多ip地址,DNS可以对来自用户的查询均衡的发送各个ip地址,以实现服务的负载均衡,当然内部的机制肯定不是简单的均衡发送那么简单,但不管其内部的算法是怎样的,DNS确实可以起到负载均衡的作用。

4.1 DNS查询过程

那么主机查询的一个域名的ip地址其具体的过程是如何的呢?
首先要知道DNS服务器的层次结构,分为三层:
根DNS服务器:存储顶级域DNS服务器的ip地址
顶级域服务器(TLD):存储权威DNS服务器ip地址
权威DNS服务器:存储域名的ip地址

DNS服务器部分层次结构

这就像一个多叉树,域名的ip信息就在树的叶子节点中。所以查询过程就和对树的叶子节点查询过程一样:

1.访问根服务器>访问TLD>访问权威DNS服务器
2.从权威DNS服务器获取ip地址

上述的查询过程有两种实现方式:(以打听某个人的联系方式为例)
递归查询:你找你的秘书要D的联系方式,秘书先问A有没有D的联系方式,A说不知道,他帮你问问B知不知道,B也不知道,他说他帮你问问C知不知道,然后C知道,告诉B,B再告诉A,A再告诉你秘书,你秘书再告诉你。
迭代查询:你找你的秘书要D的联系方式,秘书先问A有没有D的联系方式,A说不知道,当时A说B可能知道,然后告诉秘书B的联系方式,秘书接着问B知不知道,B说他也不知道,但C可能知道,然后告诉秘书C的联系方式,秘书接着问C,C知道然后告诉你了秘书,你秘书再告诉你。
其中各个角色对应的关系:

:主机
秘书:本地DNS服务器。其并没有画到DNS服务器层次结构中,因为他确实不属于该体系,他更像一个代理,你不需要直接和DNS体系发起查询(当然你确实可以直接向他们发起查询,但没有必要,也不建议)你只需要向本地的DNS服务器发起查询。
A:根DNS服务器
B:TLD服务器
C:权威服务器
D:要查询的域名,其联系方式就是ip地址

实际当中使用的更多的是迭代查询,虽然秘书累了点,但减轻了DNS体系中服务器的压力。实际上,你的秘书能力强,他手里有一本通讯录,他会先看里面有没有D的联系方式,如果没有,他知道C肯定知道(因为C是D的爸爸,父节点),可是通讯录里并没有C的联系方式,但是他知道B有C的联系方式(B是C的爸爸,爸爸应该是知道儿子的联系方式吧),可是通讯录里也没有B的联系方式,他只好找B的爸爸A了,A的联系方式是有的。秘书事先做了这样一番工作之后,才会开始前面提到的递归查询或迭代查询过程。

经过这样一轮迭代/递归查询之后,那么秘书会在通讯录里把他们的联系方式都备份下来,以后要找A他们家族的人就不用再从A开始打电话问起了。比如找D的哥哥,直接问C就可以了。

那么你想知道你的秘书是谁吗?
在网络配置里面,往往都有你的DNS服务器地址,如果你发现他和你的网关地址也就是路由器一样,你是不是觉得这个DNS服务器就内置到路由器里面了,不,他只是代理,他会去跟你的秘书交流,你真正的秘书,其实你自己也不知道他到底是谁,尤其是你第一次接入网络的时候,谁知道DNS服务器是多少,你甚至连路由器的地址都不知道,不,你甚至连你自己的ip地址你都不知道(可以通过DHCP协议自动获取),所以路由器提供了这个代理功能,在你第一次配置网络参数的时候,你就免去了配置DNS服务器的麻烦了。当然,联网成功后,你可以手动配置指定你的秘书也就是DNS服务器地址,这样就不用走路由器代理了。

4.2 DNS的记录和报文

了解了DNS查询过程之后,继续看一下其是如何记录联系方式,以及DNS的报文是什么样的?

  • DNS记录(一般用缩写RR:resource record):(Name,Value,Type,TTL)
字段名称 含义
Name 域名(也就是人名:A、B、C、D)
Value 联系方式(该联系方式可以是具体的ip也可以是域名,就好比在C那里D的联系方式记录的D的电话号码,而在B那里记录的则是:D的联系方式是C)
Type 类型:他决定了联系方式应该写具体的电话号码还是人名
Type=A:写ip地址
Type=NS:写Name域的父DNS域名
Type=CNAME:Name的别名
Type=MX:邮件服务器的规范主机名
TTL 该记录的生成时间,也就是联系方式的有效期
  • DNS 报文


    DNS报文结构

    其中主要是问题与回答,他们都是以报文记录方式描述的,很容易看,如果想深入理解各个字段的内容,最好就是用wire'shark抓几个DNS包分析分析。

4.3 DNS记录的创建

域名注册机构为客户注册一个域名需要做两件事:

  1. 将该域名和负责该域名的权威DNS服务器插入到TLD服务中,即插入类型为NS的记录
  2. 然后在权威DNS服务器中,插入该域名和其ip地址,即插入类型为A的记录。

当然了,此后你可以申请修改ip地址,也就是域名是固定的,但你的ip是可以变化的。

4.4 虚拟技术下的DNS服务

目前很多网站都部署在云服务器上,一台云服务器中,可以有很多台虚拟机,如果这些虚拟机使用桥接,则大家都能获得属于自己的ip地址,这与普通情况没什么两样。而如果你使用的是共享主机这种方式的虚拟机,那么大家都共用一个对外的ip地址,共用同一台主机,此时你在域名注册机构注册了域名之后,其最后指向的是云服务器公共ip地址,同时使用80或443端口,该主机上的其他客户也注册了域名使用了同样的ip同样的端口,这样就会产生冲突。为了解决这个冲突,大家把自己的网站应用都交由Apache或Ngix服务器管理,他们在自己的配置文件中给你配置好,如果域名是A公司的,将会把请求发送给A的网站应用处理,如果是B公司的就发给B公司,这里只是做一个简单的描述,尽管真是的情况更加复杂。这样就可是通过一个ip地址来运行多个网站了。如果你发现当你键入ip地址在浏览器器中来试图直接访问该网站时,你将直接访问到Apache或Ngix的根目录,而在有多个网站的时候,这里面通常什么都没有,所以就会提示你“/”目录下什么也没有,或者你没有权限。这是因为你用ip地址来访问的时候,host字段就是ip地址而不是域名,因此Apache或Ngix服务器无法根据host字段来确定该请求到底应该给谁。
书本上的只是是经典,在通常情况下是对的,如果你发现不对劲,那也许是技术发生了进步,以适应日新月异的网络环境。但这一切仍然要以书上的知识为基础。如果你对这些技术感兴趣,可以自己实战一遍,这样可以有更加深入的理解。

5 P2P

P2P是peer-to-peer的缩写,与传统的客户端-服务器模型区别在于,他是客户端-客户端模型的。这是一个很有意思的主题。
书中的P2P列举了两个重要的应用,即文件分发和分布式散列表。其中分布式散列表比较简单,书中也有很多细节,需要的时候,可以找资料研究。而文件分发则与我们的联系更加紧密。在开始之前,先简单介绍一下,P2P的协议:Bittorrent

5.1 P2P协议:Bittorrent

我们在较高的层面上对改该协议做一些概括。我们以百度云网盘为例来描述他的运行过程,当然实际情况可能更加复杂,但这种简化的描述,有助于理解。
每个安装了百度云盘客户端的人,都被称之为对等方,当你打开你的百度云网盘,那么你就告诉了百度云的追踪器,你加入了百度云网盘的对等方网络。追踪器将会分发给你一些其他用户的ip地址,你就可以与他们之间都开启TCP连接了,这就是你的邻近对等方,由于其他用户随时可能下线,因此你的邻近对等方列表是时刻变化的。

对于每个对等方而言,你要做两件事,第一下载文件,第二上传文件。
下载文件:当你找到一个你想要下载的资源,你的临近对等方都有该资源的一个子部分,你会使用你的多个TCP连接,从不同的对等方处获得不同的部分。并优先获得副本最少的部分。这样多个并行的TCP连接同时下载一个文件的不同部分要比起从一个服务器下载快多了。
文件上传:当你拥有该资源后,如果别人也需要的该资源的时候,百度云就会自动上传你电脑中的资源。所谓一报还一报。如果你发现你用百度云下载资源的时候,他同时在上传某些文件,不要惊讶。如果你不想一报还一报,那你可以把下载后的资源改个名称或者换个路径,百度云发现你网盘的资源对应的本地地址不存在时,就不能上传了。当然这些资源都是公开分享的资源,你私密的资源,由于没有分享连接,别人拿不到。
如果使用百度云的Web服务器下载文件,是否会使用P2P协议呢?Web浏览器做为主线程可以使用https协议,不代表他不能开启以其他应用层协议的子线程。同样https不是非要运行在Web浏览器中。Web浏览器也只是应用程序的一种。

PPTV的前身PPlive是我们华中科技大学的校友开发的。这是一个典型的P2P应用。
迅雷也是一个P2P应用。

经常听到下载人数越多时,速度越快这样不可思议的事情。其实就是用了P2P协议。当大家从服务器下载某个资源的时候,你逐渐有了该资源的一部分,而其他也在请求该资源的人可能拥有了另外一部分,这时,你可以向他传输你有的那部分,他向你传输他有的那部分。比如,服务器向将文件分割成10部分,然后发给你们10个人每人一个不同的部分,然后每个人通过9个并行的TCP连接从另外9个人那里获得剩下的部分,如果是50个人,就更快了,因为文件快变成50个,每个块变小了,传输就变块了。因为同时互传另外,此外,如果同时好几个人向你请求你有的那部分,那么你只响应他们中同时在给你传输文件速率最大的前几个人。谁给你传的多,你就传给谁。

扩展:HTTPS协议

https协议的目的就是在http协议的基础上对数据进行加密传输。
加密方式有两种:

  • 对称加密:速度快,加密和解密使用同一个密钥。
  • 非对称加密:速度慢,有一对钥匙,可以相互加密和解密,其中一把叫公钥,一把叫私钥,公钥就是公开的那一把密钥,而私钥就是不公开的那一把密钥。如果你用公钥加密,只有有私钥的人能解密,因此加密数据传输时采用公钥加密,私钥解密。如果反过来呢?用私钥加密,公钥解密,那就不能加密数据了,因为人人都可以用公钥来解密,这种相反的加密解密顺序,可以用来数字签名,即告诉大家我是我,不信你们用公钥解密认证一下。

为了更快的传输数据,所以https当然选择就是使用对称加密,问题是如何使得双方都知道这个共同的密钥,然后用来加密数据呢?这个密钥只能彼此知道,不能被其他人劫持,因此https协议,将会先通过非对称加密的方式,让客户端认证服务器,并传输对称加密的密钥。总结起来,其在和http协议的不同在于:

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

推荐阅读更多精彩内容