最近学了些TCP/IP相关的内容,做了些笔记。

关于HTTP(S)、TLS的内容链接

网络分层

网络分层的原因

  • 复杂的程序都需要分层,各层次之间是独立的

  • 某一层并不需要知道它的下一层是如何实现的,而仅仅需要知道该层通过层间的接口所提供的服务,这样能降低问题的复杂度,上层工作不影响下层的工作

  • 易与多人协作,易与实现和维护,促进标准化工作

  • 只要是在网络上跑的包,都是完整的,可以有下层没上层,绝不可能有上层没下层

  • 对于TCP来说,只要想发出去包,就必须要有IP层和MAC层

网络如何分层

  • 一般有五层模型和七层模型,按个人理解,七层模型只是把五层模型最上层的应用层给细化了
    1. 物理层(中继器、集线器、HUB、网线)
    2. 数据链路层(MAC)
    3. 网络层(IP)
    4. 传输层(TCP/UDP)
    5. 应用层(HTTP(S))

网络分层运行的过程

  1. 接收过程

当一个包经过网口时,首先看看要不要请进来处理一下(配置了混杂模式的网口,凡是经过的都拿进来),拿进来以后交给一段程序处理,于是调用process_layer2(buffer),当然这是假函数,但你知道肯定有类似功能的函数的,这个函数功能是从buffer中摘掉二层(MAC)的头查看。

假如发现这个包的MAC地址与自己相符,说明是发给你的,于是调用process_layer3(buffer),摘掉三层(IP)的头查看,如果IP地址不是自己的,就应该转发出去,如果IP地址是自己的,就根据IP头的指示,拿掉三层的头,进程下一层的处理,如果地址是TCP的,调用process_tcp(buffer),如果是UDP的调用process_udp(buffer)

假设是TCP的,调用process_tcp(buffer),查看四层的头,看是一个发起,还是应答,或者是一个正常的数据包,分别交给不同的逻辑处理。如果是发起或者应答,接下来可能要发送一个回复包;如果是一个正常的数据包,就需要交给上层了。交给谁呢?是不是有 process_http(buffer) 函数呢?

没有的,如果你是一个网络包处理程序,你不需要有process_http(buffer),而是应该交给应用去处理。交给哪个应用呢?在四层的头里面有端口号,不同的应用监听不同的端口号。如果发现浏览器应用在监听这个端口,那你发给浏览器就行了。至于浏览器怎么处理,和你没有关系。

浏览器自然是解析 HTML,显示出页面来。电脑的主人看到页面很开心,就点了鼠标。点击鼠标的动作被浏览器捕获。浏览器知道,又要发起另一个 HTTP 请求了,于是使用端口号,将请求发给了你。

  1. 发送过程

收到了一个封装好的HTTP包后,首先调用send_tcp(buffer)buffer里就是HTTP请求的内容,这个函数里面加一个TCP的头,记录下浏览器给的源端口号,一般是80(HTTPS就443)。

然后调用send_layer3(buffer)buffer里面已经有了HTTP的头和内容,以及TCP的头,这个函数里面加一个IP的头,记录下源IP地址和目标IP地址。

然后调用send_layer2(buffer)buffer里面已经有了HTTP的头和内容、TCP的头,以及IP的头,这个函数里面加一个MAC的头,记录源MAC地址,得到的就是本机器的 MAC 地址和目标的 MAC 地址。不过,这个还要看当前知道不知道,知道就直接加上;不知道的话,就要通过一定的协议处理过程,找到 MAC 地址。反正要填一个,不能空着。

只要buffer里面的内容完整,就可以从网口发出去了。

数据链路层 MAC

  • MAC的全称是Medium Access Control,即媒体访问控制。

  • 以太网规定,连入网络的所有设备,都必须具有”网卡”接口。数据包必须是从一块网卡,传送到另一块网卡。网卡的地址,就是数据包的发送地址和接收地址,这叫做MAC地址。

  • 每块网卡出厂的时候,都有一个全世界独一无二的MAC地址,长度是48个二进制位,通常用12个十六进制数表示。

  • 前6个十六进制数是厂商编号,后6个是该厂商的网卡流水号。有了MAC地址,就可以定位网卡和数据包的路径了。

  • 一块网卡获取另一块网卡MAC地址的方式:ARP协议,当已知IP时请求MAC地址

  • ARP的工作方式:广播,向同个子网络内所有计算机发送数据包,让每台计算机自己判断自己是否为接收方

网络层 IP

  • ARP寻找MAC的广播方式只能在子网络内使用,每个子网络之间通信需要IP协议
  • Linux 上查看IP地址的命令:ifconfigip addr、没有的话自行安装net-toolsiproute2
  1. IP地址

    • IPv4的网络地址由32个二进制位组成

    • IP 地址是一个网卡在网络世界的通讯地址,相当于我们现实世界的门牌号码
  2. CIDR

    • 32位的IP地址分成了5类,但因为太浪费了,所以使用CIDR
    • CIDR代替ABC类分配IP地址段,注意私有IP段,CIDR伴随广播地址和子网掩码
  3. IP的作用

    • IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个是确定哪些地址在同一个子网络。
  4. 从IP得到MAC的方式

    • 因为IP数据包是放在以太网数据包里发送的,所以我们必须同时知道两个地址,一个是对方的MAC地址,另一个是对方的IP地址。通常情况下,对方的IP地址是已知的,但是我们不知道它的MAC地址。分两种情况
    1. 如果两台主机不在同一个子网络,那么事实上没有办法得到对方的MAC地址,只能把数据包传送到两个子网络连接处的”网关”(gateway),让网关去处理。
    2. 如果两台主机在同一个子网络,那么我们可以用ARP协议,得到对方的MAC地址。
  • DHCP请求IP地址,DHCP附送PXE协议安装OS

ICMP 和 ping

  • ping 是基于 ICMP 工作的,ICMP全称Internet Control Message Protocol,就是互联网控制报文协议。

  • ICMP 报文是封装在 IP 包里面的。因为传输指令的时候,肯定需要源地址和目标地址。它本身非常简单。

  1. 查询报文

    • ping 就是ICMP的查询报文,是一种主动请求,并获得主动应答的ICMP协议,不过ping在ICMP后面增加了自己的格式
    • ping 主动请求的发送:ICMP ECHO REQUEST
    • ping 主动请求的回复:ICMP ECHO REPLY
    • 比原生ICMP多了两个字段:标识符、序号,以及可选存放请求的时间值,可计算往返时间
  2. 差错报文

    • 终点不可达:3,分为网络不可达、主机不可达、协议不可达、端口不可达、需要进行分片但设置了不分片等
    • 源抑制:4,让源站放慢发送速度
    • 超时:11,超过网络包的生存时间还没到达目标
    • 重定向:5,让下次发给另一个路由器

  1. ping 的发送和接收过程

    • 可以看出ping是使用了ICMP里的ECHO REQUESTECHO REPLY
  2. TTL (TimeToLive)

    • 在IPv4中, TTL是IP协议的一个8个二进制位的值(0-255),这个值可以被认为是数据包在internet系统中可以跳跃的次数上限。
    • TTL是由数据包的发送者设置的, 在前往目的地的过程中, 每经过一台主机或设备, 这个值就要减少一点。
    • 如果在数据包到达目的地前, TTL值被减到了0,那么这个包将作为一个ICMP错误的数据包被丢弃。
  3. 差错报文的应用Traceroute

    • Traceroute 故意设置特殊的TTL,来追踪去往目的地时沿途经过的路由器
    • 对目的IP地址发送UDP包,设置TTL为1,即到达第一个路由就挂掉了,然后返回一个ICMP差错报文包,类型是超时
    • 然后设置TTL为2,继续到第二个路由就挂了,返回超时,逐步迭代这个过程取得整个路径上的路由IP和延迟
    • 有些路由不会回复这个ICMP,这就是Traceroute返回内容部分空白的原因
    • 另外一个作用是故意设置不分片,从而确定路径的MTU

TCP 和 UDP 的区别

  1. TCP 面向连接,UDP 面向无连接

  2. TCP 提供可靠交付,UDP 不可靠

    • TCP 连接传输的数据,无差错,不丢失,不重复,按序到达
    • UDP 继承 IP 包的特性,不保证不丢失,不保证按顺序到达
  3. TCP 基于字节流,UDP 基于数据报

    • TCP 发送的时候是一个流,没头没尾
    • 虽然 IP 包是一个个的 IP 包,TCP 自己维护成了流
    • UDP 继承了 IP 的特性,基于数据报,一个个地发,一个个地收
  4. TCP 有拥塞控制, UDP 没有

    • TCP 意识到包丢失或者网络环境差后,根据情况调整行为,调节发送速度
    • UDP 没有意识,应用叫发包就发
  5. TCP 是有状态服务,UDP 是无状态服务

    • TCP 精确记录了发送、接收状态
    • UDP 不记录状态

UDP

UDP 包头格式

  • 源端口号 | 目的端口号
  • 16位UDP长度 | 16位UDP校验和
  • 数据

UDP 特点

  1. 沟通简单,认为网络通路默认就是很容易送达的,不容易被丢弃的

  2. 轻信他人, 不会建立连接,虽然有端口号,但是监听后任何人都能发数据给他,他也可以发数据给任何、任意多人

  3. 愣头青,不会根据网络情况进行发包的拥塞控制,无论丢包成什么样还是该怎么发就怎么发

UDP 使用场景

  1. 需要资源少,网络环境好的内网,或对丢包不敏感的应用。如 DCHP 和 TFTP 就是基于 UDP 协议的,因为BIOS资源少不适合维护TCP那种复杂的状态机

  2. 不需要一对一沟通建立连接,而是可以广播的应用。UDP 面向无连接的功能可以承载广播或者多播的协议,基于 UDP 的 DHCP 就是广播的形式

  3. 需要处理速度快,时延低,可容忍少数丢包,但即便网络拥塞也要不停歇发包的应用。比如网络直播,用户不关心过时数据,老数据丢了也就丢了,但不能因为拥塞而停歇了

UDP 使用例子

  1. 网页或者App的访问

    • QUIC协议
    • 移动互联,网络经常变换,TCP会经常重连很耗时,基于UDP的QUIC就比较合适
  2. 流媒体协议

    • TCP 的严格顺序传输不适合直播,用户不在意老数据而是要实时性,宁可丢包也不要卡顿
    • 很多直播应用基于 UDP 实现自己的视频传输协议
  3. 实时游戏

    • 游戏的实时性要求很高,而且服务器需要沟通很多客户端(玩家),在异步IO机制引入之前,UDP是应对海量客户端连接的策略
    • TCP的强顺序问题,对战游戏对网络要求简单,如FPS只需传输玩家位置和行为等,客户端解析响应并渲染场景,玩家并不关心过期数据,如果一个数据包丢失导致卡顿,下一秒就被爆头了。用UDP自定义可靠协议,自定义重传策略,能尽量降低延迟,减少网络问题对游戏性能的影响
  4. IoT 物联网

    • 物联网设备终端资源少,维护TCP开销太大
    • 物联网对实时性要求高,TCP容易导致高延迟
    • Google子公司推出的物联网通信协议 Thread 就是基于 UDP 的
  5. 移动通信领域

    • 4G 网络的数据协议 GTP-U 是基于 UDP 的

UDP 小结

  1. 如果将 TCP 比作成熟的社会人,UDP 则是头脑简单的小朋友。TCP 复杂,UDP 简单;TCP 维护连接,UDP 谁都相信;TCP 会坚持知进退;UDP 愣头青一个,勇往直前;

  2. UDP 虽然简单,但它有简单的用法。它可以用在环境简单、需要多播、应用层自己控制传输的地方。例如 DHCP、VXLAN、QUIC 等。

TCP

  • TCP的seq是32位的计数器,每4微秒加1,计算可知284分钟即4.73小时重置一次

TCP 包头格式

  • 源端口号 | 目标端口号
  • 包序号:seq
  • 确认序号:ack
  • 首部长度 | 保留 | URG | ACK | PSH | RST | SYN | FIN | 16位窗口大小
  • 校验和 | 紧急指针
  • 选项
  • 数据本体
  1. 首先与UDP一样是源端口和目标端口号,用于确定发给哪个应用
  2. seq序号解决乱序问题,确定每个包的先来后到,是个32位的计数器,每4微秒加1,计算可知284分钟即4.73小时重置一次
  3. ack的值是对方发来的TCP包内seq的值+1,解决不丢包问题
  4. 状态位,SYN是发起连接,ACK是回复,RST是重新连接,FIN是结束连接,这是TCP面向连接的体现,维护连接状态,这些带状态位的包的发送会引起双方的状态变更
  5. 窗口大小,做流量控制,双方各自声明一个窗口,标识自己当前的处理能力,不要发送得太快也别太慢

TCP 的特征

  • 顺序问题,稳重不乱;
  • 丢包问题,承诺靠谱;
  • 连接维护,有始有终;
  • 流量控制,把握分寸;
  • 拥塞控制,知进知退。

三次握手四次挥手

  1. 三次握手

    • ① A:您好,我是 A ② B:您好 A,我是 B ③ A:您好 B
    • TCP连接的建立称为三次握手,“请求 -> 应答 -> 应答之应答”,让双方的消息都“有去有回”
    • 第三次握手,A应答B的应答包,能解决一个问题:B收到了来自很久之前A的多次申请连接的SYN包,如果两次握手,B这时建立了连接就等于单相思,所以需要“应答之应答”
    • 为什么不四次握手?因为就算40次握手也不能真正保证连接就建立了,只要双方都有去有回就基本认为建立了,并且一般A和B连接建立后就开始发数据,一但A方开始发送数据,问题就解决了
    • 如果连接建立后A一直不发数据,这个在HTTP有keepalive机制,即使没有真实数据包也有探活包
    • B也可以设计对于长时间不发包的A主动关闭连接
    • 大写ACK与小写ack的区别:大写的是状态位,当为1时表示包是回复包,小写是确认序号,值是对方的序号seq+1

一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的 SYNACK 之后,发送 ACKACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACKACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

  1. 四次挥手

    • 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
    1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送
    2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号
    3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A
    4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1
    • 上面2与3的过程中,B可能还有要发给A的数据没发送完,就首先ACK了A的FIN后,等处理完数据后再发送FIN给A,所以一共需要4次挥手。

  2. 白话解释四次挥手

    • ① A:B 啊,我不想玩了 ② B:哦,你不想玩了啊,我知道了 ③ B:A 啊,好吧,我也不玩了,拜拜 ④ A:好的,拜拜。

    • A 开始说“不玩了”,B 说“知道了”,这个回合是没什么问题的,因为在此之前,双方还处于合作的状态,如果 A 说“不玩了”,没有收到回复,则 A 会重新发送“不玩了”。但是这个回合结束之后,就有可能出现异常情况了,因为已经有一方率先撕破脸。

    • 一种情况是,A 说完“不玩了”之后,直接跑路,是会有问题的,因为 B 还没有发起结束,而如果 A 跑路,B 就算发起结束,也得不到回答,B 就不知道该怎么办了。另一种情况是,A 说完“不玩了”,B 直接跑路,也是有问题的,因为 A 不知道 B 是还有事情要处理,还是过一会儿会发送结束。

    • 断开的时候,我们可以看到,当 A 说“不玩了”,就进入 FIN_WAIT_1 的状态,B 收到“A 不玩”的消息后,发送知道了,就进入 CLOSE_WAIT 的状态。

    • A 收到“B 说知道了”,就进入 FIN_WAIT_2 的状态,如果这个时候 B 直接跑路,则 A 将永远在这个状态。TCP 协议里面并没有对这个状态的处理,但是 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。

    • 如果 B 没有跑路,发送了“B 也不玩了”的请求到达 A 时,A 发送“知道 B 也不玩了”的 ACK 后,从 FIN_WAIT_2 状态结束,按说 A 可以跑路了,但是最后的这个 ACK 万一 B 收不到呢?则 B 会重新发一个“B 不玩了”,这个时候 A 已经跑路了的话,B 就再也收不到 ACK 了,因而TCP 协议要求 A 最后等待一段时间 TIME_WAIT,这个时间要足够长,长到如果 B 没收到 ACK 的话,“B 说不玩了”会重发的,A 会重新发一个 ACK 并且足够时间到达 B。

    • A 直接跑路还有一个问题是,A 的端口就直接空出来了,但是 B 不知道,B 原来发过的很多包很可能还在路上,如果 A 的端口被一个新的应用占用了,这个新的应用会收到上个连接中 B 发过来的包,虽然序列号是重新生成的,但是这里要上一个双保险,防止产生混乱,因而也需要等足够长的时间,等到原来 B 发送的所有的包都死翘翘,再空出端口来。

    • 等待的时间设为 2MSL,MSL是Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个TTL 域,是 IP 数据报可以经过的最大路由数,每经过一一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。协议规定 MSL 为2 分钟,实际应用中常用的是 30 秒,1 分钟和 2 分钟等。

    • 还有一个异常情况就是,B 超过了 2MSL 的时间,依然没有收到它发的 FIN 的 ACK,怎么办呢?按照 TCP 的原理,B 当然还会重发 FIN,这个时候 A 再收到这个包之后,A 就表示,我已经在这里等了这么长时间了,已经仁至义尽了,之后的我就都不认了,于是就直接发送 RST,B 就知道 A 早就跑了。

  3. TCP 状态机

  • 把上面两个时序图合并起来就是这个TCP状态机图

  • 加黑加粗的部分是主要流程,阿拉伯数字的序号是连接建立的顺序,大写中文数字的序号是连接断开的顺序。加粗实线是客户端A的状态变化过程,加粗虚线是服务端B的状态变化过程。细虚线是其他非主流过程。

TCP 的顺序机制

  • 每个包都有一个ID,按照ID一个个发送

  • 累计确认(累计应答)

    • 发送端和接收端分别有缓存保存记录所有发送和接收的包
  1. 发送端窗口

    • 发送端的缓存里是按照包ID排列的,有四种类型

      1. 发送了且已确认
      2. 发送了且尚未确认
      3. 还没发送但等待发送
      4. 还没发送且暂时不会发送

    • 上面3和4的区分是流量控制

      1. 流量控制使用窗口(Advertised Window),窗口大小等于上面2+3部分,即发送未确认和未发送但可发送的
      2. LastByteAcked:第一部分和第二部分的分界线
      3. LastByteSent:第二部分和第三部分的分界线
      4. LastByteAcked + AdvertisedWindow:第三部分和第四部分的分界线
  2. 接收端窗口

    • 接收端的缓存里是按照包ID排列的,有三种类型
      1. 接收且确认过
      2. 尚未接收但可接收
      3. 尚未接收且不能接收

    • 流量控制使用窗口(AdvertisedWindow),窗口大小是上面第2部分,需要计算

      1. MaxRcvBuffer:最大缓存的量;
      2. LastByteRead 之后是已经接收了,但是还没被应用层读取的;
      3. NextByteExpected 是第一部分和第二部分的分界线。
      • NextByteExpectedLastByteRead 的差其实是还没被应用层读取的部分占用掉的 MaxRcvBuffer 的量,我们定义为 A
      • AdvertisedWindow 其实是 MaxRcvBuffer 减去 A,即第2部分的大小
      • 也就是:AdvertisedWindow = MaxRcvBuffer - ((NextByteExpected-1) - LastByteRead)
      • 第2部分和第3部分的分界线就是 NextByteExpected + AdvertisedWindow,即 LastByteRead + MaxRcvBuffer

TCP 的丢包重发机制

  1. 确认与重发机制

    • 超时重试,没有ACK的包都有设一个定时器,超过一定时间就重新尝试
    • 超时时间需要大于往返时间 RTT,也不宜过长
  2. 自适应重传算法

    • TCP 通过采样 RTT 时间和 RTT 的波动范围,进行加权平均,算出一个值,还需要根据新状况不断变化
    • 超时触发重传的问题是超时周期可能很长
  3. 快速重传机制

    • 当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间格,于是发送三个冗余的 ACK,客户端收到后,就在定时器过期之前,重传丢失的报文段。
    • 还有一种方式 SACK (Selective Acknowledgment),在TCP头里加一个SACK,可以把可以将缓存的地图发送给发送方。

TCP 的流量控制机制

  • 如果发送方发送太猛,接收方处理不过来导致缓存中没有空间,可以通过确认信息修改窗口大小,甚至设为0,使发送方暂时停止发送

  • 当发送方发现自己窗口被调整到0后,会定时发送窗口探测数据包,看是否有机会调整窗口大小

  • 当接收方比较慢时,要防止低能窗口综合征,避免空出一个字节就通知发送方然后又填满窗口。当窗口太小时,可以暂停更新窗口,直到窗口达到一定大小或者缓冲区一半为空,才更新窗口

TCP 的拥塞控制机制

  1. 也是通过窗口大小控制,滑动窗口 rwnd 是怕发送方把接收方缓存占满,拥塞窗口 cwnd 是怕把网络塞满

    • LastByteSent - LastByteAcked <= min {cwnd, rwnd},是拥塞窗口和滑动窗口共同控制发送的速度

    • 发送方难以判断网络是否满的方法,在TCP看来网络路径是一个黑盒

    • TCP 发送包常被比喻为往一个水管里面灌水,而 TCP 的拥塞控制就是在不堵塞,不丢包的情况下,尽量发挥带宽。

  1. TCP 的拥塞控制主要用来避免两种现象:包丢失和超时重传

    • 一旦出现了这些现象就说明,发送速度太快了,要慢一点。但是一开始我怎么知道速度多快呢,我怎么知道应该把窗口调整到多大呢?

    • 慢启动:一开始 cwnd 大小为一个报文段,当收到确认时指数型增大cwnd大小

    • 指数型涨到一个值为65535字节的 ssthresh 时降为线性增长,每8个确认cwnd增加1,但总有溢出的时候。

    • 当出现拥塞即丢包时,需要超时重传,ssthresh设为cwnd/2cwnd设为1,重新开始慢启动,一夜回到解放前。

  2. 快速重传

    • 当接收端发现丢了一个中间包时,发送三次前一个包的ACK,于是发送端就会快速重传,不必等待超时重传。

    • 此时cwnd = cwnd / 2, sshthresh = cwnd,当三个包返回时,cwnd = sshthresh + 3

    • 也就是不用回到解放前,只是减半而已

    • 这种机制导致延迟高的情况下,反而降低速度,导致本文前面UDP部分里说的TCP的问题

      1. 丢包不代表通道满了,可能本身公网就是会丢包的,这时其实并没有拥塞

      2. TCP 的拥塞控制要等到将中间设备都填充满了才发生丢包,从而降低发送速度,此时已经晚了,其实TCP只要通道满了就应该开始控制了,而不应该等到连缓存都满了才控制

    • 解决方法:BBR 拥塞算法,找到一个平衡点,填满管道但是不填满中间设备的缓存,达到高带宽和低时延的平衡

TCP 小结

  • TCP 包头很复杂,但是主要关注五个问题,顺序问题,丢包问题,连接维护,流量控制,拥塞控制
  • 连接的建立是经过三次握手,断开的时候四次挥手
  • 顺序问题、丢包问题、流量控制都是通过滑动窗口来解决的
  • 拥塞控制是通过拥塞窗口来解决的

 评论

 无法加载Disqus评论系统,请确保您的网络能够正常访问。

©2019 派大星星星星

本站使用 Material X 作为主题 , 总访问量为 次 。