抱佛脚-基础知识系列之计网

抱佛脚一时爽,一直抱佛脚一直爽!这篇文章总结常见的计算机网络面试问题~因为是抱佛脚,所以结构上没有什么逻辑...

参考链接:Waking-Up CycNotes 牛客网 博客园 知乎专栏

session vs cookie

  • Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份

  • 通常session的生存期会比cookie更长一些

  • 考虑到安全应该使用session:别人可以分析存放在本地的cookie并进行cookie欺骗

  • 考虑到服务器性能应该使用cookie:session会在一定时间内保留在服务器上

  • cookie的分类

    • 会话cookie:关闭浏览器窗口,cookie就消失;一般不存储在硬盘上而是保存在内存里

    • 持久cookie:把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间

  • 流程

    • 若没有禁用cookie

      • 客户端第一次发请求给服务端,请求中有包含用户名和密码的表单

      • 服务端验证用户名和密码,把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID,返回响应报文的 Set-Cookie 首部字段包含了这个 Session ID

      • 客户端收到响应报文之后将该id写入 Cookie ,存入浏览器中

      • 客户端之后对同一个服务器进行请求时会包含该 Cookie

      • 服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作

    • 若禁用了cookie

      • 客户端第一次发请求给服务端

      • 服务端创建session并生成sessionId,返回给客户端

      • 客户端再次发请求,通过url重写等方法在请求中加上sessionId发给服务端

      • 服务器按照sessionId把这个session检索出来使用

token

  • 最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库

  • 当用户首次登录成功(注册也是一种可以适用的场景)之后, 服务器端就会生成一个 token 值,这个值,会在服务器保存token值(保存在数据库中),再将这个token值返回给客户端

  • 客户端拿到 token 值之后,进行本地保存;当客户端再次发送网络请求(一般不是登录请求)的时候,就会将这个 token 值附带到参数中发送给服务器

token vs session vs cookie

  • session代表服务器与浏览器的一次会话过程,token是一种认证手段

  • cookie,session都可以是token存储的一种方式

  • 作为身份认证,token安全性比session好

socket

含义

  • 应用层与TCP/IP协议族通信的中间软件抽象层

  • 把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议

套接字地址

  • = ip + 端口号

  • 当客户端发起一个连接请求时,客户端套接字地址中的端口是由内核自动分配的,称为临时端口

  • 服务端的套接字端口通常是某个知名的端口

  • 一个连接由它两端的套接字地址唯一确定

套接字接口

  • 是一组函数

socket通信(网络I/O)过程

  • 服务端

    • 初始化socket(包括协议、socket类型等)

    • 与端口绑定(bind,ip地址+端口号用于标识socket)

    • 对端口进行监听(listen)

    • 调用accept(是阻塞的),等待客户端连接

    • 处理请求

    • close(注意最后一个close后返回accept阶段,等待下一个连接请求

  • 客户端

    • 初始化socket(包括协议、socket类型等)

    • 发送连接请求(connect)

    • 发送数据请求

    • 关闭连接(close)

  • 当接收到一个TCP连接请求后,如果上层还没有接受它,那么系统将缓存这个连接请求;当超过指定数量后,系统将会拒绝连接;假如缓存的TCP连接请求发送来数据,那么系统也会缓存这些数据,等待SocketServer获得这个连接的时候一并交给它

I/O模型

总过程

对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所等待数据到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用进程缓冲区

同步 vs 异步

  • 同步:用户进程触发IO操作,须等待或者主动的去询问IO是否完成,完成则发送IO系统调用

  • 异步:用户进程触发IO 操作以后便开始做自己的事情,而当IO 操作已经完成的时候会得到IO 完成的通知

阻塞 vs 非阻塞

  • 阻塞:数据未就绪时读取或者写入函数将一直等待

  • 非阻塞:数据未就绪时会立刻得到error

阻塞式I/O

  • 应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回

  • 其它应用进程还可以执行,所以不消耗 CPU

非阻塞式I/O

  • 应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)

  • CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率比较低

I/O多路复用

  • 让单个进程(reactor)具有处理多个 I/O 事件的能力

  • 主进程是阻塞的

  • 使用 select / poll / epoll 等待数据,并且可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时返回,之后再使用 recvfrom 把数据从内核复制到进程中

信号驱动 I/O

  • 应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的

  • 内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中

异步I/O

  • 应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号

  • 异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O

select vs poll vs epoll

select

  • 用户把需要监控的描述符对应的bit置为1

  • 用户进程调用select(是一个系统调用),整个进程会被阻塞,直到select返回

  • select把描述符集从用户态拷贝到内核态,不断轮询所负责的所有socket,当任何一个/多个socket中的数据准备好了,把它们对应的bit置为0,并把所有描述符从内核态拷贝回用户态,select返回

  • 用户进程遍历检查描述符集中哪个描述符上返回了,通过系统调用调用内核的read操作,把数据从内核拷贝到用户进程的地址空间

  • 描述符集的大小受限于FD_SIZE

poll

  • 本质上和select没有区别

  • 但是它没有最大连接数的限制,原因是它是基于链表来存储的

  • 每增加一个描述符就向数组中加入一个含描述符的结构体,结构体只需拷贝一次到内核态

epoll

  • 只返回状态发生变化的文件描述符给用户:文件描述符就绪时,采用回调机制,避免了轮询(回调函数将就绪的描述符添加到一个链表中,执行epoll_wait时,返回这个链表)

  • 当连接数较多并且有很多的不活跃连接时,epoll的效率比其它两者高很多;但是当连接数较少并且都十分活跃的情况下,由于epoll需要很多回调,因此性能可能低于其它两者

  • 水平触发 vs 边缘触发

    • 水平触发(LT,Level Trigger)模式下,一个文件描述符F就绪触发通知,如果用户程序没有一次性把数据读写完,下次用户调用epoll_wait时还会通知用户F就绪了

    • 边缘触发(ET,Edge Trigger)模式下,当描述符从未就绪变为就绪时通知一次,之后不会再通知,直到再次从未就绪变为就绪;所以边缘触发一定要用非阻塞(non-block)IO

主机间通信

方式

  • 客户-服务器(C/S):客户是服务的请求方,服务器是服务的提供方

  • 对等(P2P):不区分客户和服务器

过程

  • 若在同一局域网中

    • 若A的ARP缓存中有主机B的IP到MAC的映射

      • 构造报文:目的IP、MAC都是主机B;源IP、MAC都是主机A

      • 把报文发给交换机C,C记录A的MAC到端口号的映射

        • 若C记录过B的MAC到端口号的映射:将报文从对应端口转发出去

        • 若C未记录B的MAC到端口号的映射:洪泛,广播报文到局域网所有机器;B收到广播后会发给C要回复给A的报文;C记录B的MAC到端口号的映射

    • 若A的ARP缓存中没有主机B的IP到MAC的映射

      • A发送ARP请求报文给C

      • C洪泛,把ARP请求广播到局域网所有机器

      • B收到广播报文,在自己的ARP缓存表中写入A的IP到MAC的映射;将自己的MAC封装到ARP回复报文中,单播给A

      • A获取到主机B的MAC后,在自己的ARP缓存表中写入B的IP到MAC的映射

  • 若不在同一局域网中

    • A先发给路由器(目的IP是路由器)

电路交换 vs 分组交换

电路交换

建立连接,且在整个通信过程中始终占用该链路

分组交换

  • 每个分组都有首部和尾部,包含了源地址和目的地址等控制信息

  • 在同一条传输线路上允许同时传输多个分组,分组交换不需要占用传输线路

传输时延 vs 传播时延

传输时延

数据帧的长度 / 传输速率

传播时延

信道长度 / 电磁波传播速度

体系结构

五层协议

数据单位 栗子 功能 设备
应用层 报文 http、dns 应用程序之间的数据传输
传输层 报文段(TCP) 用户数据报(UDP) tcp、udp 进程之间的数据传输
网络层 分组 ip 主机之间的数据传输 路由器
数据链路层 csma/cd、ppp 一条链路两端主机之间的数据传输 交换机
物理层 bit 在传输媒体上传输数据bit流 集线器

OSI七层协议

把应用层由上至下分为:

  • 会话层:建立及管理会话

  • 表示层:数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题

  • 应用层

TCP/IP四层协议

  • 把数据链路层和物理层合为网络接口层

  • TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层

数据在各层之间的传递过程

  • 在向下的过程中,需要添加下层协议所需要的首部或者尾部

  • 在向上的过程中不断拆开首部和尾部

路由器 vs 交换机

路由器

网络层设备,连接不同的局域网,根据IP地址进行转发

交换机

链路层设备,连接同一局域网中的不同主机,根据MAC地址进行转发

物理层

通信方式

  • 单工通信:单向传输

  • 半双工通信:双向交替传输

  • 全双工通信:双向同时传输

带通调制

  • 模拟信号是连续的信号,数字信号是离散的信号

  • 带通调制把数字信号转换为模拟信号

链路层

基本功能

  • 成帧(给IP数据包添加首部和尾部)

  • 透明传输(如果帧的数据部分含有和首部尾部相同的内容,需插入转义符,接收端处理后可以还原数据;整个过程对用户透明)

  • 差错检测(用循环冗余检验CRC检查比特差错)

信道分类

  • 广播信道:一对多

  • 点对点信道:一对一

信道复用

  • 频分复用:所有主机在相同的时间占用不同的频率带宽资源

  • 时分复用:所有主机在不同的时间占用相同的(所有的)频率带宽资源【ABCDABCD】

  • 统计时分复用:不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送【ACDABBCD】

  • 波分复用:光的频分复用

  • 码分复用

    • 为每个用户分配 m bit 的码片,并且所有的码片正交

    • 拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码

    • 接收端使用码片 对接收到的数据进行内积运算时,结果为 0 的是其它用户发送的数据,结果为 1 的是用户发送的比特 1,结果为 -1 的是用户发送的比特 0

CSMA/CD 协议

  • 总线型网络,许多主机以多点的方式连接到总线上

  • 每个主机都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待

  • 虽然每个主机在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞

  • 当发生碰撞时,站点要停止发送,等待一段时间再发送

PPP协议

用户计算机和 ISP 进行通信时所使用的数据链路层协议

MAC地址

链路层地址,用于唯一标识网络适配器(网卡)

以太网

  • 星型拓扑结构局域网

  • 使用广播信道

  • 使用交换机根据MAC地址进行存储转发

交换机

  • 交换表中存储着 MAC 地址到接口的映射

  • 交换机可以自学习交换表的内容,即插即用,不需要网络管理员手动配置交换表

虚拟局域网 VLAN

虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息

网络层

IP数据报格式

  • 版本 : 有 4(IPv4,32位)和 6(IPv6,128位)两个值

  • 总长度:首部长度+数据部分长度

  • 生存时间 TTL:防止无法交付的数据报在互联网中不断兜圈子

  • 首部检验和:数据报每经过一个路由器,都要重新计算检验和,首部检验和不包括对数据部分的检验

  • 标识:在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识

  • 片偏移:和标识符一起,用于发生分片的情况

无分类编址 CIDR

  • 128.14.35.7/20 表示前 20 位为网络前缀

  • 子网掩码首 1 长度为网络前缀的长度,对长度没有规定

  • IP地址与子网掩码相与可得子网ip

ARP协议

  • ARP 实现由 IP 地址得到 MAC 地址

  • 每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射表

ICMP协议

  • Ping:向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送 Echo 回答报文。Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率

  • Traceroute:跟踪一个分组从源点到终点的路径

VPN 虚拟专用网

  • 机构内的计算机可以使用仅在本机构有效的 IP 地址(专用地址)

  • 机构内的主机只与本机构内的其它主机通信

NAT 网络地址转换

  • 专用网内部的主机使用本地 IP 地址又想和互联网上的主机通信时,可以使用 NAT 来将本地 IP 转换为全球 IP

  • 分为静态转换(转换得到的全球IP地址固定不变)和动态NAT转换

路由器分组转发流程

  • 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N

  • 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付

  • 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器

  • 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器

  • 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器

  • 报告转发分组出错

传输层

UDP vs TCP

UDP TCP
连接 无连接 面向连接
可靠 不可靠 可靠
服务 提供及时性服务 提供完整性服务
流量控制
拥塞控制
面向 面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部) 面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块)
n对n 支持一对一、一对多、多对一和多对多 支持一对一

TCP三次握手

  • 客户端随机生成序列号x,向服务端发送连接请求报文,进入SYN_SENT状态

    • SYN=1, ACK=0, seq=x
  • 服务端收到连接请求报文,同意建立连接则随机生成序列号y,向客户端发送连接确认报文,进入SYN_RCVD状态

    • SYN=1, ACK=1, seq=y, ack=x+1
  • 客户端收到连接确认报文,再次向服务端发出确认报文,连接建立,进入ESTABLISHED状态

    • ACK=1, seq=x+1, ack=y+1
  • 服务端收到确认报文后,连接建立,进入ESTABLISHED状态

三次握手的原因

  • 客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接

  • 两次握手无法保证Client正确接收第二次握手的报文(Server无法确认Client是否收到)

为什么不用四次握手

因为tcp是全双工的,三次已经确认了数据在两个方向上都是可以正确到达的

第三次握手中客户端的报文未到达服务端会怎样

  • 服务端:由于Server没有收到ACK确认,因此会重发之前的SYN+ACK(默认重发五次,之后自动关闭连接进入CLOSED状态);在Server进入CLOSED状态之后,如果Client向服务器发送数据,服务器会以RST包应答

  • 客户端:收到服务端重发的SYN+ACK报文后,重新传ACK给Server

如果已经建立了连接,但客户端出现了故障怎么办

服务器每收到一次客户端的请求后都会重新复位一个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接

TCP四次挥手

  • 客户端发送连接释放报文

    • FIN=1, seq=u
  • 服务端收到释放报文后,发出确认报文,此时TCP半关闭,服务端能向客户端发送数据,但客户端不能向服务端发送数据,服务端进入CLOSE-WAIT状态

    • ACK=1, seq=v, ack=u+1
  • 服务端数据传输完成后,发出连接释放报文

    • FIN=1, ACK=1, seq=w, ack=u+1
  • 客户端收到连接释放报文后发出确认,客户端进入TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后如果没有收到服务端的回复,则释放连接

    • ACK=1, seq=u+1, ack=w+1
  • 服务端收到客户端的确认后,释放连接

四次挥手的原因

  • 客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文

  • 客户端接收到服务器端的 FIN 报文后进入 TIME-WAIT状态,要等最大数据包生存期的两倍,是为了:

    • 确保最后一个确认报文能够到达。如果服务端没收到客户端发送来的确认报文,那么就会重新发送连接释放请求报文,那么客户端就会重传确认报文

    • 让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧连接的报文

TCP可靠传输

  • 超时重传:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段

  • 确认机制:当TCP收到发自TCP连接另一端的数据,它将发送一个确认

  • 校验和:校验出包有错,丢弃报文段,不给出响应

  • 如果有必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层

TCP流量控制

  • 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率,保证接收方来得及接收

  • 发送窗口

    • 如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态

    • 发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收

  • 接收窗口

    • 接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口

    • 接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认

    • 如果接收方没有能力接收数据,就会将接收窗口设置为0,这时发送方必须暂停发送数据,但是会启动一个持续计时器(persistence timer),到期后发送一个大小为1字节的探测数据包,以查看接收窗口状态。如果接收方能够接收数据,就会在返回的报文中更新接收窗口大小,恢复数据传送

TCP拥塞控制

  • 必要性:流控只简单地表明了接收方的处理能力,并不能代表中间网络的处理能力

  • 慢开始与拥塞避免:慢开始:cwnd初始值为1,每收到一次确认cwnd*=2;拥塞避免:cwnd达到ssthresh时,每收到一次确认cwnd+=1;如果出现超时,令ssthresh = cwnd / 2,cwnd=1

  • 快重传与快恢复:快重传:发送方收到3个重复确认时可知下一个报文段丢失,立即重传下一个报文段;快恢复:考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞,所以此时不执行慢开始算法,而是直接令cwnd = ssthresh,从而进入拥塞避免执行拥塞避免算法

窗口

  • cwnd:拥塞窗口;awnd:接收方指定的发送窗口

  • 实际的发送窗口:W=min(cwnd,awnd)

TCP的四个队列

  • receive队列

    • 是真正的接收队列

    • 操作系统收到的TCP数据包经过检查和处理后,就会保存到这个队列中

  • backlog队列

    • 是备用队列

    • 当用户正在对socket进行系统调用,如recv时,操作系统收到数据包时会将数据包保存到 backlog队列中

  • prequeue队列

    • 是预存队列

    • 当socket没有正在被用户进程使用时,也就是用户进程调用了read或者recv系统调用,但是进入了睡眠状态时,操作系统直接将收到的报文保存在 prequeue中

  • out_of_order队列

    • 是乱序队列

    • 队列存储的是乱序的报文,操作系统收到的报文并不是TCP准备接收的下一个序号的报文,则放入 out_of_order队列

TCP接收报文的流程

TCP粘包

  • 产生原因

    • 只有TCP有粘包现象,UDP永远不会粘包,因为TCP是基于数据流的协议,而UDP是基于数据报的协议

    • 接收方不知道消息之间的界限,不知道一次性提取多少字节的数据

    • TCP为提高传输效率,会根据nagel优化算法(将数据量小的,且时间间隔较短的数据一次性发给对方)把数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据

  • 发生时机

    • 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

    • 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

  • 解决方法

    • 发送定长包

    • 包尾加上\r\n标记

    • 包头加上包体长度

    • 使用更加复杂的应用层协议

TCP首部

  • 固定部分:20字节

    • 源端口、目的端口

    • 序号、确认号

    • 数据偏移:即首部长度

    • 控制位:6个保留,剩下6个是

      • URG:告诉系统此报文段中有紧急数据,应尽快发送

      • ACK:仅当ACK = 1时确认号字段才有效;TCP规定,在连接建立后所有的传送的报文段都必须把ACK置为1

      • PSH:在一端的应用进程希望在键入一个命令后立即就能收到对方的响应;接收方TCP收到PSH=1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付

      • RST:当RST=1时,表明TCP连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST置为1还用来拒绝一个非法的报文段或拒绝打开一个连接

      • SYN:SYN置为1就表示这是一个连接请求或连接接受报文

      • FIN:当FIN=1时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接

    • 窗口:接收窗口的大小

    • 校验和:伪首部、首部和数据的校验和

    • 紧急指针:仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数(紧急数据结束后就是普通数据);当所有紧急数据都处理完时,TCP就告诉应用程序恢复到正常操作;即使窗口为0时也可以发送紧急数据

  • 长度可变部分

    • 选项(长度可变)

    • 填充部分

UDP首部

  • 固定为8字节

    • 源端口、目的端口

    • UDP长度:伪首部、首部和数据的总长

    • 校验和:伪首部、首部和数据的校验和

TCP/UDP伪首部

  • 在首部前面,不属于首部

  • 长度为12字节

  • 作用:这样的校验和,既校验了TCP&UDP用户数据的源端口号和目的端口号以及TCP&UDP用户数据报的数据部分,又检验了IP数据报的源IP地址和目的地址。伪报头保证TCP&UDP数据单元到达正确的目的地址

  • 内容:

    • 源IP、目的IP

    • 8位填充0

    • 协议

    • TCP/UDP长度

应用层

web页面请求过程

  • 若A没有 IP 地址:DHCP(用的是UDP)

    • A发送DHCP请求报文给交换机,目的IP是255.255.255.255,源 IP是0.0.0.0;目的MAC是FF:FF:FF:FF:FF:FF,源MAC是自己的MAC

    • 交换机洪泛,该局域网的DHCP 服务器返回IP、DNS服务器IP、默认网关路由器的 IP 地址和子网掩码

    • 交换机把报文单发给A

  • 若A的ARP缓存中没有网关路由器的IP和MAC的映射:ARP

    • A发送ARP请求给交换机

    • 交换机洪泛,网关路由器返回其MAC

    • 交换机把报文单发给A

  • 若浏览器缓存和OS缓存中没有HTTP服务器的域名和IP的映射:DNS(用的是UDP)

    • A通过交换机发送DNS报文给网关路由器

    • 网关路由器把报文转发给DNS服务器

    • DNS服务器在 DNS 数据库中查找待解析的域名

      • A到本地域名服务器进行递归查询:即本地域名服务器代替它进行查询

      • 本地域名服务器进行迭代查询:先通过根域名服务器知道.com这个顶级域名服务器的IP;再通过.com顶级服务器知道.baidu这个二级域名服务器的IP

    • 原路返回

    • A根据IP生成TCP套接字

  • HTTP

    • TCP三握手和HTTP服务器建立连接:端口是80

    • 浏览器生成 HTTP GET 报文,并交付给 HTTP 服务器

    • HTTP 服务器生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给客户端

    • 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面

  • 浏览器

    • 浏览器处理HTTP响应报文中的主体内容,首先使用loader模块加载相应的资源【loader模块有两条资源加载路径:主资源加载路径和派生资源加载路径:主资源即google主页的index.html文件 ,派生资源即index.html文件中用到的资源】

      • 浏览器的Parser模块解析主资源的内容,生成派生资源对应的DOM结构

      • 后根据需求触发派生资源的加载流程(比如,在解析过程中,如果遇到img的起始标签,会创建相应的image元素HTMLImageElement,接着依据img标签的内容设置HTMLImageElement的属性。在设置src属性时,会触发图片资源加载,发起加载资源请求)【这里常见的优化点是对派生资源使用缓存】

    • 构建DOM树(经过了Parser模块的处理,浏览器把页面文本转换成了一棵节点带CSS Style、会响应自定义事件的Styled DOM树)

      • 使用parse模块解析HTML

        • 解码:将网络上接收到的经过编码的字节流,解码成Unicode字符

        • 分词:按照一定的切词规则,将Unicode字符流切成一个个的词语(Tokens)

        • 解析:根据词语的语义,创建相应的节点(Node)

        • 建树:将节点关联到一起,创建DOM树

      • 使用parse模块解析CSS

        • 解析出一系列CSSRule,含有CSS属性和值的Key-Value集合

        • 进行CSSRule的匹配过程,即寻找满足每条CSS规则Selector部分的HTML元素,然后将其Declaration声明部分应用于该元素

      • 使用parse模块解析JS

        • 动态地改变DOM树(比如为DOM节点添加事件响应处理函数),即根据时间(timer)或事件(event)映射一棵DOM树到另一棵DOM树
    • 构建Render树

      • Render树用于表示文档的可视信息,记录了文档中每个可视元素的布局及渲染方式

      • DOM树上的节点与Render树上的节点并不是一一对应的。只有DOM树的根节点及可视节点才会创建对应的RenderObject节点

    • 构建Render Layer树

      • 以层为节点组织文档的可视信息,网页上的每一层对应一个Render Layer对象

      • 每个RenderLayer树的节点都对应着一棵Render树的子树,这棵子树上所有Render节点都在网页的同一层显示

    • 布局和渲染

      • 布局就是安排和计算页面中每个元素大小位置等几何信息;HTML采用流式布局模型,基本的原则是页面元素在顺序遍历过程中依次按从左至右、从上至下的排列方式确定各自的位置区域

      • 渲染:将Render树映射成可视的图形,它会遍历Render树调用每个Render节点的绘制方法将其内容显示在一块画布或者位图上,并最终呈现在浏览器应用窗口中成为用户看到的实际页面

HTTP协议(80端口)

  • http方法

    • GET:获取资源

    • POST:传输实体主体,如提交表单等

    • HEAD:获取报文首部,主要用于确认 URL 的有效性以及资源更新的日期时间

    • OPTIONS:查询支持的方法

    • PUT:上传文件(由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题),从客户端向服务器传送的数据取代指定的文档的内容

    • PATCH:对资源进行部分修改(PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改)

    • DELETE:删除文件,与 PUT 功能相反,并且同样不带验证机制

    • CONNECT:要求在与代理服务器通信时建立隧道,把通信内容加密后经网络隧道传输

    • TRACE:服务器会将通信路径返回给客户端

  • GET vs POST

    • GET用于获取资源,POST用于传输实体主体,比如提交表单等

    • GET不会改变服务器存储的数据,POST可能会改变服务器状态,比如上传了表单,存储到了数据库中

    • GET的参数在URL中,POST的参数在实体主体中

    • GET请求可被缓存、收藏、保留到历史记录,且其请求数据以明文出现在URL中;POST的参数不会被保存,安全性相对较高

    • GET请求在URL中传送的参数是有长度限制的,而POST没有

    • GET产生一个TCP数据包、POST产生两个TCP数据包:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)

  • http状态码

    • 100 continue:表示到目前为止都很正常

    • 2xx 成功

      • 200 ok:请求成功;一般用于GET与POST请求

      • 203 非授权信息:请求成功,但返回的meta信息不在原始的服务器,而是一个副本

      • 204 无内容:服务器成功处理,但未返回内容

    • 3xx 重定向

      • 301 永久移动:请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI,今后任何新的请求都应使用新的URI代替

      • 302 临时移动:资源只是临时被移动,客户端应继续使用原有URI

    • 4xx 客户端错误

      • 400 请求报文语法错误

      • 401 请求中缺少认证信息

      • 403 请求被拒绝

      • 404 服务器无法根据客户端的请求找到资源

      • 405 客户端请求中的方法被禁止

    • 5xx 服务端错误

      • 500 服务器内部错误,无法完成请求

      • 502 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应

      • 503 由于超载或系统维护,服务器暂时无法处理客户端的请求

    • 600 源站没有返回响应头部,只返回实体内容

  • 短连接与长连接

    • 当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问的 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大

    • 长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信

  • 流水线

    • 默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出

    • 流水线是在同一条长连接上连续发出请求,而不用等待响应返回

  • 各版本区别

    • 1.0 vs 1.1

      • HTTP1.1中默认开启长连接;HTTP1.0需要使用keep-alive参数来告知服务器端要建立一个长连接

      • 1.1节约带宽:支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,客户端接收到100才开始把请求body发送到服务器;如果返回401,客户端就可以不用发送请求body了

      • 在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname),HTTP1.0没有host域。随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机,它们共享一个IP地址。HTTP1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request)

      • 1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除

    • 1.1 vs 2.0

      • HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求(原长连接是同一连接串行处理多个请求:请求1-回复1-请求2-回复2...)

      • HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了

      • 2.0允许服务器在客户端未请求时推送数据给客户端;这样客户端可以直接从本地加载这些资源,不用再通过网络

  • HTTP请求报文格式

    • 请求行:方法、协议版本、URL(包括参数信息)

    • 请求头部:是一个个的key-value值,包括浏览器类型,客户端支持的数据格式,主机名等

    • 空行:表示header和请求数据的分隔

    • 请求数据:GET方法没有携带数据, POST方法携带一个body

  • HTTP响应报文格式

    • 状态行:状态码、协议版本

    • 消息报头:时间戳、使用的字符集、正文的长度、最后修改时间、媒体格式类型(html、图片、json、pdf等)等

    • 空行:分割消息报头和响应正文

    • 响应正文

  • HTTP特点

    • 无状态:http服务器并不保存关于客户的任何信息,HTTP/1.1 引入 Cookie 来保存状态信息

    • 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接;HTTP的短连接和长连接是指TCP的短连接和长连接,从HTTP角度看其实是无连接的

HTTPS协议(443端口)

  • 作用:解决了http的安全问题

    • http使用明文进行通信,内容可能会被窃听;https则是加密通信

    • http不验证通信方的身份,通信方的身份有可能遭遇伪装;https则有身份验证

    • http无法证明报文的完整性,报文有可能遭篡改;https有报文摘要

  • 本质

    • HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信协商加密使用的对称加密密钥,再由 SSL 和 TCP 通信

    • https采用混合加密:使用非对称密钥加密方式,传输对称密钥加密方式所需要的 Secret Key,从而保证安全性;获取到 Secret Key 后,再使用对称密钥加密方式进行通信,从而保证效率

  • 对称加密 vs 非对称加密

    • 对称密钥加密:

      • 加密和解密使用同一密钥

      • AES:设AES加密函数为E,则 C = E(K, P),其中P为明文,K为密钥,C为密文;设AES解密函数为D,则 P = D(K, C),其中C为密文,K为密钥,P为明文;AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文

    • 非对称密钥加密:

      • 通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密

      • 通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确

      • RSA加密通过生成密文【密文=明文^e mod N】,其中(N,e)是您的公钥,解密通过【明文=密文^d mod N】其中(N,d)是私钥

    • 为什么RSA(非对称加密)比AES(对称加密)慢

      • RSA主要慢在解密过程,因为私钥很长

      • RSA有幂模运算,AES主要是异或、移位等运算

  • 数字证书

    • 数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构

    • 服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对用自己的私钥已申请的公开密钥做数字签名,做成证书

    • 进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,用CA的公钥对数字签名进行验证,如果验证通过,就可以开始通信了

  • SSL握手过程

    • 客户端向服务端发送:随机数A;支持的加密算法与摘要算法

    • 服务端向客户端发送:选择的加密算法与摘要算法;数字证书(含服务端的公钥);随机数B

    • 客户端验证服务器的合法性:用CA的公钥解开数字证书进行验证,并得到服务端公钥

    • 客户端向服务端发送:用服务端公钥加密的随机数C(并且计算出对称加密的公钥K=f(A,B,C))

    • 服务端接收到信息后,用自己的私钥解密得到C,并且计算出对称加密的公钥K=f(A,B,C)

  • 报文摘要

    • 发送方:用哈希函数将报文明文生成报文摘要;将摘要用私钥加密;同时发送明文和加密后的摘要

    • 接收方:用哈希函数将报文明文生成报文摘要1;用公钥将摘要解密,获取摘要2;比较摘要1和2

  • http如何转为https

    • 302 跳转

      • 服务端把所有的 HTTP 流量跳转到 HTTPS 上

      • 有安全漏洞:第一次访问站点的时候如果是 HTTP 就有可能被中间人劫持,很可能都没到 302 跳转的时候就被劫持了

    • HSTS机制

      • 支持 HSTS 的服务端,可以强制访问它的浏览器使用 HTTPS 协议

      • 用户的浏览器一旦得到了 HSTS 的信息,下次再访问站点的时候客户端浏览器就会强制使用 HTTPS

      • 这样就解释了为什么我们使用主流浏览器输入网站域名的时候,都会自动跳转到 HTTPS 了:因为我们访问的大多是主流的大网站

      • 缺点

        • 假如用户的浏览器从未访问过这个站点,这个时候依然会有被劫持风险

        • 如果你的站点的 HTTPS 服务没有完全准备好,不要轻易的开启 HSTS 响应头。因为一旦浏览器得到这个响应头,就会在规定的 max-age 的时间段内强制使用 HTTPS 访问,如果你的服务没准备好,用户就会一直访问失败,并且不能降级

RIP协议

  • 应用层协议

  • 每个路由器维护一张表,记录该路由器到其它网络的”跳数“,路由器到与其直接连接的网络的跳数是1,每多经过一个路由器跳数就加1;更新该表时和相邻路由器交换路由信息;路由器允许一个路径最多包含15个路由器,如果跳数为16,则不可达。交付数据报时优先选取距离最短的路径

一些常用端口

  • FTP(21端口):文件传输协议

  • SSH(22端口):远程登陆

  • DNS(53端口):域名解析

  • HTTP 80

  • HTTPS 443

接口限流

计数器算法

  • 限制一秒钟的能够通过的请求数,比如限流qps为100,算法的实现思路就是从第一个请求进来开始计时,在接下去的1s内,每来一个请求,就把计数加1,如果累加的数字达到了100,那么后续的请求就会被全部拒绝

  • 有一个弊端:如果我在单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能眼巴巴的把请求拒绝,我们把这种现象称为“突刺现象”

漏桶算法

  • 处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就丢弃

  • 在算法实现方面,可以准备一个队列,用来保存请求,另外通过一个线程池定期从队列中获取请求并执行,可以一次性获取多个并发执行

  • 存在弊端:无法应对短时间的突发流量

令牌桶算法

  • 存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝

  • 如果桶中令牌数达到上限,就丢弃令牌

  • 可以准备一个队列,用来保存令牌,另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行

网络攻击

DNS劫持

  • 现象:输入的网址是 http://www.google.com,出来的是百度的页面

  • 解决方法:修改DNS服务器为一个更可靠的服务器;在host文件中记录域名到ip的映射

HTTP劫持

  • 现象:打开一个页面,右下角弹出广告

  • 解决方法:https(加密+认证+报文摘要)

SSL劫持

  • 过程:攻击者将自己伪装成客户端,获取到服务器真实有效的 CA 证书(非对称加密的公钥);将自己伪装成服务器,获取到客户端的之后通信的密钥(对称加密的密钥);有了证书和密钥就可以监听之后通信的内容了

  • 解决方法:客户端不要轻易信任证书;提前预埋证书在本地

SSL剥离

  • 过程:攻击者利用用户对于地址栏中HTTPS与HTTP的疏忽,将所有的HTTPS连接都用HTTP来代替;同时,与目标服务器建立正常的HTTPS连接,受攻击客户端与原始请求服务器之间的全部通信经过了攻击者的转发

  • 解决方法:客户端使用https;使用HSTS机制

SYN洪泛攻击

  • 过程:攻击者发送大量TCP SYN报文段,而不完成第三次握手,服务器不断为这些半开连接分配资源(TCP每建立一个连接都要分配资源:一些变量、发送/接收缓存等),导致服务器的连接资源被耗尽

  • 解决方法:

    • 服务器接收到SYN报文段时,不会建立半开连接,而是生成一个初始TCP序列号x,并发送以x为序列号的SYNACK分组

    • 只有收到确认号为x+1的ACK报文段,才会建立全连接(即完成了三次握手)

ARP欺骗

  • 过程:攻击者发送ARP包,其中IP地址到MAC地址的映射是错误的

  • 解决方法:把映射写死

反向代理 vs 正向代理

正向代理

  • 客户端代理,服务端不知道实际发起请求的客户端

  • 比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了

  • 条件GET请求

    • 为避免代理服务器中存的数据已过期,web缓存器(即代理服务器)每次都会向源服务器发送条件GET请求(在请求报文中有if-modified-since),仅当自指定日期之后该对象被修改过,源服务器才会发送该对象给web缓存器
  • 作用

    • 访问原来无法访问的资源

    • 可以做缓存,加速访问资源

    • 对客户端访问授权,上网进行认证

    • 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息

反向代理

  • 以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器

  • 服务端代理,客户端不知道实际提供服务的服务端

  • 作用

    • 保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网

    • 负载均衡,通过反向代理服务器来优化网站的负载

MSS vs MTU

MSS

  • 最大报文段长度

  • TCP可从缓存中取出并放入报文段中的最大数据数量

  • MSS<=MTU-TCP首部长度(因为要保证一个TCP报文段可以装进帧中)

MTU

  • 最大传输单元

  • 最大链路层帧长度

断点续传

含义

将一个文件按照一定的规则人为的分割成多个小文件,然后客户端每次只上传一个小文件(当然我们也可以利用多线程技术每次上传多个小文件),服务器接收到上传过来的小文件后根据一定的规则来组合这些小文件。如果在上传过程中出现网络中断等意外情况,下次再次上传时可以从已经上传的部分继续上传,而不是重新上传

实现

  • 在 http报文头部 里添加两个参数:客户端请求时发送的 Range 和服务器返回信息时返回的 Content-Range,Range 用于指定第一个字节和最后一个字节的位置

  • http响应状态行会有“partial content”字样

  • 如果在客户端请求报文头中,对 Range 填入了错误的范围值,服务器会返回 416 状态码

如何判断服务器是否支持断点续传

  • 判断服务端是否只 HTTP/1.1 及以上版本,如果是则支持断点续传,如果不是则不支持

  • 服务端返回响应的头部是否包含 Access-Ranges ,且参数内容是 bytes

当服务器端的文件发生改变时,客户端再次向服务端发送断点续传请求,如何处理

  • 法一:最后修改时间

    • 先前服务器发送给了客户端一个 Last-Modified 时间

    • 客户端把这个时间 写入if-Modified-Since,发给服务器

    • 服务器进行最后修改时间验证后,告知客户端是否需要重新从服务器端获取新内容

    • 服务器返回状态码 206 代表不需要重新获取接着下载就行,200代表需要重新获取

  • 法二:版本号

    • 法一存在的问题

      • 某些文件只是修改了修改时间而内容却没变,这时我们并不希望客户端重新缓存这些文件

      • 某些文件修改频繁,有时一秒要修改十几次,但是 if-Modified-Since 是秒级的

      • 部分服务器无法获得精确的修改时间

    • 使用etag:文件版本号

    • 如果etag没有发生改变服务器将向客户端发送剩余的部分,否则发送全部

秒传

将文件的MD5发送给服务器,服务器根据传输过来的MD5判断服务器上是否存在相同类型的文件,如果存在就将文件复制一份,而不是本地上传

服务端如何处理多个客户端的socket

每有一个Socket请求的时候,就从线程池中取一个线程来处理这个请求

线程池的好处

  • 线程复用,创建线程耗时,回收线程慢

  • 防止短时间内高并发,指定线程池大小,超过数量将等待,方式短时间创建大量线程导致资源耗尽,服务挂掉

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