linux网络编程(四)多路I/O转接服务器

news2025/7/12 7:32:36

文章目录

  • 1.多路I/O转接服务器
  • 2.select 方式的多路I/O转接服务器
  • 3.poll 方式的多路I/O转接服务器
  • 4.epoll 方式的多路I/O转接服务器

1.多路I/O转接服务器

多路IO转接服务器也叫做多任务IO服务器。该类服务器实现的主旨思想是,不再由应用程序自己监视客户端连接,取而代之由内核替应用程序监视文件。

主要使用的方法有三种

2.select 方式的多路I/O转接服务器

1.select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开的文件描述符个数并不能改变select监听文件个数

2.解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力。

#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
			fd_set *exceptfds, struct timeval *timeout);

	nfds: 		监控的文件描述符集里最大文件描述符加1,因为此参数会告诉内核检测前多少个文件描述符的状态
	readfds:	监控有读数据到达文件描述符集合,传入传出参数
	writefds:	监控写数据到达文件描述符集合,传入传出参数
	exceptfds:	监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数
	timeout:	定时阻塞监控时间,3种情况
				1.NULL,永远等下去
				2.设置timeval,等待固定时间
				3.设置timeval里时间均为0,检查描述字后立即返回,轮询
	struct timeval {
		long tv_sec; /* seconds */
		long tv_usec; /* microseconds */
	};
	void FD_CLR(int fd, fd_set *set); 	//把文件描述符集合里fd位清0
	int FD_ISSET(int fd, fd_set *set); 	//测试文件描述符集合里fd是否置1
	void FD_SET(int fd, fd_set *set); 	//把文件描述符集合里fd位置1
	void FD_ZERO(fd_set *set); 			//把文件描述符集合里所有位清0
server
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666

int main(int argc, char *argv[])
{
	int i, maxi, maxfd, listenfd, connfd, sockfd;
	int nready, client[FD_SETSIZE]; 	/* FD_SETSIZE 默认为 1024 */
	ssize_t n;
	fd_set rset, allset;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN]; 			/* #define INET_ADDRSTRLEN 16 */
	socklen_t cliaddr_len;
	struct sockaddr_in cliaddr, servaddr;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);

Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

Listen(listenfd, 20); 		/* 默认最大128 */

maxfd = listenfd; 			/* 初始化 */
maxi = -1;					/* client[]的下标 */

for (i = 0; i < FD_SETSIZE; i++)
	client[i] = -1; 		/* 用-1初始化client[] */

FD_ZERO(&allset);
FD_SET(listenfd, &allset); /* 构造select监控文件描述符集 */

for ( ; ; ) {
	rset = allset; 			/* 每次循环时都从新设置select监控信号集 */
	nready = select(maxfd+1, &rset, NULL, NULL, NULL);

	if (nready < 0)
		perr_exit("select error");
	if (FD_ISSET(listenfd, &rset)) { /* new client connection */
		cliaddr_len = sizeof(cliaddr);
		connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
		printf("received from %s at PORT %d\n",
				inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
				ntohs(cliaddr.sin_port));
		for (i = 0; i < FD_SETSIZE; i++) {
			if (client[i] < 0) {
				client[i] = connfd; /* 保存accept返回的文件描述符到client[]里 */
				break;
			}
		}
		/* 达到select能监控的文件个数上限 1024 */
		if (i == FD_SETSIZE) {
			fputs("too many clients\n", stderr);
			exit(1);
		}

		FD_SET(connfd, &allset); 	/* 添加一个新的文件描述符到监控信号集里 */
		if (connfd > maxfd)
			maxfd = connfd; 		/* select第一个参数需要 */
		if (i > maxi)
			maxi = i; 				/* 更新client[]最大下标值 */

		if (--nready == 0)
			continue; 				/* 如果没有更多的就绪文件描述符继续回到上面select阻塞监听,
										负责处理未处理完的就绪文件描述符 */
		}
		for (i = 0; i <= maxi; i++) { 	/* 检测哪个clients 有数据就绪 */
			if ( (sockfd = client[i]) < 0)
				continue;
			if (FD_ISSET(sockfd, &rset)) {
				if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
					Close(sockfd);		/* 当client关闭链接时,服务器端也关闭对应链接 */
					FD_CLR(sockfd, &allset); /* 解除select监控此文件描述符 */
					client[i] = -1;
				} else {
					int j;
					for (j = 0; j < n; j++)
						buf[j] = toupper(buf[j]);
					Write(sockfd, buf, n);
				}
				if (--nready == 0)
					break;
			}
		}
	}
	close(listenfd);
	return 0;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666

int main(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, n;

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	servaddr.sin_port = htons(SERV_PORT);

	Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	while (fgets(buf, MAXLINE, stdin) != NULL) {
		Write(sockfd, buf, strlen(buf));
		n = Read(sockfd, buf, MAXLINE);
		if (n == 0)
			printf("the other side has been closed.\n");
		else
			Write(STDOUT_FILENO, buf, n);
	}
	Close(sockfd);
	return 0;
}
pselect
pselect原型如下。此模型应用较少,有需要的同学可参考select模型自行编写C/S
#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
			fd_set *exceptfds, const struct timespec *timeout,
			const sigset_t *sigmask);
	struct timespec {
		long tv_sec; /* seconds */
		long tv_nsec; /* nanoseconds */
	};
	用sigmask替代当前进程的阻塞信号集,调用返回后还原原有阻塞信号集

3.poll 方式的多路I/O转接服务器

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
	struct pollfd {
		int fd; /* 文件描述符 */
		short events; /* 监控的事件 */
		short revents; /* 监控事件中满足条件返回的事件 */
	};
	POLLIN			普通或带外优先数据可读,即POLLRDNORM | POLLRDBAND
	POLLRDNORM		数据可读
	POLLRDBAND		优先级带数据可读
	POLLPRI 		高优先级可读数据
	POLLOUT		普通或带外数据可写
	POLLWRNORM		数据可写
	POLLWRBAND		优先级带数据可写
	POLLERR 		发生错误
	POLLHUP 		发生挂起
	POLLNVAL 		描述字不是一个打开的文件

	nfds 			监控数组中有多少文件描述符需要被监控

	timeout 		毫秒级等待
		-1:阻塞等,#define INFTIM -1 			Linux中没有定义此宏
		0:立即返回,不阻塞进程
		>0:等待指定毫秒数,如当前系统时间精度不够毫秒,向上取值

如果不再监控某个文件描述符时,可以把pollfd中,fd设置为-1,poll不再监控此pollfd,下次返回时,把revents设置为0。
相较于select而言,poll的优势:

  1. 传入、传出事件分离。无需每次调用时,重新设定监听事件。
  2. 文件描述符上限,可突破1024限制。能监控的最大上限数可使用配置文件调整。
server
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666
#define OPEN_MAX 1024

int main(int argc, char *argv[])
{
	int i, j, maxi, listenfd, connfd, sockfd;
	int nready;
	ssize_t n;
	char buf[MAXLINE], str[INET_ADDRSTRLEN];
	socklen_t clilen;
	struct pollfd client[OPEN_MAX];
	struct sockaddr_in cliaddr, servaddr;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	Listen(listenfd, 20);

	client[0].fd = listenfd;
	client[0].events = POLLIN; 					/* listenfd监听普通读事件 */

	for (i = 1; i < OPEN_MAX; i++)
		client[i].fd = -1; 							/* 用-1初始化client[]里剩下元素 */
	maxi = 0; 										/* client[]数组有效元素中最大元素下标 */

	for ( ; ; ) {
		nready = poll(client, maxi+1, -1); 			/* 阻塞 */
		if (client[0].revents & POLLIN) { 		/* 有客户端链接请求 */
			clilen = sizeof(cliaddr);
			connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
			printf("received from %s at PORT %d\n",
					inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
					ntohs(cliaddr.sin_port));
			for (i = 1; i < OPEN_MAX; i++) {
				if (client[i].fd < 0) {
					client[i].fd = connfd; 	/* 找到client[]中空闲的位置,存放accept返回的connfd */
					break;
				}
			}

			if (i == OPEN_MAX)
				perr_exit("too many clients");

			client[i].events = POLLIN; 		/* 设置刚刚返回的connfd,监控读事件 */
			if (i > maxi)
				maxi = i; 						/* 更新client[]中最大元素下标 */
			if (--nready <= 0)
				continue; 						/* 没有更多就绪事件时,继续回到poll阻塞 */
		}
		for (i = 1; i <= maxi; i++) { 			/* 检测client[] */
			if ((sockfd = client[i].fd) < 0)
				continue;
			if (client[i].revents & POLLIN) {
				if ((n = Read(sockfd, buf, MAXLINE)) < 0) {
					if (errno == ECONNRESET) { /* 当收到 RST标志时 */
						/* connection reset by client */
						printf("client[%d] aborted connection\n", i);
						Close(sockfd);
						client[i].fd = -1;
					} else {
						perr_exit("read error");
					}
				} else if (n == 0) {
					/* connection closed by client */
					printf("client[%d] closed connection\n", i);
					Close(sockfd);
					client[i].fd = -1;
				} else {
					for (j = 0; j < n; j++)
						buf[j] = toupper(buf[j]);
						Writen(sockfd, buf, n);
				}
				if (--nready <= 0)
					break; 				/* no more readable descriptors */
			}
		}
	}
	return 0;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666

int main(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, n;

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	servaddr.sin_port = htons(SERV_PORT);

	Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	while (fgets(buf, MAXLINE, stdin) != NULL) {
		Write(sockfd, buf, strlen(buf));
		n = Read(sockfd, buf, MAXLINE);
		if (n == 0)
			printf("the other side has been closed.\n");
		else
			Write(STDOUT_FILENO, buf, n);
	}
	Close(sockfd);
	return 0;
}
ppoll
GNU定义了ppoll(非POSIX标准),可以支持设置信号屏蔽字,大家可参考poll模型自行实现C/S。

#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <poll.h>
int ppoll(struct pollfd *fds, nfds_t nfds,
		   const struct timespec *timeout_ts, const sigset_t *sigmask);

4.epoll 方式的多路I/O转接服务器

epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
目前epoll是linux大规模并发网络程序中的热门首选模型。 epoll除了提供select/poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge
Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
可以使用cat命令查看一个进程可以打开的socket描述符上限。

cat /proc/sys/fs/file-max

如有需要,可以通过修改配置文件的方式修改该上限值。

sudo vi /etc/security/limits.conf

在文件尾部写入以下配置,soft软限制,hard硬限制。如下图所示。

* soft nofile 65536
* hard nofile 100000

在这里插入图片描述1.创建一个epoll句柄,参数size用来告诉

内核监听的文件描述符的个数,跟内存大小有关。

#include <sys/epoll.h>
int epoll_create(int size)		
	size:监听数目(内核参考值)
	返回值:成功:非负文件描述符;失败:-1,设置相应的errno

基础API
1.控制某个epoll监控的文件描述符上的事件:注册、修改、删除。

	#include <sys/epoll.h>
	int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
		epfd:	为epoll_creat的句柄
		op:		表示动作,用3个宏来表示:
			EPOLL_CTL_ADD (注册新的fd到epfd)EPOLL_CTL_MOD (修改已经注册的fd的监听事件)EPOLL_CTL_DEL (从epfd删除一个fd);
		event:	告诉内核需要监听的事件

		struct epoll_event {
			__uint32_t events; /* Epoll events */
			epoll_data_t data; /* User data variable */
		};
		typedef union epoll_data {
			void *ptr;
			int fd;
			uint32_t u32;
			uint64_t u64;
		} epoll_data_t;

		EPOLLIN :	表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
		EPOLLOUT:	表示对应的文件描述符可以写
		EPOLLPRI:	表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
		EPOLLERR:	表示对应的文件描述符发生错误
		EPOLLHUP:	表示对应的文件描述符被挂断;
		EPOLLET: 	将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)而言的
		EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
		返回值:成功:0;失败:-1,设置相应的errno

1.等待所监控文件描述符上有事件的产生,类似于select()调用。

	#include <sys/epoll.h>
	int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
		events:		用来存内核得到事件的集合,可简单看作数组。
		maxevents:	告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,
		timeout:	是超时时间
			-1:	阻塞
			0:	立即返回,非阻塞
			>0:	指定毫秒
		返回值:	成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1
server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666
#define OPEN_MAX 1024

int main(int argc, char *argv[])
{
	int i, j, maxi, listenfd, connfd, sockfd;
	int nready, efd, res;
	ssize_t n;
	char buf[MAXLINE], str[INET_ADDRSTRLEN];
	socklen_t clilen;
	int client[OPEN_MAX];
	struct sockaddr_in cliaddr, servaddr;
	struct epoll_event tep, ep[OPEN_MAX];

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	Bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

	Listen(listenfd, 20);

	for (i = 0; i < OPEN_MAX; i++)
		client[i] = -1;
	maxi = -1;

	efd = epoll_create(OPEN_MAX);
	if (efd == -1)
		perr_exit("epoll_create");

	tep.events = EPOLLIN; tep.data.fd = listenfd;

	res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);
	if (res == -1)
		perr_exit("epoll_ctl");

	while (1) {
		nready = epoll_wait(efd, ep, OPEN_MAX, -1); /* 阻塞监听 */
		if (nready == -1)
			perr_exit("epoll_wait");

		for (i = 0; i < nready; i++) {
			if (!(ep[i].events & EPOLLIN))
				continue;
			if (ep[i].data.fd == listenfd) {
				clilen = sizeof(cliaddr);
				connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
				printf("received from %s at PORT %d\n", 
						inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 
						ntohs(cliaddr.sin_port));
				for (j = 0; j < OPEN_MAX; j++) {
					if (client[j] < 0) {
						client[j] = connfd; /* save descriptor */
						break;
					}
				}

				if (j == OPEN_MAX)
					perr_exit("too many clients");
				if (j > maxi)
					maxi = j; 		/* max index in client[] array */

				tep.events = EPOLLIN; 
				tep.data.fd = connfd;
				res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);
				if (res == -1)
					perr_exit("epoll_ctl");
			} else {
				sockfd = ep[i].data.fd;
				n = Read(sockfd, buf, MAXLINE);
				if (n == 0) {
					for (j = 0; j <= maxi; j++) {
						if (client[j] == sockfd) {
							client[j] = -1;
							break;
						}
					}
					res = epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
					if (res == -1)
						perr_exit("epoll_ctl");

					Close(sockfd);
					printf("client[%d] closed connection\n", j);
				} else {
					for (j = 0; j < n; j++)
						buf[j] = toupper(buf[j]);
					Writen(sockfd, buf, n);
				}
			}
		}
	}
	close(listenfd);
	close(efd);
	return 0;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 6666

int main(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, n;

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	servaddr.sin_port = htons(SERV_PORT);

	Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	while (fgets(buf, MAXLINE, stdin) != NULL) {
		Write(sockfd, buf, strlen(buf));
		n = Read(sockfd, buf, MAXLINE);
		if (n == 0)
			printf("the other side has been closed.\n");
		else
			Write(STDOUT_FILENO, buf, n);
	}

	Close(sockfd);
	return 0;
}

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

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

相关文章

利用stream实现行政区域列表转tree树形结构

一、数据结构 CREATE TABLE t_districts (adcode bigint NOT NULL COMMENT 主键(区域编码)\r\n,pid varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 父级区域编码,name varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci D…

MySQL的Redo log 、Undo log、 Binlog

MySQL的redo log 、undo log、 binlog redo log概念 redo log翻译过来叫重做日志&#xff0c;是一种保证持久化的措施&#xff0c;innodb存储引擎的物理日志文件 redo log是固定大小的&#xff0c;是循环写的过程 有了redo log之后&#xff0c;innodb就可以保证即使数据库发…

数据存储介绍

数据存储对象包括数据流在加工过程中产生的临时文件或加工过程中需要查找的信息。数据以某种格式记录在计算机内部或外部存储介质上。数据存储要命名&#xff0c;这种命名要反映信息特征的组成含义。数据流反映了系统中流动的数据&#xff0c;表现出动态数据的特征&#xff1b;…

STM32个人笔记-电源管理

笔记来源于STM32F103VET6&#xff0c;野火指南者&#xff0c;中文参考手册&#xff0c;HAL库开发手册和b站的野火指南者视频。观看过好多次了&#xff0c;但往往理解得不够全面&#xff0c;现记下小笔记&#xff0c;用来回顾。属于个人笔记。 电源监控器 STM32芯片主要通过VDD…

代码审计基础之SQL注入漏洞

1.SQL注入原理 SQL注入就是攻击者通过把恶意的SQL语句插入到Web表单的输入页面中&#xff0c;且插入的恶意语句会导致原有的SQL语句发生改变&#xff0c;从而达到攻击者的目的去让它执行一些危险的数据操作&#xff0c;进一步欺骗服务器去执行一些非本意的操作。 简单来讲&am…

Python BeautifulSoup4 入门使用

一、简介 BeautifulSoup4 与 lxml 一样&#xff0c;是一个 html 解析器&#xff0c;主要功能也是解析和提取数据。 BeautifulSoup4 是 爬虫 必学的技能。BeautifulSoup 最主要的功能是从网页抓取数据&#xff0c;Beautiful Soup 自动将输入文档转换为 Unicode 编码&#xff0c…

Verilog语言中case、casex、casez的用法和区别

casez与casex语句是case语句的两种变体, 在写testbench时用到。case 语句是一种多路条件分支的形式&#xff0c;可以解决 if 语句中有多个条件选项时使用不方便的问题。 一、case、casex、casez的区别 下表给出case、casex、casez的真值表&#xff1a; 1&#xff09;在case语…

【计算机网络实验】防火墙访问控制列表实验

实验内容 防火墙访问控制列表实验 实验目的 理解访问控制列表的工作原理&#xff1b;了解访问控制列表的类型&#xff1b;学习标准访问控制列表的配置。 实验要求 1 实验拓扑图 本实验所用的网络拓扑如图1所示。 图1 ACL实验拓扑结构 2 实验步骤 Router0配置&#xff1b;&…

解决 npm install express 遇到的问题总结

方法1&#xff1a;权限 以管理员身份运行cmd执行npm install express --save命令 方法2&#xff1a;切换镜像源 查看镜像源 npm config get registry 如果要直接更换淘宝&#xff1a;npm config set registry https://registry.npmmirror.com/ 使用nrm切换 1.安装nrm npm i …

106362-34-9,(D-Ala1)-Peptide T amide

肽t的有效类似物DAPTA (aSTTTNYT-amide)在单核/巨噬细胞中显示出很强的抗hiv - 1活性&#xff0c;该肽抑制病毒的进入。 编号: 110545中文名称: 肽T、(D-Ala1)-Peptide T amide英文名: (D-Ala1)-Peptide T amideCAS号: 106362-34-9单字母: H2N-DAla-STTTNYT-NH2三字母: H2N-DAl…

设计模式 — 抽象工厂模式

抽象工厂模式女娲的失误实例 一实例 二抽象工厂模式的应用抽象工厂模式的优点抽象工厂模式的缺点抽象工厂模式的使用场景抽象工厂模式的注意事项女娲的失误 女娲造人的故事。人是造出来了&#xff0c;世界也热闹了&#xff0c;可是低头一看&#xff0c;都是清一色的类型&#…

Spark框架概述

Spark 框架概述 1.1. Spark是什么 定义&#xff1a;Apache Spark是用于大规模数据处理的统一分析引擎。 弹性分布式数据集RDD是一种分布式内存抽象&#xff0c;其使得程序员能够在大规模集群中做内存运算&#xff0c;并且有一定的容错方式。而这也是整个Spark的核心数据结构…

体验静态代码块

定义 public class Game {// 静态代码块static {System.out.println("static...run...");}// 构造方法public Game() {System.out.println("game...construct...");} }使用 结论 静态代码块在类被首次加载的时候触发启动

效能优化实践:C/C++单元测试万能插桩工具

研发效能是一个涉及面很广的话题&#xff0c;它涵盖了软件交付的整个生命周期&#xff0c;涉及产品、架构、开发、测试、运维&#xff0c;每个环节都可能影响顺畅、高质量地持续有效交付。在腾讯安全平台部实际研发与测试工作中我们发现&#xff0c;代码插桩隔离是单元测试工作…

theos tweak导入自定义类

有时&#xff0c;我们使用tweak的时候需要用到自定义的类&#xff0c;那么怎么引用呢&#xff1f; 假设我们有一个自定义类&#xff0c;people.h/people.m 那么分两种情况&#xff1a; 情况一&#xff0c;直接使用官方的tweak工程&#xff1a; 目录结构一般如下&#xff1a; …

[第九篇]——Docker 镜像使用

Docker 镜像使用 当运行容器时&#xff0c;使用的镜像如果在本地中不存在&#xff0c;docker 就会自动从 docker 镜像仓库中下载&#xff0c;默认是从 Docker Hub 公共镜像源下载。 下面我们来学习&#xff1a; 1、管理和使用本地 Docker 主机镜像2、创建镜像列出镜像列表 …

蛋白纯化-实验设计

小 M 不怕纯化“难”&#xff0c;IP、WB 只等闲。泡了两年实验室的小 M&#xff0c;理论与实操经验共有&#xff0c;且看我如何闯过蛋白纯化的几道“关”。 第一关 产品选择 小 M 敲黑板&#xff1a;此关最基础也最重要&#xff0c;谨防“一步错&#xff0c;步步错”。 亲和层析…

jenkins+junit4+allure+selenium实现自动化测试与结果可视化

安装包 jenkins.war jdk-8u332-linux-x64.tar.gz https://repo1.maven.org/maven2/io/qameta/allure/allure-commandline/2.17.2/ allure-commandline-2.17.2.zip https://chromedriver.storage.googleapis.com/index.html chromedriver 安装JDK 解压 tar xvf…

优盘数据恢复如何操作?恢复U盘数据的三个简单方法

对于我们用户来说&#xff0c;经常使用U盘来存储一些重要的文件是很常见的事。很多用户在使用的时候&#xff0c;经常因为操作不规范&#xff0c;而造成一些数据丢失。那么我们该如何做呢&#xff1f;优盘数据恢复如何操作&#xff1f;今天小编就来为大家分享一下关于如何将U盘…

基于梯度的图像边缘检测

参考视频&#xff1a;https://www.bing.com/videos/search?qacomputationalapproachtoedgedetection&docid608014236869751913&mid8C04384FFDD6A47533238C04384FFDD6A4753323&viewdetail&FORMVIRE 参考文献&#xff1a;A Computational Approach to Edge Dete…