传输层—TCP原理详解

目录

前言

1.TCP协议

2.TCP协议段格式

3.如何解包如何分用

4.网络协议栈和文件的关系

5.如何理解TCP报头

6.TCP的特点

7.TCP字段

7.1 16位窗口大小

7.2标志位

8.超时重传

9.连接管理机制

10.滑动窗口

11.拥塞控制

12.延迟应答

13.捎带应答

14.理解TCP的面向字节流

15.粘包问题

16.TCP异常情况

17.TCP小结

18.TCP/UDP对比

19.如何使用UDP实现可靠传输

20.理解listen的第二个参数

总结


前言

        在上一篇文章中我们介绍了关于UDP是如何实现的,今天我们要介绍的是TCP,关于TCP协议,前面说了是可靠的,今天我们就一起来看看TCP协议为什么是可靠的,关于可靠的实现采用了什么策略。

1.TCP协议

TCP全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传输进行一个详细的控制;

当数据拷贝到传输层的时候,继续向下传输数据的时候操作系统有自己的传输策略,所以TCP协议被称为传输控制协议,TCP协议既有发送缓冲区又有接受缓冲区,所以TCP是全双工的

如图所示:

2.TCP协议段格式

a.tcp协议报头是有标准长度的20byte,在读取的时候先读取20byte

b.转化成结构化数据,提取标准报头的四位首部长度,约定tcp报文的总长度=

4位首部长度*4byte [0,60],如果我们报头就是20byte,那么4位首部长度,应该填写5 x * 4byte = 20 x = 5 [0101] 

c.得到后续报头剩余的大小:x*4 - 20 = 0; x * 4 - 20 = n;

d.只要把报头处理完毕,剩下的就是有效载荷

3.如何解包如何分用

解包:按照上面的方式进行解包

分用:按照报头里面的目的端口号,就可以找到应用层的进程了,数据就能够向上交付了

4.网络协议栈和文件的关系

如图所示:

5.如何理解TCP报头

报头在语言层面就是一种结构化的数据:

struct tcp_ hdr
{uint32_t src_port:16;uint32_t dst_port:16;uint32_t seq;uint32_t ack_seq;uint32_t header_length:4;....
};

6.TCP的特点

可靠性,传输效率高

a.理解可靠性:
为什么会存在不可靠性:网络传输存在不可靠性的本质原因是因为传输的距离变长了
不可靠性的场景:丢包,乱序,校验错误,重复......
b.tcp如何保证可靠性:
感性理解可靠性
A和B通信:

距离变长了,不存在绝对的可靠性,但是存在相对的可靠性。
如何保证相对的可靠性呢?
一个报文只要收到了应答。就能保证该报文的可靠性!
TCP采用确认应答机制保证可靠性,双方通信一定存在最新消息,如果没有应答,最新消息一般无法保证可靠性

理解TCP的工作模式:

保证可靠性,无论是客户端到服务端还是服务端到客户端都需要有应答,双方在通信的时候,可能除了正常的数据段,还可能包含确认数据段。

TCP的真实工作模式:

tcp协议数据传输的时候达到对面的顺序和发送时的顺序不一定一样。
为了解决上述问题:需要有方式标识数据段本身,标识数据段本身采用序号和确认序号的方式
理解序号和确认序号:

确认序号 = 序号 + 1 并且是连续的

为什么序号有两组:因为TCP是全双工的,客户端向服务端发送消息,服务端向客户端发送消息

真实序号: 因为tcp协议传输数据是面向字节流的,所以将每个字节的数据都进行了编号,即序列号

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发.

7.TCP字段

7.1 16位窗口大小

tcp在发送数据的时候,快了不行,慢了也不行,如何保证发送合适的数据呢?
解决方式: 16位窗口大小一本质上是一块缓冲区,当一方向对方发送数据的时候,在自己的报头中填写缓冲区剩余空间的大小! 通过16位窗口的方式实现数据传输时的流量控制

7.2标志位

服务器会受到各种各样的报文,接受方需要根据不同的报文,做不同的动作,tcp报文也是有类型的,有的是正常的数据段,有的是ack确认数据段,按照不同的标志位做不同的动作。

tcp报文类型的划分: URG. ACK. PSH, RST, SYN, FIN


SYN:连接建立的时候,将该标志位设置为1
FIN:连接断开的时候,将该标志位设置为1
ACK:报文为确认类型的时候,将该标志位设置为1
PSH:当接收方的缓冲区满了之后,发送方催促接受方尽快将数据拿走,告知这个信息,将该标志设置为1

URG:

数据对于接收方而言,乱序本身就是不可靠的表现,所以要对收到的数据进行排序,保证数据的时序到达! 如何进行排序呢?采用序号的方式进行排序
排序之后:接收方的缓冲区就是一一个队列.
此时如何有数据想要进行插队呢?如何有数据想要进行插队,就将该数据报文中的URG设置为1
被设置URG的数据报文被16位紧急指针标识
16位紧急指针:在有效载荷中的偏移量
该数据有几个字节:在tcp协议中该数据只有一一个字节
此时该数据就能够被应用层找到提前被读取,实现数据插队

一般将这种数据称为是带外数据
使用场景:检测服务器的状态
在应用层如何标识带外数据呢?

发送方:
send .
ssize_t send(int sockfd, const void *buf, size_t len,int flags) ;
flags: MSG_ OOB
接受方: recv
ssize_t recv(int sockfd, void *buf, size_ t len,int flags) ;
flags: MSG_ OOB

RST:

三次握手建立连接成功,即使连接成功了,通信的过程中也有可能单方面出现问题
对于一方来说连接还存在,对于出问题的一方来说,连接己经不存在了,但是没有出问题的一方认为连接还存在就会向另一方发送数据,此时数据发送到出问题的一方时, 出问题的一方检测连接己经断开了,但是任然被发送数据,此时就会将RST标志位设置为1,标识恢复连接

8.超时重传

如何理解数据传送时的丢包:
丢包出现的可能性有两种:
1.数据在发送的时候丢失了
2.接受方给发送方发的应答丢失了
针对上面的两种情况tcp协议定制的策略是超时重传
如果是第二种情况,就会出现发送方给接受方发两份同样的数据,此时就需要进行去重,如何进行去重呢?可以采用序号进行去重!
既然存在重传的可能性,就说明发送端发送的数据并没有发送完之后被立即移除掉

 

如何理解超时:
超时是根据网络情况决定的,是一个浮动的时间间隔!
一般最理想的情况下,找到一个最小的时间,保证”确认应答一定能在这个时间内返回”.
Linux中(BSDUnix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍.
如果重发一次之后,仍然得不到应答,等待2*500ms 后再进行重传
如果仍然得不到应答,等待4*500ms 进行重传.依次类推,以指数形式递增.
累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接

9.连接管理机制

在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接

如何理解三次握手:
三次握手是tcp协议建立连接定制的策略,三次握手不一定保证连接一定会成功!
一次握手和两次握手可以吗?
答案是一定不行的,因为当客户端和服务端建立连接的时候,连接是需要被os管理起来的,管理的方式先描述,再组织。
管理一定会有时间和空间的成本,如果一次握手和两次握手就能够让连接成功,服务端就有可能会受到客户端的攻击,一般将这种攻击称为是SYN洪水

三次握手的特点:
a.用最小的成本验证全双工通信信道是通畅的
b.三次握手可以有效防止单机对服务器进行攻击.
如何理解tcp要建立连接?
tcp建立连接是因为要保证可靠性。
如何保证可靠性?
结构体字段保证了可靠性的数据结构基础,三次握手是创建连接结构体的基础,通过这样的方式间接保证了tcp的可靠性!

如何理解三次握手以上的建立连接:三次握手以上也能建立连接,但是造成了不需要的资源浪费!
如何理解四次挥手:断开连接是双方的事情,需要征得双方的同意

这里所谓的不发数据是指不发用户数据,并不代表底层没有管理报文的交互

tcp是如何知道数据已经发送完了,需要断开连接了呢?
答案是tcp并不知道,但是上层会调用close (sock),关闭文件描述符标识着数据已经传输完毕了,需要断开连接了。

四次挥手时的状态变化:
主动断开连接的一方, 最终状态是TIME_WAIT状态
被动断开连接的一方,最终状态时CLOSE_WAIT状态
上面的两种状态和双方是客户端还是服务端是没有任何关系的,因为TCP是地位对等的协议!


如果服务器大量出现close_wait:
1.服务器有bug,没有做close关闭文件描述符的动作
2.服务器有压力,可能一直在推送消息给client,导致来不及close

四次挥手动作完成,但是主动断开连接的一方要维持一段时间的TIME_WAIT:
为什么?一般需要多长时间,为什么?
一般需要等待两个MSL的时间才能回到CLOSED的状态!
为什么需要等待?
1.是因为可能会存在最后一个ACK响应丢失的情况,一旦丢失就需要进行超时重传,如果没有这个等待时间,就有可能会存在最后一个ACK响应丢失,一端认为并没有断开连接,继续发送信息,此时就是一种bug了
2.双方在断开连接的时候,网络中还有滞留的报文一保证滞留报文进行消散

如何理解MSL:
MSL是TCP报文的最大生存时间
可以通过cat /proc/ sys/net/ ipv4/tcp_ fin_ timeout查看ms1的值:60
如何理解服务器有时候可以重启,有时候不能重启binderror的原因:
当服务器主动断开时,服务器处于TIME_WAIT状态,此时就无法绑定成功了!

如何解决这个问题: 

使用setsockopt()设置socket描述符的 选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个socket描述符

10.滑动窗口

如何理解滑动窗口:
首先数据可能存在丢失的情况,因此有超时重传机制,决定了数据由发送端发送数据之后不会被清除掉,而是保存在发送缓冲区中,发送缓冲区又被细化分为:

如图所示

前半部分:已经发送并且受到应答
中间:滑动窗口->已经发送,但是没有收到应答!
后边:数据尚未发送.
最后边:没有数据,只有空间

如何看待滑动窗口:

窗口大小起始如何设定的,未来如何改变?
win_ start = 0; win_ end = win_ start + tcp_ win ->未来无论怎么移动,都要保证对方能够正常接受! 所以起始滑动窗口大小=对方告知我的自己接受能力的大小
窗口会向左滑动吗?一定 会向右滑动吗?
答案是不会向左滑动
可能会向右滑动,也可能保持不变
滑动窗口会一直不变吗?会变大吗?会变小吗?变的依据是什么?
答案是滑动窗口可能会一直不变, 也可能会变大,也可能会变小,变的依据是根据对方可接受缓冲区的大小!
收到应答确认的时候,如果不是最左侧发送报文的确认,而是中间的,结尾的,怎么办?
如果说没有收到应答确认就说明是丢包了:
丢报包含两种情况:
1.数据没丢,只是应答丢了
2.数据真的丢了
针对第一种情况: 因为确认序号的定义是ACK seq X + 1, 表示X + 1之前的数据全部都收到了win_ start+X+1
针对第二种情况:数据真的丢了返回的确认序号依旧是前面的确认序号,规定ack序号连续三个相同的序号,会触发重传机制
滑动一直向后滑动,空间不够了怎么办? 

发送缓冲区在内核中被设置为环形结构

11.拥塞控制

如何理解拥塞控制:

client向server端发送报文时,可能会出现丢失1~2个报文的问题,此时可以采用重传机制进行重发但是如果一旦出现client像server端发送1000个报文,而丢失了999个报文,此时就可能是网络出现了问题,因为网络出现了问题,所以不能采用超时重传机制向网络中传送大量的报文,如果一旦传送就可能会造成网络问题更加严重,,所以针对网络出现的问题,采用拥塞控制的解决方案!

拥塞控制的机制:

TCP引入慢启动机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;

采用指数增长的模式进行传输

在client向server发送数据的时候,client端会有一个滑动窗口,在发送数据的时候,会经过网络,标识网络接受数据能力的大小使用拥塞窗口
拥塞窗口的特点:
1.发送开始的时候,定义拥塞窗口大小为1
2.每次收到一个ACK应答,拥塞窗口+1
3.每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小作比较,取较小的值作为实际发送窗口的大小->滑动窗口=min(拥寒窗口,窗口大小(server接受的能力))

拥塞窗口的增长速度是指数级别的:
慢启动是指初始是慢,但是增长速度是非常快的,所以为了控制,不能使拥塞窗口单纯的按照指数形式增长
采用的方法:使用一个阈值
当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长!

当TCP开始启动的时候, 慢启动阈值等于窗口最大值;
在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1;

总结:

少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞;
当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降;
拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案.

12.延迟应答

延迟应答的目的:在不考虑网络拥塞的情况下,尽量提高传输效率
如何理解延迟应答:

延迟应答的本质是在收到报文的时候,不是立刻进行应答,而是在经过一定的时间隔之后进行答,这种方式的目的是,在这个时间间隔内可能上层会拿掉缓冲区的数据,此时进行应答的时候告知client, sereve端 接受缓冲区变大了,进而client发送更多的数据,提高数据的传输效率!

举例说明:

假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;
但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;
如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;

所有的包都可以延迟应答么? 
数量限制: 每隔N个包就应答一次;
时间限制: 超过最大延迟时间就应答一次;
具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;

如何理解每个n个包应答一次,能够保证server端收到数据了呢?
原因:是因为确认序号的特性决定的
收到当前确认序号代表在这之前的内容都已经收到了!

13.捎带应答

client向server端发送数据,server端接受到数据之后也会向client发送数据,在发送数据的时候会将上一次c1ient发送的数据的应答一起发送过去,此时,这种应答方式被称为是捎带应答,本质上也是提高数据传输效率!

14.理解TCP的面向字节流

创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区;
调用write时, 数据会先写入发送缓冲区中;
如果发送的字节数太长, 会被拆分成多个TCP的数据包发出;
如果发送的字节数太短, 就会先在缓冲区里等待, 等到缓冲区长度差不多了, 或者其他合适的时机发送出去;
接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;
然后应用程序可以调用read从接收缓冲区拿数据;


由于缓冲区的存在, TCP程序的读和写不需要一一匹配, 例如:
写100个字节数据时, 可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;
读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100个字节, 也可以一次read一个字节, 重复100次;

15.粘包问题

首先要明确, 粘包问题中的 "包" , 是指的应用层的数据包.
在TCP的协议头中, 没有如同UDP一样的 "报文长度" 这样的字段, 但是有一个序号这样的字段.
站在传输层的角度, TCP是一个一个报文过来的. 按照序号排好序放在缓冲区中.
站在应用层的角度, 看到的只是一串连续的字节数据.
那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包.

那么如何避免粘包问题呢? 归根结底就是一句话, 明确两个包之间的边界.
对于定长的包, 保证每次都按固定大小读取即可; 
对于变长的包, 可以在包头的位置, 约定一个包头总长度的字段, 从而就知道了包的结束位置;
对于变长的包, 还可以在包和包之间使用明确的分隔符(应用层协议, 是程序猿自己来定的, 只要保证分隔符不和正文冲突即可);

对于UDP协议来说, 是否也存在 "粘包问题" 呢?
对于UDP, 如果还没有上层交付数据, UDP的报文长度仍然在. 同时, UDP是一个一个把数据交付给应用层. 就有很明确的数据边界.
站在应用层的角度, 使用UDP的时候, 要么收到完整的UDP报文, 要么不收. 不会出现"半个"的情况.

16.TCP异常情况

进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
机器重启: 和进程终止的情况相同.
机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接.

17.TCP小结

可靠性的保证:
校验和,序列号,确认应答,超时重传,连接管理,流量控制,拥塞控制
提高性能:
滑动窗口,快速重传,延迟应答,捎带应答
基于tcp的应用层协议:
HTTP
HTTPS
SSH
Telnet
FTP
SMTP

18.TCP/UDP对比

我们说了TCP是可靠连接, 那么是不是TCP一定就优于UDP呢? TCP和UDP之间的优点和缺点, 不能简单, 绝对的进行比较

TCP用于可靠传输的情况, 应用于文件传输, 重要状态更新等场景;
UDP用于对高速传输和实时性要求较高的通信领域, 例如, 早期的QQ, 视频传输等. 另外UDP可以用于广播;

归根结底, TCP和UDP都是程序员的工具, 什么时机用, 具体怎么用, 还是要根据具体的需求场景去判定.

19.如何使用UDP实现可靠传输

参考TCP的可靠性机制, 在应用层实现类似的逻辑;
例如:

引入序列号, 保证数据顺序;
引入确认应答, 确保对端收到了数据;
引入超时重传, 如果隔一段时间没有应答, 就重发数据;

……

20.理解listen的第二个参数

举一个生活中的例子:
在吃海底捞的时候,如果里面人已经满了,当继续有客户来的时候,一般会在外边有桌子让来的客户坐着排队等候。
排队的本质是:当有人吃完离席的时候,等候的客户可以马上吃海底捞,对于老板来说,提高了桌子的利用率,进而提升收入,但是排队等候的也不利于过长

而listen的第二个参数本质上就是维护一个队列的大小
当client向server发送请求建立连接的时候,如果当前连接数已经满了,会将请求建立连接的client加入到该队列中该队列的大小就是listen的第二个参数
验证:设置listen的第二个参数大小为3
此时启动3个客户端同时连接服务器,用netstat查看服务器状态,一切正常.
但是启动第四个客户端时,发现服务器对于第四个连接的状态存在问题了

如图所示:

客户端状态正常, 但是服务器端出现了 SYN_RECV 状态, 而不是 ESTABLISHED 状态
这是因为, Linux内核协议栈为一个tcp连接管理使用两个队列:

1. 半链接队列(用来保存处于SYN_SENT和SYN_RECV状态的请求)
2. 全连接队列(accpetd队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求)
而全连接队列的长度会受到 listen 第二个参数的影响.
全连接队列满了的时候, 就无法继续让当前连接的状态进入 established 状态了.
这个队列的长度通过上述实验可知, 是 listen 的第二个参数 + 1. 

总结

以上就是TCP协议实现的全部内容,相信看完UDP协议的实现和TCP协议的实现对比而言,TCP为了实现可靠性,采取了相当多的策略,感谢大家的阅读,今天的介绍就到这里了,我们下次再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/97502.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GeoServe Web管理界面远程访问GeoServe Web管理界面的最佳工具

文章目录 前言1.安装GeoServer2. windows 安装 cpolar3. 创建公网访问地址4. 公网访问Geo Servcer服务5. 固定公网HTTP地址 前言 GeoServer是OGC Web服务器规范的J2EE实现,利用GeoServer可以方便地发布地图数据,允许用户对要素数据进行更新、删除、插入…

Axure RP PC电商平台Web端交互原型模板

Axure RP PC电商平台Web端交互原型模板。原型图内容齐全,包含了用户中心、会员中心、优惠券、积分、互动社区、运营推广、内容推荐、商品展示、订单流程、订单管理、售后及服务等完整的电商体系功能架构和业务流程。 在设计尺寸方面,本套模板按照主流的…

Web安全测试(五):XSS攻击—存储式XSS漏洞

一、前言 结合内部资料,与安全渗透部门同事合力整理的安全测试相关资料教程,全方位涵盖电商、支付、金融、网络、数据库等领域的安全测试,覆盖Web、APP、中间件、内外网、Linux、Windows多个平台。学完后一定能成为安全大佬! 全部…

NPM 常用命令(二)

目录 1、npm bugs 1.1 配置 browser registry 2、npm cache 2.1 概要 2.2 详情 2.3 关于缓存设计的说明 2.4 配置 cache 3、 npm ci 3.1 描述 3.2 配置 install-strategy legacy-bundling global-style omit strict-peer-deps foreground-scripts ignore-s…

亚马逊云科技通过生成式AI,帮助清华RIOS加速计算和分析的处理效率

近日,硬件创建平台Efabless宣布了其第一届“生成式AI开源芯片设计挑战赛”(AI Generated Open-Source Silicon Design Challenge)的评选结果。来自清华大学的RISC-V国际开源实验室(RIOS Lab)团队基于亚马逊云科技云上科…

机器人制作开源方案 | 桌面级全向底盘--本体说明+驱动控制

一、本体说明 1. 底盘概述 该底盘是一款模块化的桌面级应用型底盘,基于应用级软件架构设计、应用级硬件系统设计、典型应用型底盘机械系统设计。 底盘本体为一个采用半独立刚性悬挂的四驱全向底盘。 2. 软件环境介绍 操作系统:Ubuntu18.04系统。基于Deb…

电脑怎么设置定时关机,2个简单的操作

电脑作为现代生活中不可或缺的工具,我们通常会在工作或娱乐过程中使用它。但有时候,我们可能需要在一段时间后自动关机,例如在下载完成后或在睡觉前。那么电脑怎么设置定时关机呢?为了满足这种需求,电脑提供了多种定时…

Mybatis的关系关联配置

前言 MyBatis是一个流行的Java持久化框架,它提供了一种简单而强大的方式来映射Java对象和关系数据库之间的数据。在MyBatis中,关系关联配置是一种用于定义对象之间关系的方式,它允许我们在查询数据库时同时获取相关联的对象。 在MyBatis中&…

docker使用(一)生成,启动,更新(容器暂停,删除,再生成)

docker使用(一) 编写一个 Dockerfile构建镜像构建失败构建成功 运行镜像运行成功 修改代码后再次构建请不要直接进行构建,要将原有的旧容器删除或暂停停止成功删除成功再次构建且构建成功! 要创建一个镜像,你可以按照以…

ChatGPT 一条命令总结Mysql所有知识点

想学习Mysql的同学,可以使用ChatGPT直接总结mysql所有的内容与知识点大纲 输入 总结Mysql数据库所有内容大纲与大纲细分内容 ChatGPT不光生成内容,并且直接完成了思维导图。 AIGC ChatGPT ,BI商业智能, 可视化Tableau, PowerBI, FineReport, 数据库Mysql Oracle, Offi…

桌面应用小程序,一种创新的跨端开发方案

Qt Group在提及2023年有桌面端应用程序开发热门趋势时,曾经提及三点: 关注用户体验:无论您是为桌面端、移动端,还是为两者一起开发应用程序,有一点是可以确定的:随着市场竞争日益激烈,对产品的期…

Python+Requests+Pytest+Excel+Allure 接口自动化测试项目实战【框架之间的对比】

--------UnitTest框架和PyTest框架的简单认识对比与项目实战-------- 定义: Unittest是Python标准库中自带的单元测试框架,Unittest有时候也被称为PyUnit,就像JUnit是Java语言的标准单元测试框架一样,Unittest则是Python语言的标…