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 次方。




