传输层---深入理解UDP/TCP协议
传输层:负责数据能够从发送端传输到接收端
我们先来看一下端口号:
1> 端口号(port)标识了一个主机上进行通信的不同应用程序.
在TCP/IP当中,用"源IP","源的端口号","目的IP","目的端口号","协议号"这五元组来标识一个通信.
2> 端口号范围划分
0-1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议,它们的端口号都是固定的.
1024-65535:操作系统动态分配的端口号,客户端程序的端口号,就是由操作系统从这个范围分配的
3>认识知名端口号(cat /etc/services指令可以查看端口号)
ssh服务器端口号:22
ftp服务器端口号:21
telent服务器端口号:23
http服务器端口号:80
https服务器端口号:443
注意我们自己写程序时,要尽量避开这些端口号
关于端口号我们需要注意的两个问题
1.一个进程是否可以bind多个端口号?(可以)
2.一个端口号是否可以被多个端口号绑定?(不可以,若是一个进程已经绑定,再去fork(),是可以实现的)
二.查看网络和服务器pid的重要工具
通过 netstat -n | grep 文件名 来查看网络状态
通过 pidof 进程名 查看进程id,查看服务器进程比较方便
三.UDP协议
1>UDP协议端格式
2>UDP特点
①无连接:知道对端的IP和端口号就直接进行传输,不需要进行建立连接
②不可靠:没有确认机制,没有重传机制,如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息.
③面向数据报:不够灵活的控制读写数据的次数和数量.
④全双工:socket既能读也能写
面向数据报----应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并.
3>UDP缓冲区
UDP没有真正意义上的发送缓冲区,调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输工作.
UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报的顺序和发送的一致,如果缓冲区满了,再到达就会丢弃.
4>UDP注意事项
UDP协议首部中有一个16位的最大长度,也就是说一个UDP能够传输的数据最大长度是64K(包含UDP的首部)
那么64K在互联网环境下,是一个非常小的数字.
如果我们需要传输的数据超过64K,就需要在应用层手动的分包,多次发送,并在接收端手动拼装.
5>基于UDP的应用层协议
NFS:网络文件系统
TFTP:简单文件传输协议
DHCP:动态主机配置协议
BOOTP:启动协议(用于无盘设备启动)
DNS:域名解析协议
四.TCP协议
(传输控制协议) ---对数据的传输进行一个详细的控制
1>TCP协议段格式
2>面向字节流:
创建一个TCP的socket,同时就会再内核中创建一个发送缓冲区和接收缓冲区.由于缓冲的存在,就不需要读和写一一匹配.
3>粘包问题
由于TCP是面向字节流,所以就会出现粘包问题,那该如何处理呢?就需要明确两个包之间的边界
对于UDP不存在粘包问题,因为UDP是面向数据包的.
4>基于TCP的应用层协议
HTTP HTTPS SSH Telnet FTP SMTP
TCP机制
1>连接管理机制
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接.
这里注意如果触发了,捎带应答机制,就有可能变成三次
客户端状态转化:
a>:[CLOSED_SYN_SENT]客户端调用connect ,发送同步报文段
b>:[SYN_SENT-->ESTABLISHED]connect调用成功,则进入ESTABLISHEND状态,开始写数据;
c>:[ESTABLISHED-->FIN_WAIT_1]客户端主动调用close,向服务器发送结束报文段,同时进入FIN_WAIT_1;
d>:[FIN_WAIT_1-->FIN_WAIT_2]客户端收到服务器对结束报文段的回应确认,则进入FIN_WAIT_2,开始等待服务器关闭
e>:[FIN_WAIT_2-->TIME_WAIT]客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发送LAST_ACk给服务器;
f>:[TIME_WAIT-->CLOSED]客户端要等待一个2MSL(报文最大生存时间),才会进入CLOSED状态.
服务器状态转化:
a>: [CLOSED-->LISTEND]服务器调用listen后进入LISTEN状态;
b>: [LISTEND-->SYN_RCVD]一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端 发 送SYN确认报文;
c>: [SYN_RCVD-->ESTABLISHED]服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了;
d>: [ESTABLISHED-->CLOSE_WAIT]当客户端主动关闭链接(调用close),服务器就会收到结束报文段,服务器返回确认报文段,并进入CLOSE_WAIT;
e>: [CLOSE_WAIT-->LAST_ACK]进入CLOSE_WAIT后说明服务器要准备关闭连接了,但是服务器要处理完之前的数据,当服务器真正调用close关闭;连接时.会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACk到来(客户端确认收到了FIN);
f>: [LSAT_ACK-->CLOAED]服务器收到了客户端的ACK,彻底关闭连接;
理解TIME_WAIT状态
TCP协议规定,主动关闭连接的一方先处于TIME_WAIT状态,等待两个MSL(数据报从发送端到接收端所用的最大的时间)的时间后才能回到CLOSED状态
服务器在TIME_WAIT期间不能监听同样的server端口
MSL在RFC1122中规定为2分钟,但是各种操作系统的实现不同,在centen7上默认的是60s
cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值
2>确认应答(ACK)机制
3>超时重传机制(内核实现)
上图中主机A发送数据给B后,可能因为网络堵塞的原因,数据无法到达主机B
如果主机A在一个特定的时间间隔内没有收到B发来的确认应答,就会进行重发(主机A未收到B发来的确认应答有可能是因为ACK丢失).
所以主机B就会收到许多重复的数据,要对这些数据进行处理,就需要能够识别出哪些是重复的包,并且把重复的包丢弃掉.
这时候我们可以利用前面提到的序列号,就可以很容易的做到去重的效果
那么超时的时间该如何确定呢?
超时的时间---不同的系统实现的方式不一样,最理想的情况下,找到一个最小的时间,保证"确认应答一定能在这个时间内返回".
TCP为了保证在任何环境下都能够比较高性能的通信,因此会动态计算这个最大超时时间.
Linux中,超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500的整数倍.如果重发一次得不到回应,等待2*500ms后再重传一次.如果仍然得不到应答,就等待4*500ms进行重传,以此类推,以指数增长,累计到一定次数,TCP认为网络或者对端主机出现异常,强制关闭连接.
4>滑动窗口
在确认应答机制下,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段,这样的话,性能差,尤其是在数据往返的时间较长的时候.
在此之下,我们引入滑动窗口.
窗口大小:是指(一次批量发送数据的最大值)无需等待确认应答而可以继续发送数据的最大值.(上述图的窗口大小就是4000个字节,共4个字段.一个字段1000个字节)
发送前4个字段的时候, 不需要等待任何ACK,直接发送,收到第一个ACK,滑动窗口向后移动,继续发送第五个段的数据,依次类推
操作系统内部为了维护这个滑动窗口,需要开辟"发送缓冲区",来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从缓冲区中删掉.
窗口越大,则网络的吞吐率就越高.
在这里,如果批量发送,出现了丢包,如何进行重传呢?
a>如果是数据报已经抵达,ACK丢了,这种情况下,不用担心,因为后续的ACK可以进行确认.
b>数据报丢了
当某个报文段丢失之后,如上图所示,发送端会一直收到 1001这样的ACK,如果连续收到3次同样的"1001"应答,就会将对应的数据1001-2000重新进行传送; 这个时候接收端收到了,就会返回的ACK就是"7001"了,因为2001-7000在此之前就已经收到存到内核的接收缓冲区了.(这种机制被称为"高速重发控制"也叫"快重传")
5>流量控制
对于接收端,处理速度也是有限的,如果发送端发送的太快的话,导致接收端的缓冲区满了,这个时候如果发送端继续发送的话,就会造成丢包,继而引起丢包重传的一系列连锁反应.
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制
a>接收端将自己可以接受的缓冲区大小放入到TCP首部中"窗口大小"的字段(16位),通过ACK端通知给发送端,
b>窗口大小字段越大,说明网络的吞吐量越高
c>接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端
d>发送端接收到这个窗口后,就会减慢自己的发送速度
e>接收端缓冲区满了, 就会将窗口大小设置成0,这时发送方不再发送数据,但是定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送方.
TCP首部中"窗口大小"的字段(16位),最大表示65535字节,实际上,在TCP首部40个字节的选项中还包含了一个扩大因子M,实际窗口大小是窗口大小字段的值左移M位.
6>拥塞控制
TCP引入慢启动机制,先发少量的数据,探探路,看看当前的网络状态,再决定按照多大的速度传输数据
发送开始的时候,定义拥塞窗口为1,每次收到ACK应答,拥塞窗口加1,每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口.
上面所述的拥塞窗口是指数级别的,慢启动只是初始比较慢,但是增长的非常快, 为了不很快的增长,引入慢启动的阀门,当拥塞窗口超过这个阀门时,不再按照指数的方式增长,而是按照线性方式增长.
7>延迟应答
如果接收数据的主机立刻返回ACK应答,这时候返回的窗口大小可能会比较小,但实际上处理端的速度可能会很快,而延迟应答就会使缓冲区的数据处理一波后再返回,这样的话返回的窗口就会比较大
这样的话窗口越大,网络的吞吐量就会越大,传输的小路也就会越高,就可以保证在网络不拥堵的情况下,尽量提高传输的效率.
8>捎带应答
在延迟应答的基础上,我们可以捎带应答,有些情况下,ACK就可以搭顺风车,把服务器回应一起返回给客户端.
TCP异常情况
进程终止:进程终止会释放文件描述符,仍然可以发送FIN,和正常关闭没有什么问题
机器重启:和进程终止一样.