暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

网络协议 -- 传输层

小D学Java 2019-07-20
183

UDP

UDP的特点

  • UDP是无连接的

  • 不保证可靠交互(包可能丢失)

  • 无序到达

  • 基于数据报的,一个一个地发,一个一个地收

  • UDP是无状态服务


UDP包头

UDP的报头仅8字节,比TCP的20字节小很多.


UDP使用场景

  • 需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用

  • 不需要一对一沟通,不需要建立连接,而是可以广播的应用

  • 需要处理速度快,时延低,可以容忍少数丢包


TCP

TCP的特点

  • TCP是建立链接的

  • TCP提供可靠性交互。过 TCP 连接传输的数据,无差错、不丢失、不重复、并且按序到达。

  • TCP 是面向字节流的。发送的时候发的是一个流

  • TCP 是可拥塞控制.能根据网络环境调整发送包的速度

  • TCP是有状态服务

TCP报头

  • 序号是为了解决乱序问题

  • 确认序号是为了保证不丢失

  • 窗口大小时为了控制发送速度,做流量控制

三次握手

建立链接

之所以是三次,是因为通信双方都需要一个来回
的通信.如A发送给B,B应答给A.那么对于A来讲它的消息就是有来回的.相对的,对于B来讲,若B只是发出一次消息而没收到消息则无法保证A可达.因此需A再发送一个应答之应答来回应B的消息.当然双方各有一次来回还是无法保证消息真的可靠,但即使再来几百回也无法完全保证可达.因此双方各有一次消息来回即默认通信连接.

当然好在建立TCP链接建立后,一般A会马上发消息,A 发给 B 的应答丢了,当 A 后续发送的数据到达的时候,B 可以认为这个连接已经建立,或者 B 压根就挂了,A 发送的数据,会报错,说 B 不可达.

开发中一般建议开启keepalive
机制,这样即使A不发送数据,在探测包的作用下,我们也能发现链接失效.

沟通序号

三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是TCP 包的序号的问题

状态变化时序图

四次挥手

状态时序图

之所以是四次,A发的FIN是通知,然后B的第一个ACK是应答表示收到这个结束链接的请求.如果A没收到这个应答会重新发送FIN请求.如果B在回复完第一个ACk时断开了链接,那么A将永远在FIN-WAIT2
的状态,因为TCP协议木有对这个状态的处理.好在Linux有一个可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。

之所以不能在第一个ACK就结束的原因是B可能会有消息还没处理完,需等待处理完才能应答A.那么当B处理完了会发送一个B的FIN ACK
来表示B已准备结束链接.

为了确保A能收到FIN ACK
则B会等待A的应答,如果B收不到A的应答则会再重发一个FIN ACK
.为了确保A能在重发的时候收到这个ACK,TCP协议规定A在接收到FIN ACK
并回复ACK后需等待2MSL(2倍的报文最大生存时间)的时间.

TCP状态机

累计应答

为了保证顺序性,每一个包都有顺序性.tcp针对发送的包会采取应答来保证不丢包,但应答不是一个个来的,而是会应答某个之前的id,表示前面的包都收到了,这种模式称为累计确认或者累计应答cumulative acknowledgment)。

发送端保持的包记录的数据结构

在 TCP 里,接收端会给发送端报一个窗口的大小,叫Advertised window

接收端保持的包记录的数据结构

超时重传机制

方式一

采样 RTT 的时间,然后进行加权平均,算出一个值,而且这个值还是要不断变化的,因为网络状况不断的变化。除了采样 RTT,还要采样 RTT 的波动范围,计算出一个估计的超时时间。由于重传时间是不断变化的,我们称为自适应重传算法Adaptive Retransmission Algorithm)。

TCP 的策略是超时间隔加倍每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍两次超时,就说明网络环境差,不宜频繁反复发送。

方式二

有一个可以快速重传的机制,当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间格,于是发送三个冗余的 ACK,客户端收到后,就在定时器过期之前,重传丢失的报文段。

流量控制

接收端可以通过调整窗口大小来调整发送端的发送速度.

拥塞控制

前面的滑动窗口 rwnd 是怕发送方把接收方缓存塞满,而拥塞窗口 cwnd,是怕把网络塞满。TCP 的拥塞控制主要来避免两种现象,包丢失超时重传

在理想状态下,通道的容量 = 带宽 × 往返延迟。如果我们设置发送窗口,使得发送但未确认的包为为通道的容量,就能够撑满整个管道。

Socket编程

Socket传参

Socket 编程进行的是端到端的通信,往往意识不到中间经过多少局域网,多少路由器,因而能够设置的参数,也只能是端到端协议之上网络层和传输层的。

在网络层,Socket 函数需要指定到底是 IPv4 还是 IPv6,分别对应设置为 AF_INET 和 AF_INET6。在传输层指定到底是 TCP 还是 UDP。TCP 协议是基于数据流的,所以设置为 SOCK_STREAM,而 UDP 是基于数据报的,因而设置为 SOCK_DGRAM。

TCP 协议的 Socket 程序函数调用过程

在内核中,为每个 Socket 维护两个队列。一个是已经建立了连接的队列,这时候连接三次握手已经完毕,处于 established 状态;一个是还没有完全建立连接的队列,这个时候三次握手还没完成,处于 syn_rcvd 的状态。

在服务端等待的时候,客户端可以通过 connect 函数发起连接。先在参数中指明要连接的 IP 地址和端口号,然后开始发起三次握手。内核会给客户端分配一个临时的端口。一旦握手成功,服务端的 accept 就会返回另一个 Socket。监听的 Socket 和真正用来传数据的 Socket 是两个,一个叫作监听 Socket,一个叫作已连接 Socket

基于 UDP 协议的 Socket 程序函数调用过程

UDP 是没有连接的,所以不需要三次握手,也就不需要调用 listen 和 connect,但是,UDP 的的交互仍然需要 IP 和端口号,因而也需要 bind。UDP 是没有维护连接状态的,因而不需要每对连接建立一组 Socket,而是只要有一个 Socket,就能够和多个客户端通信。也正是因为没有连接状态,每次通信的时候,都调用 sendto 和 recvfrom,都可以传入 IP 地址和端口。

最大链接数

最大连接数,系统会用一个四元组来标识一个 TCP 连接。{本机 IP, 本机端口, 对端 IP, 对端端口}

服务器通常固定在某个本地端口上监听,等待客户端的连接请求。因此,服务端端 TCP 连接四元组中只有对端 IP, 也就是客户端的 IP 和对端的端口,也即客户端的端口是可变的,因此,最大 TCP 连接数 = 客户端 IP 数×客户端端口数。对 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是服务端单机最大 TCP 连接数,约为 2 的 48 次方。


文章转载自小D学Java,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论