0
点赞
收藏
分享

微信扫一扫

【网络编程·传输层】UDP和TCP的报头

追风骚年 2023-08-07 阅读 59


目录

一、端口号划分

二、部分指令

1、pidof(用于查看进程id)

2、netstat(查看网络状态)

三、UDP协议

1、UDP协议格式

2、UDP协议如何进行封装、解包、分用

2.1封装、解包

2.2分用

3、UDP协议的特点

3.1UDP协议的特点

3.2UDP协议的缓冲区

3.3UDP协议16位UDP长度

四、TCP协议(传输控制协议)

1、TCP协议格式

2、TCP协议的可靠性

2.1不可靠性的体现

2.2如何保证可靠性

3、TCP协议的报头

3.1封装、解包(4位首部长度)

3.2分用(16位源端口号、目的端口号)

3.3TCP协议的32位序号和确认序号(滑动窗口、超时重传去重有用到该字段)

3.4TCP协议的16位窗口大小(用于控制发送报文的速度)

3.5TCP协议的6个标志位(区分报文类型)

4、确认应答机制(ACK)

5、超时重传机制

6、连接管理机制

6.1三次握手(建立连接是客户端发起)

6.2四次挥手(断开连接是双方的事情)

7、流量控制

8、滑动窗口/快速重传机制

8.1滑动窗口的本质

8.2滑动窗口的作用

8.3关于滑动窗口的一些问答

9、拥塞控制

10、延迟应答

11、捎带应答

12、理解TCP的面向字节流和UDP的面向数据报

13、粘包问题

14、TCP建立连接异常的问题

五、UDP/TCP协议总结

1、TCP的可靠性和性能

2、UDP和TCP的协议的适用场景

3、关于listen的第二个参数


一、端口号划分

端口号是一个16位的无符号整型,取值范围为0-65535。

0 - 1023: 知名端口号, HTTP, FTP, SSH等这些广为使用的应用层协议, 他们的端口号都是固定的。

如下是一些约定俗成的端口号:

1024 - 65535: 操作系统动态分配的端口号。 客户端程序的端口号, 就是由操作系统从这个范围分配的。 

二、部分指令

1、pidof(用于查看进程id)

pidof httpServer | xargs kill -9//xargs用于将标准输入转换为命令行参数

2、netstat(查看网络状态)

n 拒绝显示别名,能显示数字的全部转化成数字
l 仅列出有在 Listen (监听) 的服务状态
p 显示建立相关链接的程序名
t (tcp)仅显示tcp相关选项
u (udp)仅显示udp相关选项
a (all)显示所有选项,默认不显示LISTEN相关

三、UDP协议

1、UDP协议格式

如果校验和出错,该报文就会被丢弃。

UDP报头本质上是一个结构体对象或者包含位段的结构体。

struct udphdr {
    uint16_t uh_sport;  /* 源端口号 */
    uint16_t uh_dport;  /* 目的端口号 */
    uint16_t uh_ulen;   /* UDP数据报长度(包括头部+数据) */
    uint16_t uh_sum;    /* 数据校验和 */
};

2、UDP协议如何进行封装、解包、分用

2.1封装、解包

我们之前在自定义协议的时候,采用特殊字符进行区分报头和有效载荷。

UDP协议直接按照报头长度固定的方式,认为前8个字节是报头。

2.2分用

UDP的报头中包含16位的目的端口号,在对方应用层中存在了一个绑定了这个端口号的进程,上层绑定该端口号的进程就可以通过文件描述符的形式读到,交给应用层具体的协议进行处理。

3、UDP协议的特点

3.1UDP协议的特点

UDP传输的过程类似于寄信,把信寄出去就完事了,不会去管对方有没有收到。

3.2UDP协议的缓冲区

不要考虑多个UDP报文粘连的问题,因为UDP没有真正意义上的发送缓冲区. 调用sendto会直接交给内核(应用层发一个,传输层送走一个), 由内核将数据传给网络层协议进行后续的传输动作;

UDP具有接收缓冲区。但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;所以UDP协议是一种全双工的协议。

3.3UDP协议16位UDP长度

UDP协议报头中有一个16位UDP长度的字段,就是说一个UDP报文最大可传输的长度为2^16即64KB(包含UDP首部8字节),如果需要传输大于64KB的数据,就需要应用层多次分包,并在接收端手动拼装。

四、TCP协议(传输控制协议)

1、TCP协议格式

其中报头就是一个结构体对象:

struct tcphdr 
{
    uint16_t th_sport;  /* 源端口号 */
    uint16_t th_dport;  /* 目的端口号 */
    uint32_t th_seq;    /* 序列号 */
    uint32_t th_ack;    /* 确认号 */
    uint8_t th_off;     /* 偏移量,指明TCP报文头的长度,单位是4个字节 */
    uint8_t th_flags;   /* 控制标志,如SYN、ACK、FIN等 */
    uint16_t th_win;    /* 接收窗口大小 */
    uint16_t th_sum;    /* 校验和 */
    uint16_t th_urp;    /* 紧急指针 */
};

2、TCP协议的可靠性

2.1不可靠性的体现

网络传输距离很长,路上会经过多个设备节点,所以在路上可能会存在丢包、乱序、校验错误、报文重复等问题。

2.2如何保证可靠性

一条报文只有收到了对方的应答,才能保证报文的可靠性。双方通信,一定存在最新的数据,例如图中“现在是12点”这条报文,暂未收到对方的应答,所以这条报文有没有被收到是不可靠的。

报文的应答既可以单独发送,也可以和要发送的数据可以合为一条报文进行发送,例如上图,后续服务器回复“我收到现在是12点这条消息了,那我们13点去玩吧!”这就是将应答和要传的数据塞到一条报文里。

无论是客户端给服务器发消息还是服务器给客户端发消息,每条消息都必须给应答。当然不用给纯应答报文做应答哈。

3、TCP协议的报头

3.1封装、解包(4位首部长度)

可以将TCP协议的前20个字节转换为一个结构化的数据,就可以提取出标准报头中的4位首部长度。TCP报文总长度=4位首部长度*4字节=【20字节(最低20字节),60字节】。可以根据首部字段计算得到的TCP报文总大小-标准报文大小,得到的就是剩余报头的大小,剔除所有报文信息,剩下的就是有效载荷。

3.2分用(16位源端口号、目的端口号)

TCP的报头中包含16位的目的端口号,服务器启动时绑定了端口号,就可以找到该TCP报文对应的应用层进程了。操作系统维护网络进程PCB和端口号的数据结构是哈希表。

3.3TCP协议的32位序号和确认序号(滑动窗口、超时重传去重有用到该字段)

客户端可能短时间内发送多个报文,注意这些报文到达服务器的顺序是不确定的。那么客户端收到服务器的应答之后,该如何区分该条应答对应我之前发送的哪条请求呢?这是因为TCP协议中存在32位序号和32位确认序号。

序列号(Sequence Number)是TCP协议中用于标识数据包的字段,它表示发送方发送的数据包的字节流中的第一个字节的序号。序列号的作用是确保数据包的顺序和完整性。在TCP协议中,每个数据包都有一个唯一的序列号。

确认号(ACK Number)是TCP协议中用于确认接收方已经成功接收到数据包的字段,它表示接收方期望下一个收到的数据包的序列号。在TCP协议中,接收方需要发送一个确认包(ACK包)来告诉发送方已经成功接收到数据包。确认包中的ACK Number字段就是接收方期望下一个收到的数据包的序列号。

搞两组序号的原因是TCP协议是全双工的,客户端、服务器均可能充当发送方或接收方,所以需要使用两组序号。

3.4TCP协议的16位窗口大小(用于控制发送报文的速度)

TCP协议在发送数据时,发送方发送过快会导致接收方来不及读取,报文将塞满对方的输入缓冲区,对方缓冲区都满了还发,新发的报文将会被直接丢弃。那么就需要控制发送报文的速度。

TCP协议中存在16位窗口大小,用于填写自己的输入缓冲区的大小。该报文传输至对方,对方就知道自己这边剩余输入缓冲区的空间还剩多少,就能控制发送数据的速度了。

16位窗口大小的存在实现了通信双方交换接收能力的作用,达到流量控制。

3.5TCP协议的6个标志位(区分报文类型)

服务器可能会面对多种不同类型的客户端,可能有发起连接的报文,可能有应答的报文等,为了区分不同类型的报文,TCP报头设置了多种01标志位。

4、确认应答机制(ACK)

TCP将每个字节的数据都进行了编号。即为序列号。每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。

5、超时重传机制

网络传输的一大不可靠性就是丢包问题,TCP协议的解决方式是超时重传。

如果主机A发送数据给B后,由于网络问题丢包了,B自然不会给A应答。如果主机A发现一段时间间隔没有收到确认应答,就会进行重发。

如果主机A发送给主机B后,主机B接收到了报文,但是返回的应答丢了,主机A在一段时间后也会重传。

这样接收方将会收到两份一样的报文,收到重复的数据,也体现了不可靠性,接收方需要进行根据32位序号进行去重。

重传时间:由于数据传输和当时的网络环境有关,所以重传时间不能设定为固定值,而是由操作系统动态的调整重传时间。

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间。

6、连接管理机制

三次握手是创建TCP连接结构体的基础。TCP连接结构体是维护超时重传、按序到达、流量控制、连接状态等保证可靠性的数据结构。

TCP的面向连接间接的保证了通信的可靠性,同时UDP协议因为是无连接的,并不会维护超时重传、按序到达、流量控制等数据结构,所以UDP是不可靠的。 

6.1三次握手(建立连接是客户端发起)

SYN_SENT是TCP/IP协议中的一种状态,它代表了一个已经发送了连接请求(SYN)的TCP客户端,并正在等待对方返回连接确认(ACK)的过程。在TCP三次握手连接过程中,当客户端发送SYN包后,客户端的TCP状态就会变成SYN_SENT。在这个状态下,TCP客户端将继续尝试向服务器发送SYN包,直到收到服务器返回的ACK包才能建立连接。

SYN_RECV,它是TCP/IP协议中的一种状态。在TCP连接建立的过程中,服务器端在收到客户端的SYN包后,会发送ACK和SYN包作为回复,表示已经成功接收到客户端的连接请求,并且向客户端发起连接确认请求。此时服务器的TCP状态就变成了SYN_RECV。在这个状态下,服务器会等待客户端的ACK包,以完成TCP三次握手连接的过程。如果在一段时间内没有收到客户端的ACK包也不用担心,服务器会认为自己的ACK+SYN包被丢失了,会发起超时重传。

客户端正在第三次握手发出ACK时就认为三次握手完成,服务器只有收到了客户端的这个ACK应答,才认为三次握手完成。客户端、服务器三次握手完成的时间是有差别的。三次握手不一定非得成功,尤其是客户端最后一个ACK的是否丢失客户端是不知道的,但是我们有超时重传等机制解决这个问题。

为什么一定要三次握手?

因为TCP在传输层,属于操作系统管理,维护TCP连接是有时间和空间成本的。

如果握手次数仅一次,客户端仅发送一次SYN即建立连接成功,那么某些恶意分子就可以使用客户端疯狂connect发起连接,服务器维护连接是有成本的,会导致服务器上的可用连接越来越少(SYN洪水)。

如果握手次数是两次。和握手一次的情况是一样的,客户端发送完SYN后,服务端返回SYN+ACK后,服务端即认为连接建立成功。同样会引发SYN洪水。

为什么要采用三次握手呢?首先TCP是全双工的通信协议,三次握手是验证全双工通信正常与否的最小代价。其次为了防止SYN洪水,三次握手的最后一次发起连接的是客户端,客户端最后一次发出ACK的时候,代表连接成功,客户端想在服务端建立连接就必须现在客户端建立连接。客户端想发动SYN洪水攻击其本身也会受到同等的消耗,三次握手可以有效规避单主机下的SYN攻击。(不过需要注意的是,防止SYN洪水攻击,并不是TCP协议来解决的,TCP协议的主要任务是网络通信!)

如果握手次数是四次,是不行的,最后一次服务端发送ACK应答后,就会在自己这里建立连接,只要客户端想办法不收到这个连接,就不会在客户端建立连接关系,存在SYN洪水攻击的风险。

如果握手次数是大于三次奇数次,握手三次已经能满足要求了,没必要增加握手次数浪费双方的时间。

6.2四次挥手(断开连接是双方的事情)

断开连接是双方的事情,需要征得双方同意。

四次挥手阶段,客户端和服务器都可以主动发起断开连接。

一方想断开连接的时候,另一方可能也想断开连接,可能会导致四次挥手变成三次挥手。

主动断开连接的一方,最终状态是TIME_WAIT,被动断开连接的一方,两次挥手完成会进入CLOSE_WAIT状态。如果被断开一方出现大量的CLOSE_WAIT状态,要么是服务器压力过大来不及执行close(服务端还有数据没有推送完),要么是你的close直接就是忘写了。

四次挥手完成,主动断开连接的一方会维持一段时间的TIME_WAIT。

GETSOCKOPT(2)         
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,
              void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
              const void *optval, socklen_t optlen);
int opt=1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

7、流量控制

上面章节说过,TCP报文中存在16位窗口大小,实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是窗口字段的值左移 M 位。用于交换双方接收缓冲区的大小。在三次握手时,双方就在TCP报文中顺带的完成了接收缓冲区的大小的交换。

当接收端接收缓冲区已满,会有两种策略:1、当接收端上层拿走了数据,接收端会向发送方发送一个窗口更新通知;2、发送端会时不时的询问接收端的窗口大小。

8、滑动窗口/快速重传机制

8.1滑动窗口的本质

滑动窗口本质是发送缓冲区的一部分,发送缓冲区中已发送未应答的部分称为滑动窗口。

滑动窗口的移动本质是数组下标的移动。

8.2滑动窗口的作用

8.3关于滑动窗口的一些问答

滑动窗口开始的大小是怎么设定的?未来如何变化:

滑动窗口一定是向右滑动吗?会向左滑动吗:

滑动窗口的大小会改变吗?如何改变:

滑动窗口中的数据收到应答的先后顺序并不相同,如果先ACK中间或者尾部的数据,滑动窗口应该怎么处理:

如果向后滑动,后方空间不足该怎么处理:

9、拥塞控制

路上的车多了,难免会堵车。网络也一样,同时有多台主机进行TCP通信,也会造成网络拥塞。各个接收主机突然出现大量的丢包现象,如果此时贸然对大量丢失的报文进行超时重传,对网络状态的缓解无疑是雪上加霜。

当发生拥塞时,TCP会启用慢启动机制,所有主机先发送少量的数据用于探路(减少了同一时间发送的报文数量,大大缓解了网络压力),摸清网络的状况后再提升发送速率。如下图:

图上还有一个拥塞窗口(大小根据网络状态变化)的概念,体现网络的的存储能力。

对于第三点的解释:

10、延迟应答

发送端发了一个报文至对端的接收缓冲区中,对端接收到该报文后发现接收缓冲区还剩1M,如果立即进行应答,告诉发送端我的接收缓冲区还剩1M,那么发送端下一次将会按照1M的容量进行流量控制。如果对端采取延时应答,那么上层将有概率取走一些接收缓冲区中的报文,这样就可以应答一个更大的空间,同时可以减少应答次数(TCP中有32位确认序号,可以保证之前序号的报文已经收到了)。延迟应答可以增加网络吞吐量

当然并不是说每个包都延迟应答。

11、捎带应答

一端给另一端发消息,另一端为了减少报文的传输,会把ACK标志位置1并且带上32位确认序号的同时,再加上有效载荷一并发送给对端。(携带有效载荷的应答,这也是TCP中比较常见的通信方式,能提高消息的传输效率)

12、理解TCP的面向字节流和UDP的面向数据报

应用层调用write的本质就是将数据拷贝到发送缓冲区,如果数据满将会被挂起;应用层调用read的本质就是将数据从接收缓冲区中读出,如果数据满将会被挂起。由于双方各有一个读/写缓冲区,所以TCP是一种全双工的通信协议。

对于TCP协议来说,它并不关心应用层给的数据中,多少长度为一个报文,在它眼里,应用层给的全部是一个个字节。由于缓冲区的存在,TCP协议的读和写并需要匹配,上层给多少或者想要多少以及如何将这些字节组合成一个个报文的,TCP根本不关心,想要组合成报文,应用层自己想办法例如应用层可以调用多次write写入总计写入100字节,对端可以调用一次read将100字节读出。(UDP是面向数据报,发一次必须读一次,每次收到的必定是一个报文,后续再对这个报文进行序列化和反序列化即可。对于这种独立的报文的传输称为面向数据报)

13、粘包问题

上面说了,TCP不管报文的合成,需要应用层自己解决粘包问题。

解决粘包问题的本质是明确报文和报文之间的边界。

如何明确边界:

14、TCP建立连接异常的问题

1、TCP客户端和服务器某一方或者全部挂掉了

2、机器重启

3、机器掉电或者网线被拔

五、UDP/TCP协议总结

1、TCP的可靠性和性能

可靠性:

提高性能:

2、UDP和TCP的协议的适用场景

3、关于listen的第二个参数

int listen(int sockfd, int backlog);

TCP协议要为上层维护一个全连接队列,队列里面是已经完成三次握手的待连接对象,这个队列不能没有,它的存在就好比去外面吃饭,需要排队的场景,上一个顾客走了,在排队的用户马上可以接上来,大大提高TCP的速度,当然这个队列也不能太长,因为队伍太长你还愿意等吗,TCP也一样,队列太长没必要,因为维护这个队列是需要开销的。这个全连接队列受listen的第二个参数的影响。

listen的第二个参数设置为2,在第四次建立连接的时候(客户端36648),可以看到客户端认为自己建立连接成功了,但是在服务器端,连接建立的状态是SYN_RECV。(客户端发起建立连接的请求,服务器收到后处于SYN_RECV状态并向客户端发送SYN+ACK应答,使客户端建立成功连接,但是客户端发起第三次握手的时候会无视该客户端的ACK,说话以服务器并不会建立连接,仍处于SYN_RECV的状态称为半连接状态

tcp底层最多允许backlog+1个全连接,后续来的全部都是半连接。(半连接如果没有尽快完成握手,会被server自动关掉)

举报

相关推荐

0 条评论