嵌入式学习第二十七天!(TCP并发模型)

TCP并发模型:

1. TCP多线程模型:

    缺点:创建线程会带来资源开销,能够实现的并发量比较有限。

2. IO模型:

    1. 阻塞IO:

        没有数据到来时,可以让任务挂起,节省CPU资源开销,提高系统效率

    2. 非阻塞IO:

        程序未接收到数据时一直执行,效率很低

        举例应用:

write.c

#include "head.h"

int main(void)
{
	int fd = 0;
	char tmpbuff[1024] = {0};

	mkfifo("/tmp/myfifo", 0664);
	fd = open("/tmp/myfifo", O_WRONLY);
	if(fd == -1)
	{
		perror("fail to open");
		return -1;
	}

	while(1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		gets(tmpbuff);
		write(fd, tmpbuff, strlen(tmpbuff));
	}

	close(fd);
	return 0;
}

        后面的举例应用的write.c都为上面所示

read.c

#include "head.h"

int main(void)
{
	int fd = 0;
	int flags = 0;
	ssize_t nsize = 0;
	char *pret = NULL;
	char tmpbuff[1024] = {0};

	mkfifo("/tmp/myfifo", 0664);
	fd = open("/tmp/myfifo", O_RDONLY);
	if(fd == -1)
	{
		perror("fail to open");
		return -1;
	}
	
	flags = fcntl(fd, F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(fd, F_SETFL, flags);

	flags = fcntl(0, F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(0, F_SETFL, flags);

	while(1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		pret = gets(tmpbuff);
		if(pret != NULL)
		{
			printf("STDIN:%s\n", tmpbuff);
		}

		memset(tmpbuff, 0, sizeof(tmpbuff));
		nsize = read(fd, tmpbuff, sizeof(tmpbuff));
		if(nsize > 0)
		{
			printf("RECV:%s\n", tmpbuff);
		}
	}
	
	close(fd);
	return 0;
}

        通过fcntl将fd和stdin文件描述符设置为非阻塞IO,所以read.c既可以通过管道接收消息,也可以同终端输入,接收消息,二者不会堵塞。

    3. 异步IO:

        只能绑定一个文件描述符用来读取数据

        举例应用:

read.c

#include "head.h"

int fd = 0;

void handler(int signo)
{
	char tmpbuff[1024] = {0};
	ssize_t nsize = 0;

	memset(tmpbuff, 0, sizeof(tmpbuff));
	nsize = read(fd, tmpbuff, sizeof(tmpbuff));
	if(nsize > 0)
	{
		printf("RECV:%s\n", tmpbuff);
	}

	return;

}

int main(void)
{
	int flags = 0;
	ssize_t nsize = 0;
	char *pret = NULL;
	char tmpbuff[1024] = {0};

	signal(SIGIO, handler);

	mkfifo("/tmp/myfifo", 0664);
	fd = open("/tmp/myfifo", O_RDONLY);
	if(fd == -1)
	{
		perror("fail to open");
		return -1;
	}
	
	flags = fcntl(fd, F_GETFL);
	flags |= O_ASYNC;
	fcntl(fd, F_SETFL, flags);
	fcntl(fd, F_SETOWN, getpid());

	while(1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		gets(tmpbuff);
		printf("STDIN:%s\n", tmpbuff);

	}
	
	close(fd);
	return 0;
}

    4. 多路复用:

        1. select:

            1. select监听的集合中的文件描述符有上限限制

            2. select有内核层向用户层数据空间拷贝的过程,占用系统资源开销

                原因:在使用select函数时,会涉及到内核层和用户层之间的数据传输。具体来说:select函数在内核层会监视一组文件描述符的状态变化,并在有变化发生时通知用户层。为了是实现这个功能,内核会定期检测文件描述符的状态,并在发生变化时将相关信息传递给用户层。这个过程涉及到内核层和用户层之间的数据传输,即内核层需要将监视的文件描述符的状态信息传递给用户层。这个传输过程会涉及到系统资源的开销,因为需要进行数据拷贝操作,将内核中的数据拷贝到用户空间,这个数据拷贝的过程会消耗一定的系统资源。

            3. select必须轮询检测产生事件的文件描述符

            4. select只能工作在水平触发模式(低速模式),无法工作在边沿触发模式(高速模式)

                原因:

                    1. 在水平触发模式下,一旦文件描述符中的数据可读或可写,select就会通知用户程序,并且如果这些描述符在之后的select调用中仍然保持可读或可写状态,select会再次通知程序。换句话说,在水平触发模式下,只要描述符的状态处于可读或可写状态,select就会通知程序。

                    2. 在边缘触发模式下,只有当描述符的状态发生变化时才会通知程序,这意味着如果描述符中的数据量发生变化或者有新的数据到达,程序才会被通知,而如果描述符的状态保持不变,即使在之后的select调用中它任然处于可读或可写状态,程序也不会被通知。

            举例应用:

read.c

#include "head.h"

int main(void)
{
	int fd = 0;
	int flags = 0;
	ssize_t nsize = 0;
	char *pret = NULL;
	fd_set rdfds;
	fd_set tmpfds;
	int ret = 0;
	char tmpbuff[1024] = {0};

	mkfifo("/tmp/myfifo", 0664);
	fd = open("/tmp/myfifo", O_RDONLY);
	if(fd == -1)
	{
		perror("fail to open");
		return -1;
	}
	
	FD_ZERO(&rdfds);
	FD_SET(fd, &rdfds);
	FD_SET(0, &rdfds);
	
	while(1)
	{
		tmpfds = rdfds;
		ret = select(fd+1, &tmpfds, NULL, NULL, NULL);
		if(ret == -1)
		{
			perror("fail to select");
			return -1;
		}
		
		if(FD_ISSET(0, &tmpfds))
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			gets(tmpbuff);
			printf("STDIN:%s\n", tmpbuff);
		}

		if(FD_ISSET(fd, &tmpfds))
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			read(fd, tmpbuff, sizeof(tmpbuff));
			printf("FIFO:%s\n", tmpbuff);
		}
	}

	close(fd);
	return 0;
}

        2. poll:

            1. poll有内核层向用户层数据空间拷贝的过程,占用系统资源开销

            2. poll必须轮询检测产生事件的文件描述符

            3. poll只能工作在水平触发模式(低速模式),无法工作在边沿触发模式(高速模式)

            举例应用:

read.c

#include "head.h"

int main(void)
{
	int fd = 0;
	int ret = 0;
	int flags = 0;
	ssize_t nsize = 0;
	struct pollfd fds[2];
	char tmpbuff[1024] = {0};


	mkfifo("/tmp/myfifo", 0664);
	fd = open("/tmp/myfifo", O_RDONLY);
	if(fd == -1)
	{
		perror("fail to open");
		return -1;
	}
	
	fds[0].fd = 0;
	fds[0].events = POLLIN;
	fds[1].fd = fd;
	fds[1].events = POLLIN;

	while(1)
	{
		ret = poll(fds, 2, -1);
		if(ret == -1)
		{
			perror("fail to poll");
			return -1;
		}

		if(fds[0].revents & POLLIN)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			gets(tmpbuff);
			printf("STDIN:%s\n", tmpbuff);
		}

		if(fds[1].revents & POLLIN)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			read(fd, tmpbuff, sizeof(tmpbuff));
			printf("FIFO:%s\n", tmpbuff);
		}
	}
	
	close(fd);
	return 0;
}

        3. epoll:

            举例应用:

read.c

#include "head.h"

int main(void)
{
	int i = 0;
	int fd = 0;
	int epfd = 0;
	int nready = 0;
	ssize_t nsize = 0;
	char tmpbuff[1024] = {0};
	struct epoll_event env;
	struct epoll_event retenv[2];

	mkfifo("/tmp/myfifo", 0664);
	fd = open("/tmp/myfifo", O_RDONLY);
	if(fd == -1)
	{
		perror("fail to open");
		return -1;
	}
	
	epfd = epoll_create(2);
	if(epfd == -1)
	{
		perror("fail to epoll_create");
		return -1;
	}
	
	env.data.fd = 0;
	env.events = EPOLLIN;
	epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &env);
	env.data.fd = fd;
	env.events = EPOLLIN;
	epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);

	while(1)
	{
		nready = epoll_wait(epfd, retenv, 2, -1);
		if(nready == -1)
		{
			perror("fail to epoll_wait");
			return -1;
		}
		
		for(i = 0; i < nready; i++)
		{
			if(retenv[i].data.fd == 0)
			{
				memset(tmpbuff, 0, sizeof(tmpbuff));
				gets(tmpbuff);
				printf("STDIN:%s\n", tmpbuff);
			}
			else if(retenv[i].data.fd == fd)
			{
				memset(tmpbuff, 0, sizeof(tmpbuff));
				read(fd, tmpbuff, sizeof(tmpbuff));
				printf("FIFO:%s\n", tmpbuff);
			}
		}
	}
	
	close(fd);
	return 0;
}

3. 函数接口:

    1. select:

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

        功能:select监听文件描述符集合中是否有文件描述编程ready状态

        参数:

            nfds:最大文件描述符的值+1

            reafds:读文件描述符集合

            writefds:写文件描述符集合

            exceptfds:其余文件描述符集合

            timeout:等待的时长

                        NULL:一直等待

        返回值:

            成功返回文件描述符集合中的文件描述符个数
            失败返回-1

    void FD_CLR(int fd, fd_set *set);

                功能:判断文件描述符fd是否仍在集合中

    void FD_SET(int fd, fd_set *set);

                功能:将文件描述符fd加入到集合中

    int  FD_ISSET(int fd, fd_set *set);

                功能:判断文件描述符fd是否仍在集合中

    void FD_ZERO(fd_set *set);

                功能:将文件描述符集合清0

    2. poll:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

        功能:监听文件描述符集合是否有事件发生

        参数:

            fds:监听文件描述符集合是否有时间发生

            nfds:监听文件描述符集合元素个数

            timeout:等待时间(-1一直等待)

struct pollfd {
    int   fd;         /* file descriptor */
    short events;     /* requested events */
    short revents;    /* returned events */
};

                fd:监听的文件描述

                events:要监听的事件,POLLIN:是否可读,POLLOUT:是否可写

                revents:实际产生的事件

    3. epoll:

        1. epoll_create:
int epoll_create(int size);

            功能:创建一张内核事件表

            参数:

                size:事件的个数

            返回值:

                成功返回文件描述符
                失败返回-1

        2. epoll_ctl:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

            功能:维护epoll时间表

            参数:

                epfd:事件表的文件描述符

                op:

                    EPOLL_CTL_ADD     添加事件

                    EPOLL_CTL_MODE  修改事件

                    EPOLL_CTL_DEL      删除事件

                fd:操作的文件描述符

                events:事件对应的事件

typedef union epoll_data {
    void    *ptr;
    in    fd;
    uint32_t    u32;
    uint64_t    u64;
} epoll_data_t;

struct epoll_event {
    uint32_t     events;      /* Epoll events */
    epoll_data_t data;        /* User data variable */
};

            返回值:

                成功返回0 
                失败返回-1 

        3. epoll_wait:
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

            功能:监听事件表中的事件

            参数:

                epfd:文件描述符

                events:存放实际产生事件的数组空间首地址

                maxevents:最多存放事件的个数

                timeout:设定监听的时间(超过该时间则不再监听)

                        -1:一直监听直到有事件发生

            返回值:

                成功返回产生事件的文件描述符个数
                失败返回-1 
                如果时间达到仍没有事件发生返回0

4. 练习作业:

        1. 编写TCP并发模型之select模型

client.c

#include "head.h"

int CreateTcpClient(char *pip, int port)
{
	int ret = 0;
	int sockfd = 0;
	struct sockaddr_in seraddr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to socket");
		return -1;
	}
	
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(port);
	seraddr.sin_addr.s_addr = inet_addr(pip);
	ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (-1 == ret)
	{
		perror("fail to connect");
		return -1;
	}
	
	return sockfd;
}

int main(void)
{
	int sockfd = 0;
	char tmpbuff[4096] = {"hello world"};
	int cnt = 0;
	ssize_t nsize = 0;

	sockfd = CreateTcpClient("192.168.1.125", 50000);
	
	while (1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		sprintf(tmpbuff, "hello world --- %d", cnt);
		cnt++;
		nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
		if (-1 == nsize)
		{
			perror("fail to send");
			return -1;
		}

		memset(tmpbuff, 0, sizeof(tmpbuff));
		nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
		if (-1 == nsize)
		{
			perror("fail to recv");
			return -1;
		}

		printf("RECV:%s\n", tmpbuff);
	}

	close(sockfd);

	return 0;
}

        后面的client.c都为上面所示

server.c

#include "head.h"

int CreateListenSocket(char *pip, int port)
{
	int ret = 0;
	int sockfd = 0;
	struct sockaddr_in serveraddr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("fail to socket");
		return -1;
	}
	
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(port);
	serveraddr.sin_addr.s_addr = inet_addr(pip);
	ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	if(ret == -1)
	{
		perror("fail to bind");
		return -1;
	}
		

	ret =listen(sockfd, 10);
	if(ret == -1)
	{
		perror("fail to listen");
		return -1;
	}

	return sockfd;
}

int HandleTcpClient(int confd)
{
	char tmpbuff[4096] = {0};
	ssize_t nsize = 0;

	memset(tmpbuff, 0, sizeof(tmpbuff));
	nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to recv");
		return -1;
	}
	else if(nsize == 0)
	{
		return 0;
	}

	sprintf(tmpbuff, "%s ------echo", tmpbuff);
	nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to send");
		return -1;
	}
	
	return nsize;

}

int main(void)
{
	int i = 0;
	int ret = 0;
	int confd = 0;
	int sockfd = 0;
	fd_set rdfds;
	fd_set tmpfds;
	int maxfd = 0;
	
	sockfd = CreateListenSocket("192.168.1.125", 50000);
	
	FD_ZERO(&rdfds);
	FD_SET(sockfd, &rdfds);
	maxfd = sockfd;

	while(1)
	{
		tmpfds = rdfds;
		ret = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
		if(ret == -1)
		{
			perror("fail to select");
			return -1;
		}

		if(FD_ISSET(sockfd, &tmpfds))
		{
			confd = accept(sockfd, NULL, NULL);
			if(confd == -1)
			{
				perror("fail to accept");
				FD_CLR(sockfd, &rdfds);
				close(sockfd);
				continue;
			}

			FD_SET(confd, &rdfds);
			maxfd = maxfd > confd ? maxfd : confd;
		}

		for(i = sockfd+1; i <= maxfd; i++)
		{
			if(FD_ISSET(i, &tmpfds))
			{
				ret = HandleTcpClient(i);
				if(ret == -1)
				{
					fprintf(stderr, "handle client fialed!\n");
					FD_CLR(i, &rdfds);
					close(i);
					continue;
				}
				else if(ret == 0)
				{
					fprintf(stderr, "client disconnected!\n");
					FD_CLR(i, &rdfds);
					close(i);
					continue;
				}
			}
		}
	}
	
	close(confd);
	close(sockfd);

	return 0;
}

        2. 编写TCP并发模型之poll模型

server.c

#include "head.h"

int CreateListenSocket(char *pip, int port)
{
	int ret = 0;
	int sockfd = 0;
	struct sockaddr_in serveraddr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("fail to socket");
		return -1;
	}
	
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(port);
	serveraddr.sin_addr.s_addr = inet_addr(pip);
	ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	if(ret == -1)
	{
		perror("fail to bind");
		return -1;
	}
		

	ret =listen(sockfd, 10);
	if(ret == -1)
	{
		perror("fail to listen");
		return -1;
	}

	return sockfd;
}

int HandleTcpClient(int confd)
{
	char tmpbuff[4096] = {0};
	ssize_t nsize = 0;

	memset(tmpbuff, 0, sizeof(tmpbuff));
	nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to recv");
		return -1;
	}
	else if(nsize == 0)
	{
		return 0;
	}

	sprintf(tmpbuff, "%s ------echo", tmpbuff);
	nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to send");
		return -1;
	}
	
	return nsize;

}

int InitFds(struct pollfd *fds, int maxlen)
{
	int i = 0;

	for(i = 0; i < maxlen; i++)
	{
		fds[i].fd = -1;
	}

	return 0;
}

int AddFds(struct pollfd *fds, int maxlen, int fd, short env)
{
	int i = 0;

	for(i = 0; i < maxlen; i++)
	{
		if(fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = env;
			break;
		}
	}

	if(i == maxlen)
	{
		return -1;
	}

	return 0;
}

int DeleteFds(struct pollfd *fds, int maxlen, int fd)
{
	int i = 0;
	for(i = 0; i < maxlen; i++)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			break;
		}
	}

	return 0;
}

int main(void)
{
	int i = 0;
	int ret = 0;
	int nready = 0;
	int confd = 0;
	int sockfd = 0;
	struct pollfd fds[1024];

	sockfd = CreateListenSocket("192.168.1.125", 50000);
	
	InitFds(fds, 1024);
	AddFds(fds, 1024, sockfd, POLLIN);

	while(1)
	{
		nready = poll(fds, 1024, -1);
		if(nready == -1)
		{
			perror("fail to poll");
			return -1;
		}
		
		for(i = 0; i < 1024; i++)
		{
			if(fds[i] == -1)
			{
				continue;
			}
			if(fds[i].revents & POLLIN && fds[i].fd == sockfd)
			{
				confd = accept(sockfd, NULL, NULL);
				if(confd == -1)
				{
					perror("fail to accept");
					close(sockfd);
					DeleteFds(fds, 1024, sockfd);
					continue;
				}
				
				AddFds(fds, 1024, confd, POLLIN);
			}
			else if(fds[i].revents & POLLIN && fds[i].fd != sockfd)
			{
				ret = HandleTcpClient(fds[i].fd);
				if(ret == -1)
				{
					fprintf(stderr, "handle tcp client failed!\n");
					close(fds[i].fd);
					DeleteFds(fds, 1024, fds[i].fd);
					continue;
				}
				else if(ret == 0)
				{
					fprintf(stderr, "client disconnected!\n");
					close(fds[i].fd);
					DeleteFds(fds, 1024, fds[i].fd);
					continue;
				}
			}
		}
	}
	
	close(confd);
	close(sockfd);

	return 0;
}

        3. 编写TCP并发模型之epoll模型

server.c

#include "head.h"

int CreateListenSocket(char *pip, int port)
{
	int ret = 0;
	int sockfd = 0;
	struct sockaddr_in serveraddr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("fail to socket");
		return -1;
	}
	
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(port);
	serveraddr.sin_addr.s_addr = inet_addr(pip);
	ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	if(ret == -1)
	{
		perror("fail to bind");
		return -1;
	}
		

	ret =listen(sockfd, 10);
	if(ret == -1)
	{
		perror("fail to listen");
		return -1;
	}

	return sockfd;
}

int HandleTcpClient(int confd)
{
	char tmpbuff[4096] = {0};
	ssize_t nsize = 0;

	memset(tmpbuff, 0, sizeof(tmpbuff));
	nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to recv");
		return -1;
	}
	else if(nsize == 0)
	{
		return 0;
	}

	sprintf(tmpbuff, "%s ------echo", tmpbuff);
	nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to send");
		return -1;
	}
	
	return nsize;

}

int main(void)
{
	int i = 0;
	int ret = 0;
	int nready = 0;
	int confd = 0;
	int sockfd = 0;
	struct epoll_event env;
	struct epoll_event retenv[1024];
	int epfd = 0;
	
	epfd = epoll_create(1024);
	if(epfd == -1)
	{
		perror("fail to epoll_create");
		return -1;
	}

	sockfd = CreateListenSocket("192.168.1.125", 50000);
	
	env.data.fd = sockfd;
	env.events = EPOLLIN;
	epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &env);

	while(1)
	{
		nready = epoll_wait(epfd, retenv, 1024, -1);
		if(nready == -1)
		{
			perror("fail to epoll");
			return -1;
		}
		
		for(i = 0; i < nready; i++)
		{
			if(retenv[i].data.fd == sockfd)
			{
				confd = accept(sockfd, NULL, NULL);
				if(confd == -1)
				{
					perror("fail to accept");
					close(sockfd);
					env.data.fd = sockfd;
					env.events = EPOLLIN;
					epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, &env);					
					continue;
				}
				env.data.fd = confd;
				env.events = EPOLLIN;
				epoll_ctl(epfd, EPOLL_CTL_ADD, confd, &env);
			}
			else if(retenv[i].data.fd != sockfd)
			{
				ret = HandleTcpClient(retenv[i].data.fd);
				if(ret == -1)
				{
					fprintf(stderr, "handle tcp client failed!\n");
					close(retenv[i].data.fd);
					env.data.fd = retenv[i].data.fd;
					env.events = EPOLLIN;
					epoll_ctl(epfd, EPOLL_CTL_DEL, retenv[i].data.fd, &env);
					continue;
				}
				else if(ret == 0)
				{
					fprintf(stderr, "client disconnected!\n");
					close(retenv[i].data.fd);
					env.data.fd = retenv[i].data.fd;
					env.events = EPOLLIN;
					epoll_ctl(epfd, EPOLL_CTL_DEL, retenv[i].data.fd, &env);
					continue;
				}
			}
		}
	}
	
	close(confd);
	close(sockfd);

	return 0;
}

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

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

相关文章

51单片机基础篇系列-LED灯点亮代码部分

&#x1f308;个人主页: 会编辑的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” #include<reg52.h> //包含单片机内部寄存器 void main() //&#xff08;&#xff09;{P10xfe;//1111 1110while(1); // } 上面是第一个 LED实验 #include<reg52.h>…

解码人工智能的幽默:理解其背后的误解与挑战

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

爬虫之矛---JavaScript基石篇5<JS混淆问题(1)>

前言: 随着现代JavaScript应用程序的复杂性增加,源代码的安全性成为开发者和企业关注的焦点之一。为了保护知识产权和防止代码被逆向工程,开发者采用了各种技术手段,其中一种重要的方法是混淆。 正文: 如何调试JS? 以chrome浏览器为例,在开发者工具里面,可以通过在source…

MacOS - 在 Mac 上自定义“访达”边栏(快捷方式)

将文件添加到边栏&#xff1a;按住 Command 键&#xff0c;然后将文件拖到“个人收藏”部分。如果没有看到“个人收藏”部分&#xff0c;请选取“访达” > “设置” > “边栏”&#xff0c;然后在“个人收藏”部分中选择至少一个项目。 将文件添加到“访达”边栏仅会创建…

WPF(2)命令绑定

效果是&#xff1a;当TextBox控件的Text属性为空时show按钮不可用&#xff0c;有值时show按钮可用 项目结构 界面代码 <Window x:Class"WpfApp1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://sc…

Qt - 信号和槽

目录 一、信号 二、槽 三、信号和槽的使用 (一) 连接信号和槽 (二) 自定义槽 (三) 通过 Qt Creator生成信号槽代码 (四) 自定义信号 四、带参数的信号和槽 五、信号与槽的断开 六、Qt4版本信号与槽的连接 (一) Qt4版本信号与槽连接的优缺点 一、信号 在 Qt 中&…

基于Python3的数据结构与算法 - 14 队列

目录 一、定义 1. 环形队列 2. 自定义队列 二、队列的内置模块 1. 双向队列 一、定义 队列&#xff08;Queue&#xff09;是一个数据集合&#xff0c;仅允许在列表的一端进行插入&#xff0c;另一端进行删除。进行插入的一端称为队尾&#xff08;rear&#xff09;&#…

前端基础篇-深入了解用 HTML 与 CSS 实现标题排版

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 HTML 与 CSS 概述 2.0 HTML - 标题排版 2.1 图片标签 2.2 标题标签 2.3 水平标签 2.4 实现标题排版 3.0 HTML - 标题样式(style 样式) 3.1 CSS 的引入方式 3.2…

2024最新版使用PyCharm搭建Anaconda

2024最新版使用PyCharm搭建Anaconda 因为pycharm自带的包不全&#xff0c;或者下载的时候比较慢&#xff0c;所以我们直接用anaconda的包&#xff0c;毕竟我们以后还会学到很多的包&#xff0c;不多说&#xff0c;直接开干&#xff01; 一、下载Pycharm、Anacoda pycharm中文网…

02_electron快速建立项目

一、安装 yarn 在此之前可以先安装 git&#xff1a;Git - Downloads (git-scm.com) 下面就是 yarn 安装的代码&#xff0c;在终端输入即可。 npm install --global yarn 检查是否安装成功&#xff1a; yarn --version 二、快速建立一个electron项目 其实在Getting Started - …

用chatgpt写论文重复率高吗?如何降低重复率?

ChatGPT写的论文重复率很低 ChatGPT写作是基于已有的语料库和文献进行训练的&#xff0c;因此在写作过程中会不可避免地引用或借鉴已有的研究成果和观点。同时&#xff0c;由于ChatGPT的表述方式和写作风格与人类存在一定的差异&#xff0c;也可能会导致论文与其他文章相似度高…

06多表查询

多表查询 多表查询&#xff0c;也称为关联查询&#xff0c;指两个或更多个表一起完成查询操作。前提条件&#xff1a;这些一起查询的表之间是有关系的&#xff08;一对一、一对多&#xff09;&#xff0c;它们之间一定是有关联字段&#xff0c;这个 关联字段可能建立了外键&am…

网络基础『 序列化与反序列化』

&#x1f52d;个人主页&#xff1a; 北 海 &#x1f6dc;所属专栏&#xff1a; Linux学习之旅、神奇的网络世界 &#x1f4bb;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f324;️前言&#x1f326;️正文1.协议的重要性2.什么是序列化与反序列化&…

安装配置Kafka

一个典型的Kafka集群中包含若干Producer&#xff08;可以是Web前端FET&#xff0c;或者是服务器日志等&#xff09;&#xff0c;若干Broker&#xff08;Kafka支持水平扩展&#xff0c;一般Broker数量越多&#xff0c;集群吞吐率越高&#xff09;&#xff0c;若干ConsumerGroup&…

wordpress免费主题下载

免费wordpress模板下载 简洁大气的文化艺术类wordpress模板&#xff0c;可以免费下载&#xff0c;实用易上手&#xff0c;新手也适合。 https://www.wpniu.com/themes/304.html 免费wordpress主题下载 高端大气上档次的wordpress主题&#xff0c;也可以是免费的&#xff0c;…

【机器学习】无监督学习算法之:层次聚类

层次聚类 1、引言2、层次聚类2.1 定义2.2 原理2.3 实现方式2.4 算法公式2.5 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 这周末过的滋润啊。 小鱼&#xff1a;… 每个周末都挺滋润的啊。 小屌丝&#xff1a;啊~ ~ 你这… 小鱼&#xff1a;周末加班&#xf…

Skywalking(9.7.0) 告警配置

图片被吞&#xff0c;来这里看吧&#xff1a;https://juejin.cn/post/7344567669893021736 过年前一天发版&#xff0c;大家高高兴兴准备回家过年去了。这时候老板说了一句&#xff0c;记得带上电脑&#xff0c;关注用户反馈。有紧急问题在高速上都得给我找个服务区改好。 但是…

矩阵乘法--Strassen算法

一、矩阵乘法 从中可以看出&#xff0c;计算两个矩阵的乘积&#xff0c;需要三个 for 循环&#xff0c;可以简单写出代码&#xff1a; for(int i1;i<m;i)for(int j1;j<p;j)for(int k1;k<n;k)c[i][j]a[i][k]*b[k][j]; 时间复杂度的分析&#xff1a;很明显&#xff0c;…

JDK环境变量配置-jre\bin、rt.jar、dt.jar、tools.jar

我们主要看下rt.jar、dt.jar、tools.jar的作用&#xff0c;rt.jar在​%JAVA_HOME%\jre\lib&#xff0c;dt.jar和tools.jar在%JAVA_HOME%\lib下。 rt.jar&#xff1a;Java基础类库&#xff0c;也就是Java doc里面看到的所有的类的class文件。 tools.jar&#xff1a;是系统用来编…

网络通信(一)

网络编程概述 可以让设备中的程序与网络上其他设备中的程序进行数据交互&#xff08;实现网络通信的&#xff09;。 Java提供了哪些网络编程的解决方案 java.net.*包下提供了网络编程的解决方案 基本的通信架构 基本的通信架构有2种形式&#xff1a;CS架构&#xff08;Clie…
最新文章