网络基础(三)传输层UDP / TCP协议

传输层概念及功能

传输层是整个网络体系结构中的关键层次之一,主要负责向两个主机中进程之间的通信提供服务。
传输层在终端用户之间提供透明的数据传输,向上层提供可靠的数据传输服务。传输层在给定的链路上通过流量控制、分段/重组和差错控制来保证数据传输的可靠性。

传输层的服务一般要经历传输连接建立阶段,数据传送阶段,传输连接释放阶段3个阶段才算完成一个完整的服务过程。而在数据传送阶段又分为一般数据传送和加速数据传送两种形式。传输层中最为常见的两个协议分别是传输控制协议TCP(Transmission Control Protocol)和用户数据报协议UDP(User Datagram Protocol) 。传输层提供逻辑连接的建立、传输层寻址、数据传输、传输连接释放、流量控制、拥塞控制、多路复用和解复用、崩溃恢复等服务。

传输层提供了主机应用程序进程之间的端到端的服务,基本功能如下:
(1) 分割与重组数据
(2) 按端口号寻址
(3) 连接管理
(4) 差错控制和流量控制、纠错等功能 传输层要向会话层提供通信服务的可靠性,避免报文的出错、丢失、延迟时间紊乱、重复、乱序等差错。

传输层的任务是根据通信子网的特性,最佳的利用网络资源,为两个端系统的会话层之间,提供建立、维护和取消传输连接的功能,负责端到端的可靠数据传输。在这一层,信息传送的协议数据单元称为段或报文。

UDP协议

报文格式

在UDP协议层次模型中,UDP位于IP层之上。应用程序访问UDP层然后使用IP层传送数据报。IP数据包的数据部分即为UDP数据报。IP层的报头指明了源主机和目的主机地址,而UDP层的报头指明了主机上的源端口和目的端口。UDP传输的段(segment)有8个字节的报头和有效载荷字段构成。
在这里插入图片描述
UDP报头由4个域组成,其中每个域各占用2个字节,具体包括源端口号、目标端口号、数据报长度、校验和。

UDP协议头部:
struct udphdr
{
	u_int16_t uh_ sport;     /* source port */源端口
	u_int16_t  uh_ dport;     /* destination port */目的端口
	u_int16_t uh_ ulen; .     /* udp length */ udp数据报长度
	u_int16_t uh_ sum;      /* udp checksum */检验和
};

在这里插入图片描述

  • 16位源端口号:源主机的应用程序使用的端口号。
  • 16位目的端口号:目的主机的应用程序使用的端口号。
  • 16位UDP数据报长度:是指UDP头部和UDP数据的字节长度。udp数据报最大是65535个字节。然而64K在当今的互联网环境下, 是一个非常小的数字,如果我们需要传输的数据超过64K, 就需要在应用层手动的分包(自定制协议), 多次发送, 并在接收端手动拼装,来确保数据包的完整性。
  • 16位UDP校验和:该字段提供了来判断UDP数据在传输的过程中是否发生损坏;该字段是可选的。

UDP的缓冲区

UDP没有真正意义上的 发送缓冲区,在传输层时,(先打上udp协议头)再调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作。
UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致,也不保证可靠; 如果缓冲区满了而迟迟没被读走, 再到达的UDP数据就会被丢弃。

检验和

1.作用:
检验UDP数据在传输过程当中是否有损坏;
如果有损坏,则不会提交给应用层,直接丢弃; (都不给个信,所以不可靠)
如果没有损坏,则应用层在调用recvfrom的时候,将数据提交给应用层;
2.判断原理
UDP协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。
具体算法:
发送时:
1.由于udp数据报头部里面的四个域都是16个比特位,将源端口+目的端口+数据报长度相加操作;若两两相加可能会出现进位到第17位上,就进行回卷处理。
2.对加起来的结果进行反码运算;
3.将反码运算的结果,放到校验和的16个比特位当中去;

  1000 1111 0000 1100 //原端口与目的端口和
+ 1011 1011 1011 0101 //数据报长度 
 10100 1010 1100 0001 //需要回卷 此时将最高位的1提出来,再与剩下的16位相加

 0100 1010 1100 0001
+				   1
=0100 1010 1100 0010 //再取反码  --》 1011 0101 0011 1101 放入检验和字段

接受时:
直接将四个域相加,若为全一,则数据未有损坏。
在这里插入图片描述

主要特点(无连接、面向数据报、不可靠);

无连接:传输数据之前源端和终端不建立连接(只需要知道对端ip和端口便可以发送数据),当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中(缓冲区),应用程序每次从队列中读一个消息段。
特点:
1.由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
2.UDP信息包的报头很短,只有8个字节,相对于TCP的20个字节信息包而言UDP的额外开销很小。
3.吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
面向数据报:发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并(不能够灵活的控制读写数据的次数和数量),而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
不可靠:没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息;但它是分发信息的一个理想协议。例如,在屏幕上报告股票市场、显示航空信息等等。UDP也用在路由信息协议RIP中修改路由表。在这些应用场合下,如果有一个消息丢失,在几秒之后另一个新的消息就会替换它。且UDP广泛用在多媒体应用中。

基于UDP的应用层协议

NFS: 网络文件系统
TFTP: 简单文件传输协议
DHCP: 动态主机配置协议
BOOTP: 启动协议(用于无盘设备启动)
DNS: 域名解析协议

适用场合
在选择UDP作为传输协议时必须要谨慎。在网络质量令人十分不满意的环境下,UDP协议数据包丢失会比较严重。但是由于UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如QQ就是使用的UDP协议。
实际应用举例
在现场测控领域,面向的是分布化的控制器、监测器等,其应用场合环境比较恶劣,这样就对待传输数据提出了不同的要求,如实时、抗干扰性、安全性等。基于此,现场通信中,若某一应用要将一组数据传送给网络中的另一个节点,可由UDP进程将数据加上报头后传送给IP进程,UDP协议省去了建立连接和拆除连接的过程!取消了重发检验机制,能够达到较高的通信速率。

TCP协议

不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换.
传输控制协议(TCP,Transmission Control Protocol)就是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。

报文格式

在这里插入图片描述

  • 源端口号:发送方发送数据使用的端口;
  • 目的端口号:接收方在进行数据分用的时候,传输层的tcp协议通过端口,可以将数据提交给指定侦听的应用层程序;传输层在通过目的端口来区分数据属于哪一个进程;
  • 32位序号:标识TCP源端向TCP目的端发送的数据字节流(发送数据包中的第一个字节的序列号);
  • 32位确认序号:标识TCP目的端期望TCP源端的下一个请求序号;
    由于tcp是面向字节流的,且可以分割组装,所以有这两个序号来标识每一段字节流数据,且还用于tcp协议的确认应答机制。
  • 4位TCP报头长度: 表示该TCP头部有多少个32位bit(有多少个4字节); 所以TCP头部最大长度是(2^4-1)*4=15 * 4 = 60
  • 6位标志位:
    URG: 紧急指针是否有效
    ACK: 确认号是否有效
    PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走
    RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段
    SYN: 请求建立连接((在建立TCP连接的时候使用); 我们把携带SYN标识的称为同步报文段
    FIN: 通知对方, 没有数据发送,本端要关闭了(在关闭TCP连接的时候使用), 我们称携带FIN标识的为结束报文段
  • 16位窗口大小:表示接收缓冲区的空闲空间,16位,用来告诉TCP连接对端自己能够接收的最大数据长度。
  • 16位校验和: 发送端填充, CRC校验. 每条收发都要检测,若接收端校验不通过, 则认为数据有问题, 此处的检验和不光包含TCP首部, 也包含TCP数据部分.
  • 16位紧急指针: 标识哪部分数据是紧急数据;只有URG标志位被设置时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。

网络抓包

wireshark是 windows 下的一个网络抓包工具. 虽然 Linux 命令行中有 tcpdump 工具同样能完成抓包, 但是tcpdump 是纯命令行界面, 使用起来不如 wireshark 方便。

linux下tcpdump抓包格式:

//需要root权限
tcpdump -i any port [服务端侦听端口] -s 0 -W 123.dat

利用之前的在本机客户端服务端tcp小程序来模拟端对端的通信流程,再利用抓包工具来分析具体的细节。
客户端发起连接–》与服务器通信–》客户端断开连接
在这里插入图片描述
服务端响应连接–》与客户端通信–》断开连接
在这里插入图片描述
利用wireshark对此阶段抓取数据包
在这里插入图片描述

整个客户端服务端tcp通信程序的通信流程

在这里插入图片描述
服务端状态转化:
[CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文.
[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文, 就进ESTABLISHED状态, 可以进行读写数据了.
[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT;
[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
[LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接.

客户端状态转化:
[CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段;
[SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态, 开始读写数据;
[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段, 同时进入FIN_WAIT_1;
[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进FIN_WAIT_2, 开始等待服务器的结束报文段;
[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK;
[TIME_WAIT -> CLOSED] 客户端要等待一个2MSL的时间, 才会进入CLOSED状态.

建立连接(三次握手)

当主动方发出SYN连接请求后,等待对方回答SYN+ACK,并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接。
在这里插入图片描述
SYN_RECV状态:服务端被动打开后,接收到了客户端的SYN并且发送了ACK时的状态。再进一步接收到客户端的ACK就进入ESTABLISHED状态。
ESTABLISHED状态:确认连接状态。

对应于之前的抓取的数据包
在这里插入图片描述

断开连接(四次挥手)

建立一个连接需要三次握手,而终止一个连接要经过四次挥手,这是由于TCP的半关闭(half-close)造成的。

  • 1.某个应用进程首先调用close,称该端执行“主动关闭”。该端的TCP于是发送一个FIN报文段,表示数据已发送完毕。
  • 2.接收到这个FIN的对端执行 “被动关闭”),这个FIN由TCP确认,并发送一个ACK应答。
    注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
  • 3.一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也给对端发送一个FIN。
  • 4.接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)发送ACK来应答确认这个FIN。

由于每个方向都需要一个FIN和一个ACK,因此通常需要4个分节(便是四次挥手的由来)。
注意:
(1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。
(2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的(可能会有捎带应答),这称为“半关闭”(half-close)。
(3) 当一个进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。
无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。
在这里插入图片描述
对应之前抓取的数据包(与上图不同之处就是可能带有捎带应答机制的ACK)
在这里插入图片描述

理解TIME_WAIT状态

TIME_WAIT状态:TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态。
MSL:最大报文段生存时间,指的是发送方认为TCP报文在网络当中的最大生存时间;

  • MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同, 在Centos7上默认配置的值是60s;
  • 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值;

如果ACK丢失,则服务端认为FIN并没有递达到客户端(虽然说可能到了,但是没收到应答),服务端就会进行重传FIN报文;
在这里插入图片描述

可以简单理解为:2MSL == 丢失ACK的MSL + 重传FIN的MSL

为什么是TIME_WAIT的时间是2MSL?

  • MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话,就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
  • 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN.,这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以在LAST_ACK状态重发FIN。

解决TIME_WAIT状态引起的bind失败的方法
现在做一个测试,首先启动server,然后启动client,然后用Ctrl-C使server终止,这时马上再运行server(模拟重启), 结果是:
在这里插入图片描述
这是因为,虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此不能再次监 听同样的server端口.我们用netstat命令查看一下:
在这里插入图片描述
TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL的时间后才能回到CLOSED状态.
我们使用Ctrl-C终止了server, 所以server是主动关闭连接的一方,会处于TIME_WAIT状态,而在这个状态等待2MSL的时间内,我们的内核会拿着端口,等待2MSL时间,而这个行为是传输层TCP的行为,即使应用程序已经退出掉了,但是在内核当中对应的端口还在被占用着;,所以在TIME_WAIT期间不能再次监听同样的server端口。

此规则引申出来的问题例子
在server的TCP连接没有完全断开之前不允许重新监听, 某些情况下可能是不合理的。
服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户端来请求).这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务器端主动清理掉), 就会产生大量TIME_WAIT连接。
由于我们的请求量很大, 就可能导致TIME_WAIT的连接数很多, 每个连接都会占用一个通信五元组(源ip,源端口, 目的ip, 目的端口, 协议). 其中服务器的ip和端口和协议是固定的. 如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了, 就会出现问题。
解决此问题的方法:
使用setsockopt()函数设置socket描述符的属性, 允许创建地址和端口相同的多个socket描述符。

#include <sys/types.h>          
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval,socklen_t optlen);
	sockfd:将要被设置的套接字
	level:指定套接字的层次,取值有三种
		SOL_SOCKET:通用套接字选项 --》地址复用
		IPPROTO_TCP:TCP选项
		IPPROTO_IP:IP选项
	optname:在level中要完成的任务
		SOL_SOCKET 
			SO_REUSERADDR:允许重用本地地址和端口
			SO_RECVBUF:获取接收缓冲区的大小
		IPPROTO_TCP
			TCP_MAXSEG:获取TCP最大数据段的大小
		IPPROTO_IP 
			IP_TTL:获取最大字节数,也就是最大生存空间
	optval:需要完成上面的任务,需要地址复用就传入1即可。
		注意:传入的是一个 变量的地址
		例如:int i = 1;
			&i;
	optlen:optval的字节长度

具体用法:
在创建套接字之后,便可以使用此函数进行可以重新监听的属性设置。

sock_ = socket(AF_INET ,SOCK_STREAM, IPPROTO TCP) ;//创建套接字
//设置属性
int opt = 1;
setsockopt(sock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));

这样便解决了server的TCP连接没有完全断开之前不允许重新监听的情况了。
在这里插入图片描述

协议对比

UDP和TCP协议的主要区别是两者在如何实现信息的可靠传递方面不同
TCP协议中包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其它信息,否则将一直等待直到收到确认信息为止。与TCP不同,UDP协议并不提供数据传送的保证机制。如果在从发送方到接收方的传递过程中出现数据包的丢失,协议本身并不能做出任何检测或提示。因此,通常人们把UDP协议称为不可靠的传输协议。
TCP 是面向连接的传输控制协议,而UDP 提供了无连接的数据报服务;TCP 具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;UDP 在传输数据前不建立连接,不对数据报进行检查与修改,无须等待对方的应答,所以会出现分组丢失、重复、乱序,应用程序需要负责传输可靠性方面的所有工作;
UDP 具有较好的实时性,工作效率较 TCP 协议高;
UDP 段结构比 TCP 的段结构简单,因此网络开销也小。
TCP 协议可以保证接收端毫无差错地接收到发送端发出的字节流,为应用程序提供可靠的通信服务。对可靠性要求高的通信系统往往使用 TCP 传输数据.

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值