python网络编程详解
最近在看《UNIX网络编程 卷1》和《FREEBSD操作系统设计与实现》这两本书,我重点关注了TCP协议相关的内容,结合自己后台开发的经验,写下这篇文章,一方面是为了帮助有需要的人,更重要的是方便自己整理思路,加深理解。
理论基础
OSI网络模型

OSI模型是一个七层模型,实际工程中,层次的划分没有这么细致。一般来说,物理层和数据层对应着硬件和设备驱动程序,例如网卡和网卡驱动。传输层和网络层由操作系统内核实现,当用户进程需要通过网络传输数据,通过系统调用的方式让内核将数据封装为相应的协议格式,进而调用网卡驱动传输数据。顶上三层对应具体的网络应用协议:FTP、HTTP等,这些应用层协议不需要知道具体的通信细节。
传输层
在实际工程中,我们常用的应用层服务(例如:HTTP服务、数据库服务、缓存服务)通信的直接底层就是传输层,下图是一些常用命令涉及的通信协议。

IPv4(Internet Protocol version 4)全称是网际协议版本4,它使用32地址,平时常说的IP协议就是指IPv4,类似于192.168.99.100的地址可以看成4位256进制数据,也就是32网络地址。但随着网络设备爆炸式增长,32地址面临这用完的风险,IPv6(Internet Protocol version 6)应运而生。IPv6使用128位地址,但IPv4地址耗尽的问题有了新的解决方案,目前普遍使用的还是IPv4,IPv6全面取代IPv4还有很长的距离。
UDP (User Datagram Protocol),全称用户数据报协议。UDP提供面向无连接的服务,客户端和服务端不存在任何长期的关系。UDP不提供可靠的通信,它不保证数据报一定送达,也不保证数据包送达的先后顺序,也不保证每份数据报只送达一次。虽然UDP可靠性差,但是消耗资源少,适用在网络环境较好的局域网中,例如不需要精确统计的监控服务(eg: Statsd)。由于使用了UDP,客户端每次打点统计只需要一次发送UDP数据报的IO开销,服务性能损失很小,而且在内网环境数据包一般都能正常到达服务端,也能保证较高的可行度。
TCP(Transmission Control Protocl),全称传输控制协议。和UDP相反,TCP提供了面向连接的服务,而且提供了可靠性保障。平常我们使用的应用层协议,例如HTTP,FTP等,几乎都是建立在TCP协议之上,深入了解TCP的细节对于开发高质量的后台开发和客户端开发都有很好的借鉴意义。下面开始重点介绍TCP协议的细节。
TCP协议
状态转换
为了提供可靠的通信服务,TCP通过三次分节建立连接,四次分节关闭连接,心跳检查判断连接是否正常,因此需要记录连接的状态,TCP一共定义了11种不同的状态。

通过netstat命令可以查看所有的tcp状态。

三路握手

在三路握手之前,服务器必须准备好接收外来的连接。这通常通过调用bind和listen完成被动打开,此时服务进程有一个套接字处于LISTEN状态。在客户端发通过调用connect送一个SYN分节后,服务进程必须确认(ACK)此分节,同时也发送一个SYN分节,这两步在同一分节中完成,通过上面的转台扭转图,可以知道服务进程中会生成一个处于SYN_RCVD状态的套接字。当再次收到客户端的ACK分节后,服务端的套接字状态转变为ESTABLISHED。
客户端通过connect函数发起主动打开,在此之前客户端套接字状态为CLOSED。调用connect导致客户TCP发送一个SYN分节,此时套接字状态有CLOSED变为SYN_SENT,在收到服务器的SYN和ACK后,客户端socket再发送ACK分节,套接字状态变为ESTABLISHED,此时connect返回。
备注:SYN分节中除了有序列号之外,还会有最大分节大小、窗口规模选项、时间戳等TCP参数,具体可以参考协议详细规定。
终止连接

上图展示了客户端执行主动关闭的情形,实际上无论客户端还是服务器,都可以执行主动关闭。一般情况下客户端执行主动关闭较多,所以使用客户端主动关闭为例讲解。
客户端调用close,执行主动关闭时,发送FIN分节,此时客户端套接字状态由ESTABLISED变为FIN_WAIT_1。服务器收到这个FIN,会执行被动关闭,并向客户端发送ACK,FIN的接受也作为一个文件结束符传递给服务进程,如果此时服务进程调用套接字的方法,无论缓存区是否有数据都会返回EOF,服务端套接字状态由ESTABLISED变为为CLOSE_WAIT。客户端接收到ACK后,客户端套接字状态由FIN_WAIT_1变为FIN_WAIT_2。
一段时间后,当服务进程调用close或者shutdown时,也会发生送FIN分节,服务端套接字状态由CLOSE_WAIT变为LAST_ACK。客户端在接收到FIN分节后,发送ACK分节,客户端套接字状态由FIN_WAIT_2变为TIME_WAIT。服务器段接收到客户端的ACK分节,状态变成CLOSED。

在某些情况下,第二和第三分节可能会合并发送。调用close可能会触发主动关闭,当进程正常或者非正常退出时,内核会将该进程所使用的文件描述符对应的打开次数执行减一操作,当某个文件打开次数为0时,也就是说所有的进程都没有使用此文件时,也会触发TCP的主动关闭操作。
TIME_WAIT状态
在终止连接的过程中,主动关闭方套接字最终的状态是TIME_WAIT,在经过2MSL(maximun segment lifetime,每个IP数据报都包含一个跳限的字段,表明数据报能经过的路由最大个数,因此默认每个数据报在因特网中有一个最大存活时间)时间后状态才变为CLOSED,为什么这样设计呢?
这样的设计出于两个考虑:
- 可靠地实现TCP全双工连接的终止。上图的四次分节关闭连接是在正常流程,实际情况中,任何一次分节都可能出现发送失败的情况。主动关闭方最后的一个ACK分节可能会因为路由问题发送失败,为了保证可靠性,需要重新发送保证另一方正确关闭套接字,因此此时的状态不能为CLOSED。
- 允许老的重复分界在网络中消失。加入10.10.89.9的3400端口和206.168.12.12的80端口建立了一个TCP连接,此连接中断后,之前发送的TCP分节可能因为路由循环的问题还在因特网中游荡,而此时这两个机器相同的端口再建立起新的连接后,原来在网络中游荡的分解会对新的连接造成干扰。为了避免这种情况,设置一个2MSL的超时时间,保证之前还在网络中游荡的数据包完全消失。
套接字编程
下图是C语言的套接字函数,考虑Python的socket库只是底层C库的简单封装,接口参数大同小异,而且Python方便上手调试,语法上也更通俗易懂,所以本文使用Python的socket库作为讲解实例。

socket
socket是python套接字类,通过构造函数生成套接字对象,构造函数签名如下

其中family参数指协议族;type参数指套接字类型;protocol值协议类型,或者设置为0,以选择所给定family和type组合的系统默认值;fileno指文件描述符(我从来没用过)。
| family | 说明 |
|---|---|
| AF_INET | IPv4协议 |
| AF_INET6 | IPv6协议 |
| AF_LOCAL | Unix域协议 |
| AF_ROUTE | 路由套接字 |
| AF_KEY | 密钥套接字 |
| type | 说明 |
|---|---|
| SOCK_STREAM | 字节流套接字 |
| SOCK_DGRAM | 数据包套接字 |
| SOCK_SEQPACKET | 有序分组套接字 |
| SOCK_RAW | 原始套接字 |
| protocol | 说明 |
|---|---|
| IPPROTO_TCP | TCP传输协议 |
| IPPROTO_UDP | UDP传输协议 |
并非所有套接字family和type的组合都是有效的,下表给出了一些有效的组合和对应的协议,其中标是的项也是有效的,但是没有找到便捷的缩略词,而空白项是无效组合。

connect
connect用于客户端和服务器建立连接,函数签名如下:

客户端在调用connect之前不必非得调用bind函数,内核会确定源IP地址,并选择一个临时端口作为源端口。如果使用TCP协议,connect将激发TCP的三路握手过程,TCP状态由CLOSED变为SYN_SENT,最终变为ESTABLISHED,在三路握手的过程中,可能会出现下面几种情况导致connect报错。connect失败则套接字不可用,必须关闭,不能对这样的套接字再次调connect函数。
- TCP客户端没有是收到SYN分节响应,一般发生在服务端backlog队列已满的情况下,服务器会对收到的SYN分节不做任何处理。客户端等待一段时间后会重新发送SYN分节,直到等待时间超过上限,才会抛出
ETIMEDOUT错误(对应的python异常是TimeoutError)。 - 对客户端SYN的响应是RST,表明服务端在指定的端口上没有进程在等待与之连接,客户端马上会抛出
ECONNRFUSED错误。下图是用python连接一个未使用的端口,抛出异常ConnectionRefusedError,该异常错误号码111,errno中查找正是ECONNRFUSED对应的错误码。

- 如果发出的SYN在中间的吗某个路由器上引发了目的地不可达错误,客户端会等待一段时间后重新发送,直到等待时间超过上限(和第一种情况类似),此时会抛出
ENETUNREACH或者EHOSTUNREACH错误。下图为关闭本机网络后,用python调用connect,由于网络不可达,异常的错误码为101,errno中查找正是ENETUNREACH错误码。

bind
bind方法把一个本地协议地址赋予给一个套接字,方法签名如下:

在不调用bind的情况下,内核会确定IP地址,并分配临时端口,这种情况很适合客户端,因此客户端在调用connect之前不调用bind方法。而服务端需要一个确定的ip和端口,因此需要调用bind指定地址和端口。一般情况下,服务器都有多个ip地址,除了环路地址127.0.0.1外,还有局域网和公网地址,如果bind绑定的是环路地址127.0.0.1,则只有本机通过环路地址才能访问,如果需要通过任一ip地址都能访问到,可以绑定通配地址0.0.0.0。当指定的端口为0时,内核会分配一个临时端口。
如果端口已经在使用,会抛出EADDRINUSE(errno对应错误码是98)异常,可以通过设置SO_REUSEADDR和SO_REUSEPORT这两个套接字参数让多个进程使用同一个TCP连接。

listen
当创建一个套接字时,默认为主动套接字,也就是说,是一个将调用connect发起连接的客户套接字。listen方法把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的状态请求。根据TCP状态转换图,调用listen导致套接字从CLOSED状态转换到LISTEN状态。此方法参数规定了内核应该为相应套接字排队的最大连接个数,在bind之后,并在accept之前调用。

为了理解backlog参数,我们必须认识到内核为其中任何一个给定的监听套接字维护两个队列:
- 未完成连接队列,每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程,这些套接字处于SYN_RCVD状态。
- 已完成连接队列,每个已完成TCP三路握手过程的客户对应其中一项,这些套接字处于ESTABLISHED状态。

RTT指的是未连接队列中的任何一项在队列中的存活时间。linux下的backlog指的是已完成连接队列的容量,如果服务器长时间未调用accept从此队列中取走数据,当新的客户端通过三路握手重新建立连接时,服务器不会处理收到的SYN分节,而客户端会一直等待并不断重试直到超时。在服务器负载很大的情况下,就会造成客户端连接时间长,所以需要合理设置backlog大小。
accept
accept用于从已完成连接队列头返回下一个已完成连接,如果已完成连接队列为空,那么进程会被投入睡眠(套接字为阻塞方式)。

accept会自动生成一个全新的文件描述符,代表与所返回客户的TCP连接。需要注意的是,此处有两个套接字对象,一个是监听套接字,一个返回的已连接套接字。区分这两个套接字很重要,一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在,内核为每个由服务器进程接受的客户连接创建一个已连接套接字(也就是说TCP三路握手已经完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字会被关闭。
close
close方法用来关闭套接字,方法签名如下:

需要注意的是,close方法并不一定会触发TCP的四分组连接终止序列,当一个已连接套接字被多个进程打开时,关闭套接字只会导致此进程相应描述符的计数值减1,只有所有进程都将该套接字关闭后,套接字的引用计数值小于1以后,系统内核才会开始终止连接操作,这一点在多进程开发过程中需要格外注意。如果确实想在某个TCP连接上发送FIN触发主动关闭,可以调用shutdown方法。
send
send方法用于TCP发送数据,方法签名如下:

每一个TCP套接字都有一个发送缓冲区,默认大小通过socket.SO_SNDBUF查看,当某个进程调用send时,内核从该应用进程的缓冲区复制所有数据到所写套接字的发送缓冲区,如果该套接字的发送缓冲区容不下该应用进程的所有数据(或是应用进程的缓冲区大小大于套接字的发送缓冲区,或是套接字的发送缓冲区已有其他数据),该应用进程将被投入睡眠(套接字阻塞的情况),内核将不从系统调用返回,直到应用进程缓冲区的所有数据都复制到套接字发送缓存区。当对端确认收到数据后,会发送ACK分节,随着对端ACK的不断到达,本端TCP才能从套接字发送缓存区中丢弃已确认的数据。

在类似于HTTP的应用层协议中,客户端在发送完请求数据之后,可以调用s.shutdown(socket.SHUT_WR)告诉服务端所有的数据已经发送完成,服务端通过recv会读取到空字符串,之后就可以处理请求数据了。
recv
recv方法用于TCP接收数据,方法签名如下:

每一个TCP套接字也都有一个接受缓存区,默认大小通过socket.SO_RCVBUF查看。当某个进程调用recv而且缓存区没有数据时,该进程会被投入睡眠(套接字阻塞的情况),内核将不从系统调用返回。
在《Unix网络编程》中,所有C语言调用accept,read, write函数都会检查errno是否等于EINTR,这是因为进程在执行这些系统调用的时候可能会被信号打断,导致系统调用返回。而我自己用python2.7尝试的时候发现并没有此问题,猜测是python针对系统调用被信号打断的情况。
IO多路复用
在做服务器开发的时候,经常会碰到处理多个套接字的情形,此时可以通过多进程或这多线程的模型解决此问题。用一个主进程或者主线程负责监听套接字,其它每个进程或线程负责一个已连接套接字,这样还可以利用操作系统的线程切换实现多并发,提高机器利用率。但是机器资源有限,不可能无限制的生成新线程或进程,IO多路复用应运而生。当内核一旦发现进程指定的一个或者多个IO条件就绪,它就通知进程。
IO模型
Unix下有5中IO模型:
- 阻塞式IO
- 非阻塞式IO
- IO复用
- 信号驱动IO
- 异步IO
已读取数据为例,讲解这物种IO模型的区别。每次读取数据包括以下两个阶段,而这五种模型的不同之处也体现在这两个阶段不同的处理。
- 等待数据准备好
- 从内核想进程复制数据
阻塞式IO
socket套接字默认就是阻塞式IO。以recvfrom为例,用户进程通过系统调用获取TCP数据,如果套接字缓存区没有数据,系统调用不会返回,造成用户进程一直阻塞。直到缓存区有可用数据,内核将缓存区数据拷贝至用户进程空间,系统调用才会返回。

非阻塞式IO
python可以通过调用s.setblocking(False)或者s.settimeout(0.0)将一个套接字设置为非阻塞式IO。以recvfrom为例,当没有可用的数据时,用户进程不会阻塞,而是马上抛出EWOULDBLOCK错误(或者EAGAIN,对应的errno错误码都是11),只有当数据复制到内核空间后,才会正确返回数据。

IO多路复用
在有多个IO操作时,先阻塞于select调用,等待数据报套接字变为可读,然后再通过recvfrom把缓存区数据复制到用户进程空间。和阻塞是IO相比,当处理的套接字个数较少的时候,多路复其实没有性能上的优势,它的优势在于可以方便操作很多套接字。

信号驱动式IO
通过信号处理的方式读取数据。

异步IO
当数据包被复制到用户进程后,用户通过callback的方式获取数据。

模型对比

可以发现,前四种IO模型——阻塞式IO、非阻塞式IO、IO复用、信号驱动IO都是同步IO模型,因为真正的IO操作(recvfrom)将阻塞进程,只有异步IO模型才不会导致用户进程阻塞。
python使用
较早的时候使用的多路复用是select函数,但是由于时间复杂度较高,很快就被其他的函数替代:linux下的epoll,unix下的kqueue,windows下的iocp。为了屏蔽不同系统下的不同实现,跨平台的第三方库出现:libuv、libev、libevent等,这些库根据平台的不同,调用不同的底层代码。
如果想直接使用底层的epoll或者select,它们封装在python的select库中;libuv、libev都有相应的python封装,库名叫做pyuv、pyev,通过pip安装后即可使用。
python示例
一般情况下,为了提升服务的承载量,都会采用进程+IO多路复用或者线程+IO多路复用的开发模式。IO多路复用是为了一个并发单位管理多个套接字,而多进程或者多线程是为了充分利用多核。由于GIL的存在,python多线程模型并不能充分多核,因此我们常见的wsgi server,例如:gunicorn、uwsgi、tornado等都是使用的多进程+IO多路服用开发模式。
tornado使用epoll管理多个套接字,gunicorn和uwsgi都可以使用gevent,gevent是一个python网络库,用greenlet做协程切换,每个协程管理一个套接字,主协程通过libevent轮询查找可用的套接字。因为gevent可以通过monkey patch将socket设置为非阻塞模式,因此当服务器有数据库、缓存或者其他网络请求的时候,相比tornado,uwsgi和gunicorn可以充分利用这部分的阻塞时间。和gunicorn相比,uwsgi是c语言实现,直观感觉这三个server的性能应该是:uwsgi > gunicorn > tornado,和网上的benchmark大致匹配。
| |50|100|150|200|250|300|350|400|450|
|---|---|---|---|---|---|---|---|---|---|---|
| libev| 92| 181| 269.9| 355.2| 362.6| 367.1| 373.8| 378.5| 315(3%)|
| thread| 88.9| 180.5| 266.1| 354.8| 428.9| 460.2| 486.5(2%)| 477.9(7%)| 486.5(22%)|

横坐标是连接个数,纵坐标是qps,括号内的数字表示错误率。在连接数较少的情况下,使用libev管理socket和多线程性能相差不大,在连接数超过200后,libev模型的请求耗时会增加,导致qps增加的并不多,但是线程模型在连接数很多的情况下,会导致部分请求一直得不到处理,在连接个数350的时候就会出现部分请求超时,而libev模型在450的时候才会出现。
相关文章:
python网络编程详解
最近在看《UNIX网络编程 卷1》和《FREEBSD操作系统设计与实现》这两本书,我重点关注了TCP协议相关的内容,结合自己后台开发的经验,写下这篇文章,一方面是为了帮助有需要的人,更重要的是方便自己整理思路,加…...
ICRA 2023 | 首个联合暗光增强和深度估计的自监督方法STEPS
原文链接:https://www.techbeat.net/article-info?id4629 作者:郑宇鹏 本文中,我们提出了STEPS,第一个自监督框架来联合学习图像增强和夜间深度估计的方法。它可以同时训练图像增强网络和深度估计网络,并利用了图像增…...
基于react+nodejs+mysql开发用户中心,用于项管理加入的项目的用户认证
基于reactnodejsmysql开发用户中心,用于项管理加入的项目的用户认证用户中心功能介绍页面截图后端采用架构user表projects表project_user表仓库地址用户中心功能介绍 用户中心项目,用于统一管理用户信息、登录、注册、鉴权等 功能如下: 用…...
mapreduce与yarn
文章目录一、MapReduce1.1、MapReduce思想1.2、MapReduce实例进程1.3、MapReduce阶段组成1.4、MapReduce数据类型1.5、MapReduce关键类1.6、MapReduce执行流程1.6.1、Map阶段执行流程1.6.2、Map的shuffle阶段执行流程1.6.3、Reduce阶段执行流程1.7、MapReduce实例WordCount二、…...
鲲鹏云服务器上使用 traceroute 命令跟踪路由
traceroute 命令跟踪路由 它由遍布全球的几万局域网和数百万台计算机组成,并通过用于异构网络的TCP/IP协议进行网间通信。互联网中,信息的传送是通过网中许多段的传输介质和设备(路由器,交换机,服务器,网关…...
代码随想录算法训练营第47天 || 198.打家劫舍 || 213.打家劫舍II || 337.打家劫舍III
代码随想录算法训练营第47天 || 198.打家劫舍 || 213.打家劫舍II || 337.打家劫舍III 198.打家劫舍 题目介绍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&…...
JVM调优方式
对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数。 1.Full GC 会对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比较慢,因此应该尽可能减少Full GC的次数。 2.导致Full GC的原因 1)年老…...
机器学习模型监控的 9 个技巧
机器学习 (ML) 模型是非常敏感的软件;它们的成功使用需要进行仔细监控以确保它们可以正常工作。当使用所述模型的输出自动做出业务决策时尤其如此。这意味着有缺陷的模型通常会对终端客户的体验产生真正的影响。因此,监控输入数据(和输出&…...
Linux 实现鼠标侧边键实现代码与网页的前进、后退
前言 之前一直是使用windows进行开发,最近转到linux后使用VsCode编写代码。 但是不像在win环境下,使用鼠标侧边键可以实现代码的前向、后向跳转。浏览网页时也不行(使用Alt Left可以后退)。 修改键盘映射实在没有那么方便&…...
健身蓝牙耳机推荐,推荐五款适合健身的蓝牙耳机
出门运动健身,有音乐的陪伴是我们坚持运动的不懈动力,在健身当中佩戴的耳机,佩戴舒适度以及牢固程度是我们十分需要注意的,还不知道如何选择健身蓝牙耳机,可以看看下面这些运动蓝牙耳机分享。 1、南卡Runner Pro4骨传…...
Type-c诱骗取电芯片大全
随着Type-C的普及和推广,目前市面上的电子设备正在慢慢淘汰micro-USB接口,逐渐都更新成了Type-C接口,micro-USB接口从2007年上市,已经陪伴我们走过十多个年头,如今也慢慢退出舞台。 今天我们评测的产品是市面上Type-C…...
Scala模式匹配详解(第八章:基本语法、模式守卫、模式匹配类型)(尚硅谷笔记)
模式匹配第 8 章 模式匹配8.1 基本语法8.2 模式守卫8.3 模式匹配类型8.3.1 匹配常量8.3.2 匹配类型8.3.3 匹配数组8.3.4 匹配列表8.3.5 匹配元组8.3.6 匹配对象及样例类8.4 变量声明中的模式匹配8.5 for 表达式中的模式匹配8.6 偏函数中的模式匹配(了解)第 8 章 模式匹配 Scal…...
Linux:基于libevent读写管道代码
基于libevent读写管道代码: 读端: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <event2/event.h> #include…...
2022年中职网络安全逆向题目整理合集
中职网络安全逆向题目整理合集逆向分析:PE01.exe算法破解:flag0072算法破解:flag0073算法破解:CrackMe.exe远程代码执行渗透测试天津逆向re1 re2逆向分析:PE01.exe FTPServer20220509(关闭链接) FTP用户名:PE01密码…...
Tencent OS下逻辑卷(LVM)增加硬盘扩容
上一篇文章写了逻辑卷创建以及使用剩余空间为已经创建的逻辑卷扩容。 本篇是针对卷组空间已经用尽时的扩容方法。那就是增加硬盘。 首先我们为虚拟机增加硬盘/dev/sdd 使用fdisk为/dev/sdd分区,方法在上一篇文章已经描述,在此不再赘述。 新增的硬盘使用如下命令添加到卷组…...
【Java】Spring的创建和使用
Spring的创建和使用 Spring就是一个包含众多工具方法的IOC容器。既然是容器,那么就具备两个最主要的功能: 将对象存储到容器中从容器中将对象取出来 在Java语言当中对象也叫作Bean。 1. 创建Spring项目 创建一个普通maven项目添加Spring框架支持(spri…...
【HTML】HTML 表单 ④ ( textarea 文本域控件 | select 下拉列表控件 )
文章目录一、textarea 文本域控件二、select 下拉列表控件一、textarea 文本域控件 textarea 文本域 控件 是 多行文本输入框 , 标签语法格式如下 : <textarea cols"每行文字字符数" rows"文本行数">多行文本内容 </textarea>实际开发中 并不…...
MySQL 操作 JSON 数据类型
MySQL 从 v5.7.8 开始支持 JSON 数据类型。 JSON 数据类型和传统数据类型的操作还是有很大的差别,需要单独学习掌握。好在 JSON 数据类型的学习成本不算太高,只是在 SQL 语句中扩展了 JSON 函数,操作 JSON 数据类型主要是对函数的学习。 新…...
关于vue3生命周期的使用、了解以及用途(详细版)
生命周期目录前言组合式写法没有 beforeCreate / created 生命周期,并且组合式写生命周期用哪个先引哪个beforeCreatecreatedbeforeMount/onBeforeMountmounted/onMountedbeforeUpdate/onBeforeUpdateupdated/onUpdatedbeforeUnmount/onBeforeUnmountunmounted/onUn…...
2月,真的不要跳槽。
新年已经过去,马上就到金三银四跳槽季了,一些不满现状,被外界的“高薪”“好福利”吸引的人,一般就在这时候毅然决然地跳槽了。 在此展示一套学习笔记 / 面试手册,年后跳槽的朋友可以好好刷一刷,还是挺有必…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...
