TCP协议补充实验

news2024/10/7 10:21:24

目录

一、理解CLOSE_WAIT状态

二、理解TIME_WAIT状态

 解决TIME_WAIT状态引起的bind失败的方法

三、 理解listen的第二个参数

四、SYN洪水

五、TCP、UDP对比

六、优化UDP实现可靠传输(面试题)


一、理解CLOSE_WAIT状态

当客户端和服务器在进行TCP通信时,若客户端调用close函数关闭对应的文件描述符,此时客户端底层操作系统就会向服务器发起FIN请求,服务器收到该请求后会对其进行ACK响应。

但若当服务器收到客户端的FIN请求后,服务器端不调用close函数关闭对应的文件描述符,那么服务器就不会给客户端发送FIN请求,相当于只完成了四次挥手中的前两次挥手,此时客户端和服务器的连接状态分别会变为FIN_WAIT_2和CLOSE_WAIT

可以编写一个简单的TCP服务器端来模拟出该现象,采用一些网络工具来充当客户端向服务器发起连接请求

服务器的初始化需要进行套接字的创建、绑定以及监听,然后主线程就可以通过调用accept函数从底层获取建立好的连接了。获取到连接后主线程创建新线程为该连接提供服务,而新线程只需执行一个死循环逻辑即可

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

const int port = 8082;
const int num = 5;

void* Routine(void* arg)
{
	pthread_detach(pthread_self());
	int fd = *(int*)arg;
	delete (int*)arg;
	while (1){
		std::cout << "socket " << fd << " is serving the client" << std::endl;
		sleep(1);
	}

	return nullptr;
}

int main()
{
	//创建监听套接字
	int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_sock < 0) {
		std::cerr << "socket error" << std::endl;
		return 1;
	}
	//绑定
	struct sockaddr_in local;
	memset(&local, 0, sizeof(local));
	local.sin_port = htons(port);
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = INADDR_ANY;

	if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
		std::cerr << "bind error" << std::endl;
		return 2;
	}
	//监听
	if (listen(listen_sock, num) < 0){
		std::cerr << "listen error" << std::endl;
		return 3;
	}
	//启动服务器
	struct sockaddr_in peer;
	memset(&peer, 0, sizeof(peer));
	socklen_t len = sizeof(peer);
	
	for (;;){
		int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
		if (sock < 0){
			std::cerr << "accept error" << std::endl;
			continue;
		}
		std::cout << "get a new link: " << sock << std::endl;
		int* p = new int(sock);
		
		pthread_t tid;
		pthread_create(&tid, nullptr, Routine, (void*)p);
	}
	return 0;
}

代码编写完成后编译并运行服务器,并用telnet工具连接服务器,最后运行下面监控脚本

[cl@VM-0-15-centos DisconnectStatus]$ while :; do sudo netstat -ntp|head -2&&sudo netstat -ntp | grep 8082; sleep 1; echo "##################"; done

令telnet退出,即客户端向服务器发起了连接断开请求,但此时服务器端并没有调用close函数关闭对应的文件描述符,所以当telnet退出后,客户端维护的连接的状态会变为FIN_WAIT_2,而服务器维护的连接的状态会变为CLOSE_WAIT 

因此若不及时关闭不用的文件描述符,除了会造成文件描述符泄漏,可能导致连接资源没有完全释放,即内存泄漏问题

 

二、理解TIME_WAIT状态

当客户端和服务器在进行TCP通信时,客户端调用close函数关闭对应的文件描述符,若服务器收到后也调用close函数进行了关闭,那么此时双方将正常完成四次挥手。但主动发起四次挥手的一方在四次挥手后,不会立即进入CLOSED状态,而是进入短暂的TIME_WAIT状态等待若干时间,最终才会进入CLOSED状态

继续刚才的实验,由于telnet退出后服务器端没有调用close关闭对应的文件描述符,因此客户端维护的客户端维护连接的状态停留在了FIN_WAIT_2状态,而服务器维护连接的状态停留在了CLOSE_WAIT状态。

要让客户端和服务器继续完成后两次挥手,就需要服务器端调用close函数关闭对应的文件描述符。虽然服务器代码中没有调用close函数,但因为文件描述符的生命周期是随进程的,当进程退出的时候,该进程所对应的文件描述符都会自动关闭。因此只需要在telnet退出后让服务器进程退出就行了,此时服务器进程所对应的文件描述符会自动关闭,此时服务器底层TCP就会向客户端发送FIN请求,完成剩下的两次挥手。

四次挥手后客户端维护的连接就会进入到TIME_WAIT状态,而服务器维护的连接则会进入到CLOSED状态

 解决TIME_WAIT状态引起的bind失败的方法

主动发起四次挥手的一方在四次挥手后,会进入TIME_WAIT状态。若在有客户端连接服务器的情况下服务器进程退出了,就相当于服务器主动发起了四次挥手,此时服务器维护的连接在四次挥手后就会进入TIME_WAIT状态

在该连接处于TIME_WAIT期间,若服务器想重新启动,则会出现绑定失败问题

在TIME_WAIT期间,连接并没有被完全释放,即服务器绑定的端口号仍然被占用,此时服务器若想继续绑定该端口号,则只能等待TIME_WAIT结束。但当服务器崩溃后最重要的是让服务器立马重新启动,若想要让服务器崩溃后在TIME_WAIT期间也能立刻重新启动,需要让服务器在调用socket函数创建套接字后,继续调用setsockopt函数设置端口复用,这也是编写服务器代码时的推荐做法

setsockopt函数

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • sockfd:需要设置的套接字对应的文件描述符
  • level:被设置选项的层次。比如在套接字层设置选项对应就是SOL_SOCKET
  • optname:需要设置的选项。该选项的可取值与设置的level参数有关
  • optval:指向存放选项待设置的新值的指针
  • optlen:待设置的新值的长度

返回值:设置成功返回0,设置失败返回-1,同时错误码会被设置

设置监听套接字,将监听套接字在套接字层设置端口复用选项SO_REUSEADDR,该选项设置为非零值表示开启端口复用

int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

此时当服务器崩溃后即可立马重新启动,而不用等待TIME_WAIT结束

实验

先启动服务器,再启动客户端连接服务器,此时出现两个ESTABLISHED连接

此时向服务器发送信号使得服务器退出,客户端检测到服务器关闭也关闭了文件描述符,由于服务器主动发起四次挥手,其状态变为TIME_WAIT 

由于代码中调用了setsockopt函数,设置监听套接字端口复用,此时可用重新启动服务器。接下来重新启动服务器和客户端,此时出现了三个连接,其中8082端口绑定了两个连接(端口复用)

从上述实验中可以看出,即便通信双方对应的进程都退出,但服务器端依然存在一个处于TIME_WAIT状态的连接,这也说明了进程管理和连接管理是两个相对独立的单元。连接由TCP自行管理,连接不一定会随进程的退出而关闭

 

 

三、 理解listen的第二个参数

在编写TCP套接字的服务器代码时,在进行了套接字的创建和绑定之后,需要调用listen函数将创建的套接字设置为监听状态,此后服务器就可以调用accept函数获取建立好的连接了。其中listen函数的第一个参数是需要设置为监听状态的套接字,而listen的第二个参数一般设置为5,那你知道listen函数的第二个参数具体的含义是什么吗?

实验

下面通过一个实验来说明listen的第二个参数的具体含义:

先编写TCP套接字的服务器端代码,服务器初始化时依次进行套接字创建、绑定、监听,但服务器初始化后不调用accept函数获取底层建立好的连接。为了方便验证,下面将listen函数的第二个参数设置为2

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

const int port = 8081;
const int num = 2;

int main()
{
	//创建监听套接字
	int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_sock < 0){
		std::cerr << "socket error" << std::endl;
		return 1;
	}
	int opt = 1;
	setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	//绑定
	struct sockaddr_in local;
	memset(&local, 0, sizeof(local));
	local.sin_port = htons(port);
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = INADDR_ANY;
	
	if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
		std::cerr << "bind error" << std::endl;
		return 2;
	}
	//监听
	if (listen(listen_sock, num) < 0){
		std::cerr << "listen error" << std::endl;
		return 3;
	}
	//启动服务器
	for (;;){
		//不调用accept获取连接
	}
	return 0;
}

启动服务器后使用netstat -nltp命令,可以看到该服务器当前正处于监听状态

接下来使用Postman向服务器发起一个连接请求 

注意:为了让实验现象更加明显,最好不要使用浏览器测试,因为浏览器发起连接请求后若得不到响应会进行重发

Postman发起连接请求后,此时通过下面命令就可以发现,此时服务器端新增了一个连接,该连接处于ESTABLISHED状态 

sudo netstat -ntp | head -2&&sudo netstat -ntp | grep 8082

 继续使用Postman发起第二个、第三个连接请求

此时在服务器端也会新增对应的第二个、第三个连接,现在服务器端已经建立的连接的个数就是3

现在继续使用Postman向服务器发起第四次连接请求

此时没有继续新增状态为ESTABLISHED的连接,而是新增了一个状态为SYN_RCVD的连接

服务器端新增了一个状态SYN_RCVD的连接,即意味着服务器没有对刚才客户端发来的连接请求进行响应

对于刚才状态为SYN_RCVD的连接,由于服务器长时间不对其进行应答,三次握手失败后该连接会被自动释放

实验现象:

  • 无论有多少客户端向服务器发起连接请求,最终在服务器端最多只有三个连接会建立成功
  • 当发来后续的连接请求时,服务器只是收到了该客户端发来的SYN请求,但并没有对其进行响应

listen的第二个参数

TCP在进行连接管理时会用到两个连接队列:

  • 全连接队列(accept队列)。全连接队列用于保存处于ESTABLISHED状态,但没有被上层调用accept取走的连接
  • 半连接队列。半连接队列用于保存处于SYN_SENT和SYN_RCVD状态的连接,即未完成三次握手的连接

全连接队列长度受到listen第二个参数影响,一般TCP全连接队列的长度等于listen第二个参数的值加一。因为实验时设置listen第二个参数的值为2,所以服务器端全连接队列长度就为3,服务器最多只允许有三个处于ESTABLISHED状态的连接

若将实验代码中listen的第二个参数值设置为3,服务器就最多允许存在4个处于ESTABLISHED状态的连接。在服务器端已有4个ESTABLISHED状态的连接的情况下,再有客户端发来建立连接请求,服务器端就会新增状态为SYN_RCVD的连接,该连接暂时存放在半连接队列中

若半连接队列中也存放满了,此后就算再有客户端发来连接请求,在服务器端也不会新增任何状态的连接

为什么底层要维护连接队列?

一般当服务器压力较大时连接队列的作用才会体现出来,若服务器压力本身就不大,那么一旦底层有连接建立成功,上层就会立马将该连接读走并进行处理

服务器端启动时一般会预先创建多个服务线程为客户端提供服务,主线程从底层accept上来连接后就可以将其交给这些服务线程进行处理。若向服务器发起连接请求的客户端很少,那么连接一旦在底层建立好就被主线程立马accept上来并交给服务线程处理了。但若向服务器发起连接请求的客户端非常多,当每个服务线程都在为某个连接提供服务时,底层再建立好连接主线程就不能获取上来了,此时底层这些已经建立好的连接就会被放到连接队列中,只有等某个服务线程空闲时,主线程就会从这个连接队列中获取建立好的连接。

若没有连接队列,那么当服务器端的服务线程都在提供服务时,其他客户端发来的连接请求就会直接被拒绝。但有可能正当这个连接请求被拒绝时,某个服务线程提供服务完毕,此时这个服务线程就无法立马得到一个连接为之提供服务,所以一定有一段时间内这个服务线程是处于闲置状态的,直到再有客户端发来连接请求。

而若设置了连接队列,当某个服务线程提供完服务后,若连接队列中有建立好的连接,那么主线程就可以立马从连接队列中获取一个连接交给该服务线程进行处理,此时就可以保证服务器几乎是满载工作的,从而提高效率

为什么连接队列不能太长?

全连接队列不能太长,系统一般设置为5。虽然维护连接队列能让服务器处于几乎满载工作的状态,但连接队列也不能设置得太长

  • 若队列太长,也意味着在队列较尾部的连接需要等待较长时间才能得到服务,此时客户端的请求也就迟迟得不到响应
  • 服务器维护连接也是需要成本的,连接队列设置的越长,系统就要花费越多的成本去维护这个队列。与其与其维护一个长连接,造成客户端等待过久,并且占用大量暂时用不到的资源,还不如将部分资源节省出来给服务器使用,让服务器更快的为客户端提供服务

全连接队列的长度

全连接队列的长度由两个值决定:

  • 用户层调用listen时传入的第二个参数backlog
  • 系统变量net.core.somaxconn,默认值为128

通过以下命令可以查看系统变量net.core.somaxconn的值

sudo sysctl -a | grep net.core.somaxconn

全连接队列长度等于listen传入的backlog和系统变量net.core.somaxconn中的较小值加一

四、SYN洪水

连接正常建立的过程

  • 当客户端向服务器发起连接建立请求后,服务器会对其进行SYN+ACK响应,并将该连接放到半连接队列(syns queue)中
  • 当服务器发出的SYN+ACK得到客户端ACK响应后,就会将该连接由半连接队列移到全连接队列(accept queue)中
  • 此时上层就可以通过调用accept函数,从全连接队列中获取建立好的连接了

连接建立异常

  • 若客户端在发起连接建立请求后突然死机或掉线,那么服务器发出的SYN+ACK就得不到对应的ACK应答
  • 这种情况下服务器会进行重试(再次发送SYN+ACK给客户端)并等待一段时间,最终服务器会因为收不到ACK应答而将这个连接丢弃,这段时间长度就称为SYN timeout
  • 在SYN timeout时间内,这个连接会一直维护在半连接队列中

服务器虽然需要短暂维护这些异常连接,但这种情况毕竟是少数,不会对服务器造成太大影响

SYN洪水

但若有一个恶意用户大量模拟这种情况:向服务器发送大量的连接建立请求,但收到服务器的SYN+ACK后故意不对其进行ACK应答

  • 此时服务器就需维护一个非常大的半连接队列,并且这些连接最终都不会建立成功,也就不会被移到全连接队列中供上层获取,最后会导致半连接队列越来越长。
  • 当半连接队列被占满后,新来的连接就会直接被拒绝,哪怕是正常的连接建立请求,此时就会导致正常用户无法访问服务器
  • 这种向服务器发送大量SYN请求,但并不对服务器的SYN+ACK进行ACK响应,最终可能导致服务器无法对外提供服务的行为,就被称为SYN洪水攻击(SYN Flood)

如何解决SYN洪水攻击?

TCP作为传输控制协议需要对其进行处理,而上层应用层也要尽量避免遭到SYN洪水攻击。

应用层可以记录,向服务器发起连接建立请求的主机信息,若发现某个主机多次向服务器发起SYN建立连接请求,但从不对服务器的SYN+ACK进行ACK响应,此时就可以对该主机进行黑名单认证,此后该主机发来的SYN请求一概不进行处理

TCP为了防范SYN洪水攻击,保证正常用户使用,引入了syncookie机制:

核心问题就在于半连接队列被占满了,但不能简单的扩大半连接队列,就算半连接队列再大,恶意用户也会发送更多的SYN请求占满,并且维护半连接队列中的连接也是需要成本的
因此TCP引入了syncookie机制,当服务器收到一个连接建立请求后,会根据这个SYN包计算出一个cookie值,将其作为将要返回的SYN+ACK包的初始序号,然后将这个连接放到一个暂存队列中。当服务器收到客户端的ACK响应时,会提取出当中的cookie值进行对比,对比成功则说明是一个正常连接,此时该连接就会从暂存队列中移到全连接队列供上层读取,保证正常用户的使用

引入了syncookie机制的好处:

  • 引入syncookie机制后,异常连接就不会堆积在半连接队列中了,也就不会出现半连接队列被占满的情况了
  • 对于正常的连接,一般会立即对服务器的SYN+ACK进行ACK应答,因此正常连接会很快建立成功
  • 异常的连接,不会对服务器的SYN+ACK进行ACK应答,因此异常的连接最终都会堆积到暂存队列中,最终被处理

五、TCP、UDP对比

TCP协议

TCP协议被称为传输控制协议(Transmission Control Protocol),TCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议

TCP协议是面向连接的,若两台主机之间想要进行数据传输,那么必须要先建立连接,当连接建立成功后才能进行数据传输。其次,TCP协议是保证可靠的协议,数据在传输过程中如果出现了丢包、乱序等情况,TCP协议都有对应的解决方法

UDP协议

UDP协议被称为用户数据报协议(User Datagram Protocol),UDP协议是一种无需建立连接的、不可靠的、面向数据报的传输层通信协议

使用UDP协议进行通信时无需建立连接,若两台主机之间想要进行数据传输,那么直接将数据发送给对端主机就行了,但这也就意味着UDP协议是不可靠的,数据在传输过程中如果出现了丢包、乱序等情况,UDP协议本身是不知道的。

TCP/UDP对比

TCP协议虽然是保证可靠性的协议,但不能说TCP就一定比UDP好,因为TCP保证可靠性也就意味着TCP需要做更多的工作,而UDP不保证可靠性也就意味着UDP足够简单

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

UDP和TCP没有谁最好,只有谁最合适,网络通信时具体采用TCP还是UDP完全取决于上层的应用场景

六、优化UDP实现可靠传输(面试题)

当面试官让你用UDP实现可靠传输时,一定要立马想到TCP协议,因为TCP协议就是当前比较完善的保证可靠性的协议,面试官要求用UDP这个不可靠的协议来实现可靠传输,无非就是要求在应用层来实现可靠性,此时就可以参考TCP协议保证可靠性的各种机制

例如:

  • 引入序列号,保证数据按序到达
  • 引入确认应答,确保对端接收到了数据
  • 引入超时重传,若隔一段时间没有应答,就进行数据重发

当被面试官问到这个问题时,最好与面试官进一步进行沟通,比如询问这个用UDP实现可靠传输的应用场景是什么。因为TCP保证可靠性的机制太多了,但在某些场景下可能只需要引入TCP的部分机制即可,因此在不同的应用场景下模拟实现UDP可靠传输的侧重点也是不同的

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录--Vue3+TS(uniapp)手撸一个聊天页面

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 Vue3TS(uniapp)手撸一个聊天页面 前言 最近在自己的小程序中做了一个智能客服&#xff0c;API使用的是云厂商的API&#xff0c;然后聊天页面...嗯&#xff0c;找了一下关于UniApp(vite/ts)版本的好像不…

Linux常用命令——ifconfig命令

在线Linux命令查询工具 ifconfig 配置和显示Linux系统网卡的网络参数 补充说明 ifconfig命令被用于配置和显示Linux内核中网络接口的网络参数。用ifconfig命令配置的网卡信息&#xff0c;在网卡重启后机器重启后&#xff0c;配置就不存在。要想将上述的配置信息永远的存的电…

如何用Facebook为你的跨境电商店铺进行引流?

对于跨境电商店铺来说&#xff0c;引流客户代表着潜在的商业机会和利润。当更多潜在客户访问你的店铺并下单购买产品时&#xff0c;这将增加你的销售额和利润&#xff0c;并帮助你的品牌影响力不断扩大。 一、Facebook广告付费玩法 1.创建一个Facebook商业页面&#xff1a;首先…

首发 | 人工智能赋能的未来作战构想(上): 海上作战篇

源自&#xff1a;中国指挥与控制学会 ▲图1&#xff1a;报告封面和封底 一、人工智能赋能改变战争形态 ▲图2&#xff1a;以AI赋能万物互联 二、人工智能赋能的海上作战 ▲图3&#xff1a;海上作战要保持持续有效的火力输出 &#xff08;一&#xff09;海上防御作战构想 ▲图4&…

MODBUS协议下,触摸屏与PLC能否实现无线通讯?

想要实现触摸屏与PLC之间的MODBUS无线通讯&#xff0c;就需要一个满足这两个设备通讯接口&#xff0c;并可在MODBUS协议下运行的通讯终端作为媒介&#xff0c;搭建起这两者之间的通讯桥梁。如果是使用威纶通触摸屏作为主站&#xff0c;三菱FX5U PLC作为从站&#xff0c;便可采用…

代码随想录-数组

学习《代码随想录》 理论基础什么是数组&#xff1f; 二分查找左闭右闭左闭右开 移除元素暴力法双指针法 长度最小的子数组暴力法滑动窗口 螺旋矩阵 理论基础 什么是数组&#xff1f; 数组是存储在连续内存空间上的相同类型数据的集合。 二分查找 有两种解法&#xff1a;左闭…

spring6概述

Spring6要求JDK最低版本是JDK17 Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开…

阿里云李钟:弹性计算控制系统团队的提效之路

2023 年 3 月 25 日&#xff0c;“城市领航之夜第一期”活动在上海举行&#xff0c;阿里云弹性计算控制系统技术架构负责人李钟出席了本次活动并带来了《弹性计算控制系统团队提效之路》的主题演讲&#xff0c;为大家详细分享了阿里云弹性计算控制系统团队所面临的挑战、如何通…

计算机毕业论文选题推荐|软件工程|系列五

文章目录 导文题目导文 计算机毕业论文选题推荐|软件工程 (***语言)==使用其他任何编程语言 例如:基于(***语言)门窗账务管理系统的设计与实现 得到:基于JAVA门窗账务管理系统的设计与实现 基于vue门窗账务管理系统的设计与实现 等等 题目 基于(***语言)遥感图像的居民…

言之画: AI绘画平台

【产品介绍】 言之画是出门问问推出的AI绘画平台。支持二次元、蒸汽朋克、插画等 8 种创作风格。用户只需输入文字&#xff0c;「言之画」就能一次性生成 8 张光影逼真、细节丰富的 2K 高分辨率图像。 除了以文生图&#xff0c;它还拥有以图生图、动图生成、个性头像生成等 AI …

MATLAB 之 常用内部函数,运算,字符串和结构数据与单元数据

文章目录 一、常用内部函数1. 常用数学函数2. 矩阵的超越函数2.1 矩阵平方根2.2 矩阵对数2.3 矩阵指数2.4 普通矩阵函数 二、MATLAB 运算1. 算术运算1.1 基本算术运算1.2 点运算 2. 关系运算3. 逻辑运算 三、字符串1. 字符串的表示2. 字符串的操作2.1 字符串的执行2.2 字符串与…

ChatGPT下的自动化工具Auto-GPT、AgentGPT、BabyAGI和GodMode

ChatGPT 对于一个大目标来说&#xff0c;他是不知道怎么一步一步的帮我们构建出来。有了Auto-GPT、AgentGPT、BabyAGI和GodMode 这些自动化工具&#xff0c;我们只需要输入一个大目标&#xff0c;这些自动化工具能够帮我们一步一步轻松实现。 1.Auto-GPT 网址&#xff1a;htt…

C/C++每日一练(20230512) 成绩打印、补齐数组、水仙花数

目录 1. 成绩打印 ※ 2. 按要求补齐数组 &#x1f31f;&#x1f31f;&#x1f31f; 3. 水仙花数 ※ &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 1. 成绩打印 一个班有10个同学&am…

airserver7.2.7最新中文版下载及功能介绍

最近开会打算把手机投屏到自己的Mac上演示用&#xff0c;于是就打算用下听了很久好用但是一值没有使用的AirServer!十分简单的操作就可以完美的投屏到Mac电脑&#xff0c;而且不用像Mac自带的QuickTime用线连接手机!它可以把AirPlay / AirTunes上的音频、视频、照片、幻灯片还有…

母亲节祝福html源码,母亲节祝福html代码,母亲节表白妈妈代码,母亲节祝福网页代码

母亲节祝福html源码&#xff0c;母亲节祝福html代码&#xff0c;母亲节表白妈妈代码&#xff0c;母亲节祝福网页代码 运行截图 核心代码&#xff1a; <!DOCTYPE HTML> <html lang"zh-Hans"> <head><meta charset"utf-8"><meta…

我们选择护眼台灯的标准是什么?护眼台灯的国家标准

小时候在学校&#xff0c;父母跟老师经常强调“眼睛是心灵的窗户”要保护好眼睛&#xff0c;不管是在学校上学还是在家写作业时&#xff0c;都会很突然的听见“头抬高点”、“眼睛离远点”等类似的劝导&#xff0c;刚开始还不以为然&#xff0c;直到视力下降&#xff0c;带上了…

ctf_BUUCTF_web_总结(待更新)

文章目录 SQL注入文件上传漏洞文件包含漏洞php反序列化RCE模板安全问题&#xff08;SSTI&#xff09;XXE代码审计杂七杂八的知识点正则表达式CTF做题总结 SQL注入 [极客大挑战 2019]EasySQL[SUCTF 2019]EasySQL[强网杯 2019]随便注[极客大挑战 2019]BabySQL[BJDCTF2020]Easy MD…

雪浪云算力开发者大赛

1赛题背景 汽车生产工艺复杂&#xff0c;一辆汽车的制造需要完成焊装、涂装、总装三大工艺&#xff0c; 经过焊装车间、涂装车间、总装车间。各车间存在上下游关联关系&#xff0c;每个车间有自己的优化排序目标&#xff0c;需要综合考虑多种复杂的排序规则及工艺约束&#xf…

9.100ASK_V853-PRO开发板支持E907小核开发

0.前言 ​ 100ASK_V853-PRO开发板的V853芯片集成Arm Cortex-A7和RISC-V E907 双CPU&#xff0c;玄铁E907 是一款完全可综合的高端 MCU 处理器。它兼容 RV32IMAC 指令集&#xff0c;提供可观的整型性能提升以及高能效的浮点性能。E907 的主要特性包括&#xff1a;单双精度浮点单…

centos7设置时区,时间+时间同步的三种方式

centos7设置时区&#xff0c;时间时间同步的三种方式 1.centos7设置时区 1.1查看当前时区&#xff1a; timedatectl 1.2查看时间命令&#xff1a; date1.3选择时区命令 [rootlocalhost test]# tzselect Please identify a location so that time zone rules can be set co…