Linux socket网络编程实战(tcp)实现双方聊天

在上节已经系统介绍了大致的流程和相关的API,这节就开始写代码!

回顾上节的流程:

创建一个NET文件夹 来存放网络编程相关的代码:

tcp服务端代码初步实现--上

这部分先实现服务器的连接部分的代码并进行验证

server1.c:

#include <sys/types.h>     
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/in.h>
#include <string.h>



int main()
{
	int sockfd;
	int conn_sockfd;
	int ret;

	struct sockaddr_in my_addr;
	struct sockaddr_in client_addr;
	memset(&my_addr,0,sizeof(struct sockaddr_in));
	memset(&client_addr,0,sizeof(struct sockaddr_in));

	//socket
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return 1;
	}else{
		printf("socket success, sockfd = %d\n",sockfd);
	}

	//bind
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8888);//host to net (2 bytes)
	inet_aton("192.168.20.137",&my_addr.sin_addr); //char* format -> net format

	ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_in));
	if(ret == -1){
                perror("bind");
                return 1;
        }else{
               printf("bind success\n");
        }

	//listen
	ret = listen(sockfd,10);
	if(ret == -1){
                perror("listen");
                return 1;
        }else{
               printf("listening...\n");
        }

	//accept
	int len = sizeof(struct sockaddr_in);
	conn_sockfd = accept(sockfd,(struct sockaddr *)&client_addr,&len);
	if(conn_sockfd == -1){
                perror("accept");
                return 1;
        }else{
               printf("accept success, client IP = %s\n",inet_ntoa(client_addr.sin_addr));//将网络格式的IP地址再转回字符串

        }

	//read
	

	//write
	
	//read
	
	//close


	return 0;
}

代码验证:

先编译并运行这部分代码:

可见,此时没有客户端进行连接,程序会阻塞在监听的阶段

此时打开windows的cmd(windows系统和linux虚拟机的系统可以看作两台不同的终端

telnet指令使用的也是TCP协议

执行这条命令后,windows的cmd变成了这样:

再反观linux虚拟机:

使用windows的ipconfig可以验证IP地址 :

所以,连接部分的代码已经成功,只是因为没有接下来的数据传输所以退出了。 

tcp服务端代码初步实现--下

这部分实现服务器的连接成功后的读写并验证

server1.c:

#include <sys/types.h>     
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/in.h>
#include <string.h>



int main()
{
	int sockfd;
	int conn_sockfd;
	int ret;
	int n_read;
	int n_write;
	char readbuf[1024];

	struct sockaddr_in my_addr;
	struct sockaddr_in client_addr;
	memset(&my_addr,0,sizeof(struct sockaddr_in));
	memset(&client_addr,0,sizeof(struct sockaddr_in));

	//socket
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return 1;
	}else{
		printf("socket success, sockfd = %d\n",sockfd);
	}

	//bind
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(8888);//host to net (2 bytes)
	inet_aton("192.168.20.137",&my_addr.sin_addr); //char* format -> net format

	ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_in));
	if(ret == -1){
                perror("bind");
                return 1;
        }else{
               printf("bind success\n");
        }

	//listen
	ret = listen(sockfd,10);
	if(ret == -1){
                perror("listen");
                return 1;
        }else{
               printf("listening...\n");
        }

	//accept
	int len = sizeof(struct sockaddr_in);
	conn_sockfd = accept(sockfd,(struct sockaddr *)&client_addr,&len);
	if(conn_sockfd == -1){
                perror("accept");
                return 1;
        }else{
               printf("accept success, client IP = %s\n",inet_ntoa(client_addr.sin_addr));

        }

	//read
	n_read = read(conn_sockfd,&readbuf,1024);
	if(n_read == -1){
		perror("read");
		return 1;
	}else{
		printf("%d bytes has been read, context = %s\n",n_read,readbuf);
	}

	//write
	char *msg = "this is server, I have received your msg\n";
	n_write = write(conn_sockfd,msg,strlen(msg));
	if(n_write == -1){
                perror("write");
                return 1;
        }else{
                printf("%d bytes has been written\n",n_write);
        }
	
	//read
	
	
	//close


	return 0;
}

代码验证:

这部分如果用windows的telnet,打一个符号就会直接结束,所以用Linux另开一个cmd使用telnet来模拟服务器和客户端的对话:

还是先运行代码:

然后运行telnet:

反观服务端:

 

在连接成功后客户端输入一句话然后回车:

客户端显示:

服务器显示:

可见,数据的交互基本没什么问题。

至此,构建了一个大致的服务器代码框架,可以开始着手编写客户端的代码了。

客户端代码初步实现

client.c:

#include <sys/types.h>     
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/in.h>
#include <string.h>



int main()
{
	int sockfd;
	int ret;
	int n_read;
	int n_write;
	char readbuf[1024];

	struct sockaddr_in server_addr;
	memset(&server_addr,0,sizeof(struct sockaddr_in));

	//socket
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return 1;
	}else{
		printf("socket success, sockfd = %d\n",sockfd);
	}

	//connect
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(8888);//host to net (2 bytes)
	inet_aton("192.168.20.137",&server_addr.sin_addr); 
	ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
	if(ret == -1){
		perror("connect");
		return 1;
	}else{
		printf("connect success!\n");
	}

	//write
        char *msg = "client: hello\n";
        n_write = write(sockfd,msg,strlen(msg));
        if(n_write == -1){
                perror("write");
                return 1;
        }else{
                printf("%d bytes has been written\n",n_write);
        }

	//read
	n_read = read(sockfd,&readbuf,1024);
	if(n_read == -1){
		perror("read");
		return 1;
	}else{
		printf("%d bytes has been read, context = %s\n",n_read,readbuf);
	}

	
	
	//close


	return 0;
}

代码验证:

先编译并允许服务端:

再编译并运行客户端:

 回看服务端:

 

客户端 & 服务端 代码的最终实现

之前,已经大致编写出了客户端和服务端的代码,但是和本节开头的框图对比,发现还差一些,首先,数据的发送和读取应该持续进行,直到收到结束信号,另外在最后要编写关闭套接字,关闭连接的代码,且服务器应该可以接受多个客户端的数据,还有一些细节的优化

回顾之前关于fork函数的一节:进程的创建_mjmmm的博客-CSDN博客

 所以此处可以使用fork函数!

小知识点:自动对齐(gg=G)

(参考:linux代码对齐快捷键和man帮助文档的使用总结_陌上花开缓缓归以的博客-CSDN博客)

在命令模式下(即非“插入”等编辑模式),先输入gg,这时候光标会移动到第一行第一个字符,然后按 “=” 号之后切换成大写,再按一下G,这时候光标会移到最后一行的第一个字符,这时候就可以看到代码被排得整整齐齐了!
“gg"将光标移动到代码首部,”="表示对齐指令,"G"表示代码尾部,所以执行"gg=G"后,该文件的所有代码都将对齐!

实现思路

服务端:

在listen之后,进入一个while(1)循环,并调用accept阻塞,一旦连接到一个客户端,就fork一个子进程来处理数据,父进程则继续通过while(1)继续调用accept阻塞等待下一个客户端连接。

而子进程中再次调用fork,父进程写一个while(1)不断的写数据,子进程写一个while(1)不断的读数据。

客户端:

在connect之后进行fork,父进程写一个while(1)不断的写数据,子进程写一个while(1)不断的读数据。

如何退出:

我希望的退出方式是客户端输入“quit”就会退出,但是不管是客户端还是服务端,为了读和写不会相互阻塞,都在不同的进程中的while(1)里,当客户端输入“quit”之后,只有客户端的写端和服务器的读端知道,客户端的读端和服务器的写端并不知情,所以需要使用进程间的通讯,此处我使用了FIFO


在客户端中创建FIFO并在不断写数据的父进程不断检测是否输入了“quit”如果是就只写打开fifo,并阻塞等待....一旦等待到了有进程只读打开FIFO,就会往FIFO写入“quit”,然后关闭FIFO;关闭套接字;收集子进程退出状态;然后退出循环;正常退出。 同时在不断读数据的子进程不断非阻塞的只读打开fifo,并每次都将光标移到最开头,一旦从FIFO读取到了“quit”,就exit。


但是,不断读数据的子进程会阻塞读取服务器传来(写入)的数据,这就导致,当客户端输入“quit”之后无法立刻退出,而是要等到服务器再发来消息,才能进行下一轮的FIFO读取,才能使得子进程收到父进程通过FIFO发来的“quit”并退出。解决办法就是:在服务器端中一旦检测到客户端发来的消息是quit之后,就立刻给客户端发送一句话


此时,对于客户端来说输入了“quit”之后会立刻退出,但是服务端只有读端的while可以退出,写端的while无法退出,此时就有一个疑问“我在读端关闭了客户端套接字,照理说写端应该往这个套接字里写会报错,我直接在报错处理函数里退出写端不就行了”,但其实这是行不通的,因为文件描述符的作用域默认情况下只在进程内有效,而无法在进程之间进行传递。所以还是需要使用FIFO,且注意,FIFO是进程与进程间的,客户端和服务端本质也属于两个进程,所以服务端如果要使用FIFO应该在mkfifo函数中对于FIFO的名字修改,不要和服务端的FIFO重名


在服务器端创建另一个FIFO并在不断读数据的子进程不断判断是否从客户端收到了“quit”,如果收到了就立刻回复(写)一个“Bye”(也就是为了让客户端能立刻退出);只写打开FIFO,并阻塞等待....一旦等待到了有进程只读打开FIFO,就会往FIFO写入“quit”,然后exit。 同时在不断写数据的父进程不断非阻塞的只读打开fifo,并每次都将光标移到最开头,一旦从FIFO读取到了“quit”,就关闭FIFO;关闭客户端的套接字;收集子进程退出状态;然后退出循环;执行fork之后的exit。

程序框图:

server_final.c:

#include <sys/types.h>     
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/in.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>


int main(int argc, char **argv)
{
	int conn_num = 0;
	int flag = 0;
	int sockfd;
	int conn_sockfd;
	int ret;
	int n_read;
	int n_write;
	int len = sizeof(struct sockaddr_in);
	char readbuf[128];
	char msg[128];

	int fd; //fifo
	char fifo_readbuf[20] = {0};
	char *fifo_msg = "quit";

	pid_t fork_return;
	pid_t fork_return_1;

	struct sockaddr_in my_addr;
	struct sockaddr_in client_addr;
	memset(&my_addr,0,sizeof(struct sockaddr_in));
	memset(&client_addr,0,sizeof(struct sockaddr_in));

	if(argc != 3){
		printf("param error!\n");
		return 1;
	}

	//socket
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return 1;
	}else{
		printf("socket success, sockfd = %d\n",sockfd);
	}

	//bind
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(atoi(argv[2]));//host to net (2 bytes)
	inet_aton(argv[1],&my_addr.sin_addr); //char* format -> net format

	ret = bind(sockfd, (struct sockaddr *)&my_addr, len);
	if(ret == -1){
		perror("bind");
		return 1;
	}else{
		printf("bind success\n");
	}

	//listen
	ret = listen(sockfd,10);
	if(ret == -1){
		perror("listen");
		return 1;
	}else{
		printf("listening...\n");
	}

	//fifo
	if(mkfifo("./fifo1",S_IRWXU) == -1 && errno != EEXIST)
	{
		perror("fifo");
	}


	while(1){
		//accept
		conn_sockfd = accept(sockfd,(struct sockaddr *)&client_addr,&len);
		if(conn_sockfd == -1){
			perror("accept");
			return 1;
		}else{
			conn_num++;
			if(conn_num > 1){
				printf("there are more then one client, msg may not be sent accuratly!\n");
			}
			printf("accept success, no.%d client IP = %s\n",conn_num,inet_ntoa(client_addr.sin_addr));

		}

		fork_return = fork();

		if(fork_return > 0){//father keeps waiting for new request
			//wait(NULL); //cant wait,will block	
		}else if(fork_return < 0){
			perror("fork");
			return 1;
		}else{//son deals with request
			fork_return_1 = fork();
			if(fork_return_1 > 0){//father keeps writing msg
				while(1){
					fd = open("./fifo1",O_RDONLY|O_NONBLOCK);
					lseek(fd, 0, SEEK_SET);
					read(fd,&fifo_readbuf,20);
					//printf("read from fifo:%s\n",fifo_readbuf);
					if(fifo_readbuf[0]=='q' && fifo_readbuf[1]=='u' && fifo_readbuf[2]=='i' && fifo_readbuf[3]=='t'){
						printf("sorry,the last msg sent fail,client has quit\n");
						close(fd);
						close(conn_sockfd);
						wait(NULL);
						break;
					}


					//write
					memset(&msg,0,sizeof(msg));
					//printf("\ntype msg:");
					scanf("%s",(char *)msg);
					n_write = write(conn_sockfd,&msg,strlen(msg));
					if(n_write == -1){
						perror("write");
						return 1;
					}else{
						printf("%d bytes msg sent\n",n_write);

					}
				}

			}else if(fork_return_1 < 0){
				perror("fork");
				return 1;
			}else{//son keeps reading msg
				while(1){
					//read
					memset(&readbuf,0,sizeof(readbuf));
					n_read = read(conn_sockfd,&readbuf,128);
					if(readbuf[0]=='q' && readbuf[1]=='u' && readbuf[2]=='i' && readbuf[3]=='t'){
						printf("client quit\n");
						conn_num--;
						printf("%d client remain\n",conn_num);
						write(conn_sockfd,"BYE",3);
						fd = open("./fifo1",O_WRONLY);
						write(fd,fifo_msg,strlen(fifo_msg));
						exit(1);
					}
					if(n_read == -1){
						perror("read");
						return 1;
					}else{
						printf("\nclient: %s\n",readbuf);
					}

				}
			}
			exit(2);
		}

	}


	return 0;
}

client_final.c:

#include <sys/types.h>     
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/in.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>


int main(int argc, char **argv)
{
	int sockfd;
	int ret;
	int n_read;
	int n_write;
	char readbuf[128];
	char msg[128];

	int fd; //fifo
	char fifo_readbuf[20] = {0};
	char *fifo_msg = "quit";

	pid_t fork_return;

	if(argc != 3){
		printf("param error!\n");
		return 1;
	}


	struct sockaddr_in server_addr;
	memset(&server_addr,0,sizeof(struct sockaddr_in));

	//socket
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return 1;
	}else{
		printf("socket success, sockfd = %d\n",sockfd);
	}

	//connect
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(argv[2]));//host to net (2 bytes)
	inet_aton(argv[1],&server_addr.sin_addr); 
	ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));
	if(ret == -1){
		perror("connect");
		return 1;
	}else{
		printf("connect success!\n");
	}

	//fifo
	if(mkfifo("./fifo",S_IRWXU) == -1 && errno != EEXIST)
	{
		perror("fifo");
	}

	//fork
	fork_return = fork();

	if(fork_return > 0){//father keeps writing msg
		while(1){
			//write
			memset(&msg,0,sizeof(msg));
			//printf("\ntype msg:");
			scanf("%s",(char *)msg);
			n_write = write(sockfd,&msg,strlen(msg));
			if(msg[0]=='q' && msg[1]=='u' && msg[2]=='i' && msg[3]=='t'){
				printf("quit detected!\n");
				fd = open("./fifo",O_WRONLY);
				write(fd,fifo_msg,strlen(fifo_msg));
				close(fd);
				close(sockfd);
				wait(NULL);
				break;
			}
			if(n_write == -1){
				perror("write");
				return 1;
			}else{
				printf("%d bytes msg sent\n",n_write);
			}
		}
	}else if(fork_return < 0){
		perror("fork");
		return 1;
	}else{//son keeps reading 
		while(1){
			fd = open("./fifo",O_RDONLY|O_NONBLOCK);
			lseek(fd, 0, SEEK_SET);
			read(fd,&fifo_readbuf,20);
			//printf("read from fifo:%s\n",fifo_readbuf);
			if(fifo_readbuf[0]=='q' && fifo_readbuf[1]=='u' && fifo_readbuf[2]=='i' && fifo_readbuf[3]=='t'){
				exit(1);
			}

			//read
			memset(&readbuf,0,sizeof(readbuf));
			n_read = read(sockfd,&readbuf,128);
			if(n_read == -1){
				perror("read");
				return 1;
			}else{
				printf("\nserver: %s\n",readbuf);
			}
		}

	}


	return 0;
}

实现效果:

编译并运行server端:

编译并运行client端:

此时回看server端:

 

此时,就可以实现双方聊天的功能了:(服务器左,客户端右)

直到客户端打出“quit” :

客户端会立刻退出,服务器此时只有读端知道客户端退出了

必须再打一句话让服务器写端刷新,这样才能从FIFO读取到信息,让写端也知道客户端退出了:

此时,服务器的读写端也全部退出,再次进入阻塞状态,等待新连接...

下面模拟如果同时有两个或以上连接的时候:(左侧一个服务器,右侧两个客户端) 

 我代码执行的顺序是:连接第一个客户端(右上)--> 服务器发送“hi” --> 连接第二个客户端(右下)--> 服务器发送“hihi” --> 服务器发送“hihihi” --> 服务器发送“hihihihi” 

可见,连接第一个客户端的时候,服务器发送hi准确的到了右上的客户端1,但是当连接第二个客户端了之后,服务器分别发送了“hihi”,“hihihi” ,“hihihihi” ,从服务端看没有任何区别,但实际情况下“hihi”和“hihihihi”到了客户端1,“hihihi”到了客户端2,明显出现了混乱,所以我在代码中设置了提醒,检测到大于1个客户端接入时会提醒。

问题探讨 和 一些思路过程

  • 如刚刚所说,服务器用了两次fork,相当于有3个独立的进程,所以我的conn_num变量的设置实际上是相当残疾的,因为我把conn_num-- 放在了一个子进程里,而fork之后的变量空间都是独立的,所以我的conn_num变量只要有客户端推出就不准了,应该也使用进程间通信来通知,但是鉴于我的代码实现目的本来就是双人聊天,所以多客户端的连接部分就没有深入修改了
  • 在我目前的代码逻辑中,我本来觉得只设置了一个conn_sockfd变量来存放客户端的套接字不合理,因为当多个客户端接入的时候,套接字可能会被覆盖,导致读写异常,但是其实一个就够了,原因也是因为fork之后变量空间也会被复制,根据我的代码逻辑,每出现一个新连接,就会fork一个子进程来处理这个连接的读写,如果有多个客户端,就会有多个子进程,里面的套接字变量名都是conn_sockfd,但其实值是不一样的。
  • 并且,服务端的第一个fork,父进程不能调用wait,因为wait会阻塞直到子进程退出,但我希望父进程不被阻塞而一直while循环等待新连接,所以第一次fork生成的子进程在客户端退出之后会变成僵尸进程,且每有一个新的客户端退出就会多一个僵尸进程,在当前的逻辑下不可避免。

且,当多个客户端连接服务器的时候,服务器会针对每个客户端fork一个子进程来处理,而每一个子进程都会再fork一个读端不停的scanf检测输入

但是进程与进程之间是竞争的关系,所以在cmd中看来,光标一直在闪没有变化,但实际上,可能上一秒这是客户端1对应子进程的scanf,下一秒就变成了客户端2对应子进程的scanf

这就导致了如果此时对着光标输入消息并回车,无法确定收到消息的是客户端1还是客户端2。

  • 解决办法1:使用线程,实现真正的多方数据收发,但是难度很大,需要更多的新知识
  • 解决办法2:舍弃服务器的scanf功能,改为自动回复,这样可以实现多个客户端对服务器的自定义消息发送,但是服务器只能回复预设的内容
  • 解决办法3:(也就是现在实现的效果)舍弃多方发送,只用一个客户端,这样就可以实现客户端和服务器的自定义消息交互,但是此时不能增加更多的客户端

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在小红书进行学习直播

诸神缄默不语-个人CSDN博文目录 因为我是从B站开始的&#xff0c;所以一些直播常识型的东西请见我之前写的如何在B站进行学习直播这一篇。 本篇主要介绍一些小红书之与B站不同之处。 小红书在手机端是可以直接点击“”选择直播的。 文章目录 1. 电脑直播-小红书直播软件2. 电…

二叉树题目:二叉树的右视图

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;二叉树的右视图 出处&#xff1a;199. 二叉树的右视图 难度 4 级 题目描述 要求 给定二叉树的根结点 root \t…

DHCP中继实验

文章目录 一、实验背景与目的二、实验拓扑三、实验需求四、实验解法1. 配置IP地址2.配置R1为DHCP服务器&#xff0c;能够跨网段为192.168.2.0/24网段自动分配IP地址3. 在PC3上Ping 192.168.1.1&#xff0c;确认可以Ping通 摘要&#xff1a; 本实验旨在通过配置DHCP中继实现跨网…

C++项目:网络版本在线五子棋对战

目录 1.项目介绍 2.开发环境 3.核心技术 4. 环境搭建 5.websocketpp 5.1原理解析 5.2报文格式 5.3websocketpp常用接口介绍 5.4websocket服务器 6.JsonCpp使用 6.1Json数据格式 6.2JsonCpp介绍 7.MySQL API 7.1MySQL API介绍 7.2MySQL API使用 7.3实现增删改查…

降噪音频转录 Krisp: v1.40.7 Crack

主打人工智能降噪服务的初创公司「Krisp」近期宣布推出音频转录功能&#xff0c;能对电话和视频会议进行实时设备转录。该软件还整合的ChatGPT&#xff0c;以便快速总结内容&#xff0c;开放测试版于今天上线。 随着线上会议越来越频繁&#xff0c;会议转录已成为团队工作的重…

微服务系统面经之二: 以秒杀系统为例

16 微服务与集群部署 16.1 一个微服务一般会采用集群部署吗&#xff1f; 对于一个微服务是否采用集群部署&#xff0c;这完全取决于具体的业务需求和系统规模。如果一个微服务的访问压力较大&#xff0c;或者需要提供高可用性&#xff0c;那么采用集群部署是一种常见的策略。…

C语言之函数题

目录 1.乘法口诀表 2.交换两个整数 3.函数判断闰年 4.函数判断素数 5.计算斐波那契数 6.递归实现n的k次方 7.计算一个数的每位之和&#xff08;递归&#xff09; 8.字符串逆序&#xff08;递归实现&#xff09; 9.strlen的模拟&#xff08;递归实现&#xff09; 10.求…

NoSQL MongoDB Redis E-R图 UML类图概述

NoSQL NoSQL(Not only SQL)是对不同于传统的关系数据库的数据库管理系统的统称&#xff0c;即广义地来说可以把所有不是关系型数据库的数据库统称为NoSQL。 NoSQL 数据库专门构建用于特定的数据模型&#xff0c;并且具有灵活的架构来构建现代应用程序。NoSQL 数据库使用各种数…

这是一条求助贴(postman测试的时候一直是404)

看到这个问题是404的时候总感觉不该求助大家&#xff0c;404多常见一看就是简单的路径问题&#xff0c;我的好像不是&#xff0c;我把我的问题奉上。 首先我先给出我的url http://10.3.22.195:8080/escloud/rest/escloud_contentws/permissionStatistics/jc-haojl/sz 这是我…

抖音电商,提前批offer!

南京夫子庙茶颜悦色店 摄于2023.8.27 小伙伴们大家好&#xff0c;我是阿秀。 互联网圈有个梗就是"两大码农工厂&#xff1a;南华科、北北邮"&#xff0c;就是说这两所高校的毕业生从事互联网工作的特别多&#xff0c;北邮虽然是211&#xff0c;但在互联网圈子里比很多…

Qt5升级到Qt6分步迁移教程

Qt框架的一个新的长期支持版本6.5最近发布。它为以前的版本引入了许多修复、改进和新功能。有些可能对您的应用程序有用&#xff08;如果不是现在&#xff0c;可能会在将来&#xff09;&#xff0c;因此最好将应用程序迁移到最新版本的框架。 仍然有许多应用程序仍在使用Qt 5&…

瑞芯微:基于RK3568得人脸朝向检测

驾驶员监控系统是基于驾驶员面部图像处理来研究驾驶员状态的实时系统。首先挖掘出人在疲劳状态下的表情特征&#xff0c;然后将这些定性的表情特征进行量化&#xff0c;提取出面部特征点及特征指标作为判断依据&#xff0c;再结合实验数据总结出基于这些参数的识别方法&#xf…

git clone 报SSL证书问题

git命令下运行 git config --global http.sslVerify false 然后再进行重新clone代码

Web安全——信息收集下篇

Web安全 一、网络空间搜索引擎二、扫描敏感目录/文件1、御剑2、7kbstorm3、bbscan4、dirmap5、dirsearch6、gobuster7、网站文件 三、扫描网页备份四、网站头信息收集五、敏感文件搜索1、GitHub搜索2、Google-hacking3、wooyun漏洞库4、网盘搜索5、社工库6、网站注册信息7、js敏…

登录校验-Filter-登录校验过滤器

目录 思路 登录校验Filter-流程 步骤 流程图 登录校验Filter-代码 过滤器类 工具类 测试登录 登录接口功能请求 其他接口功能请求 前后端联调 思路 前端访问登录接口&#xff0c;登陆成功后&#xff0c;服务端会生成一个JWT令牌&#xff0c;并返回给前端&#xff0…

【YonBuilder课堂】“入职申请单”的创建流程

YonBuilder是面向企业组织和个人开发者的低代码开发平台&#xff0c;实现可视化、低代码/无代码开发。提供以元数据驱动、点击拖拽自动化代码生成和多端编译的技术&#xff0c;与开放平台、连接集成平台、DevOps平台无缝整合&#xff0c;形成覆盖业务建模&#xff0c;开发、集成…

模糊测试面面观 | 模糊测试是如何发现异常情况的?

协议模糊测试是一种用于评估通信协议、文件格式和API实现系统安全性和稳定性的关键技术。在模糊测试过程中&#xff0c;监视器扮演着关键角色&#xff0c;它们能够捕获异常情况、错误响应、资源利用等&#xff0c;为测试人员提供有价值的信息&#xff0c;有助于发现潜在漏洞和问…

MySQL----索引

一、索引的概念 索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff08;类似于c语言的链表通过指针指向数据记录的内存地址&#xff09;。使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索引表找到该…

Kubernetes对象深入学习之五:TypeMeta无效之谜

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《Kubernetes对象深入学习之五》系列的第五篇&#xff0c;从前文的分析也能看出&#xff0c;代表对象类型的schema.ObjectKind&#xff0c;于…

Shell编程之流程控制

目录 if判断 case语句 for循环 while循环 if判断 语法&#xff1a; if [ 条件判断表达式 ] then 程序 elif [ 条件判断表达式 ] then 程序 else 程序 fi 注意&#xff1a; [ 条件判断表达式 ]&#xff0c;中括号和条件判断表达式之间必须有空格。if&#xff0c;elif…