150 Linux 网络编程6 ,从socket 到 epoll整理。listen函数参数再研究

一 . 只能被一个client 链接 socket例子

此例子用于socket 例子,
该例子只能用于一个客户端连接server。
不能用于多个client 连接  server

socket_server_support_one_clientconnect.c


/*
此例子用于socket 例子,
该例子只能用于一个客户端连接server。
不能用于多个client 连接  server


*/
#include "wrap.h"
#define SERVER_PORT 8888

int main(int argc, const char* argv[]) {

	int lfd;
	// 第一步:创建 lfd
	lfd = Socket(AF_INET, SOCK_STREAM, 0);


	// 第二步: 给lfd 绑定 IP 和 PORT,这里需要将 port 和IP 从小端转成大端
	struct sockaddr_in sockaddrin_server;
	sockaddrin_server.sin_family = AF_INET;
	sockaddrin_server.sin_port = htons(SERVER_PORT);
	sockaddrin_server.sin_addr.s_addr = htonl(INADDR_ANY);

	Bind(lfd,(struct sockaddr *)&sockaddrin_server,sizeof(sockaddrin_server));

	//第三步:设定监听上限,这个监听上限的意思是:同时只能有8个客户端和此 server连接,我们这个server只能要一个客户端和我们连接,因此这个值在这里没有具体意义
	Listen(lfd, 8);

	// 第四步 :阻塞,并等待客户端连接. accept的第二个参数是传入传出参数,代表的是:成功于服务器连接的客户端的 地址结构
	struct sockaddr_in sockaddrin_client;
	socklen_t sockaddrin_clientsocklen_t = sizeof(sockaddrin_client);
	int  cfd = Accept(lfd, (struct sockaddr *)&sockaddrin_client, &sockaddrin_clientsocklen_t);
	char clientip[33] = { 0 };
	const char* clientipresult = inet_ntop(AF_INET, &sockaddrin_client.sin_addr.s_addr, clientip, 32);

	if (clientipresult == NULL) {
		printf("inet_ntop error\n");
	}
	else {
		printf("client addr port = %d,addr ip = %s \n", ntohs(sockaddrin_client.sin_port), clientipresult);
	}
	printf("client addr port = %d,addr ip = %s \n",ntohs(sockaddrin_client.sin_port), clientip);

	//只要客户端连接上了, 没有错误,就会走到这一步。

	//第五步:连接上后,server端就可以循环的 使用read函数 读取客户端发送过来的数据了。
	//注意的是,read函数默认是阻塞读取的
	char readbuffer[256] = { 0 };
	int readbufferlen = 0;
	
	while (1) {
		readbufferlen = Read(cfd, readbuffer, 256);
		printf("read buffer from client %s \n", readbuffer);
		for (int i = 0; i < readbufferlen; ++i) {
			readbuffer[i] = toupper(readbuffer[i]);
		}
		Write(cfd, readbuffer, readbufferlen);
		Write(STDOUT_FILENO, readbuffer, readbufferlen);
	}
}

wrap.h

#ifndef __WRAP_H_
#define __WRAP_H_

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <netinet/ip.h> 
#include <strings.h>


void perr_exit(const char* s);
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);
int Close(int fd);
ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
static ssize_t my_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);
int tcp4bind(short port, const char* IP);

#endif

wrap.c

#include "wrap.h"

void perr_exit(const char* s)
{
	perror(s);
	exit(-1);
}

int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
	int n;

again:
	if ((n = accept(fd, sa, salenptr)) < 0) {
		if ((errno == ECONNABORTED) || (errno == EINTR))
			goto again;
		else
			perr_exit("accept error");
	}
	return n;
}

int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
	int n;

	if ((n = bind(fd, sa, salen)) < 0)
		perr_exit("bind error");

	return n;
}

int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{
	int n;

	if ((n = connect(fd, sa, salen)) < 0)
		perr_exit("connect error");

	return n;
}

int Listen(int fd, int backlog)
{
	int n;

	if ((n = listen(fd, backlog)) < 0)
		perr_exit("listen error");

	return n;
}

int Socket(int family, int type, int protocol)
{
	int n;

	if ((n = socket(family, type, protocol)) < 0)
		perr_exit("socket error");

	return n;
}

ssize_t Read(int fd, void* ptr, size_t nbytes)
{
	ssize_t n;

again:
	if ((n = read(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

ssize_t Write(int fd, const void* ptr, size_t nbytes)
{
	ssize_t n;

again:
	if ((n = write(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

int Close(int fd)
{
	int n;
	if ((n = close(fd)) == -1)
		perr_exit("close error");

	return n;
}

/*参三: 应该读取的字节数*/
ssize_t Readn(int fd, void* vptr, size_t n)
{
	size_t  nleft;              //usigned int 剩余未读取的字节数
	ssize_t nread;              //int 实际读到的字节数
	char* ptr;

	ptr = vptr;
	nleft = n;

	while (nleft > 0) {
		if ((nread = read(fd, ptr, nleft)) < 0) {
			if (errno == EINTR)
				nread = 0;
			else
				return -1;
		}
		else if (nread == 0)
			break;

		nleft -= nread;
		ptr += nread;
	}
	return n - nleft;
}

ssize_t Writen(int fd, const void* vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	const char* ptr;

	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ((nwritten = write(fd, ptr, nleft)) <= 0) {
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;
			else
				return -1;
		}

		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}

static ssize_t my_read(int fd, char* ptr)
{
	static int read_cnt;
	static char* read_ptr;
	static char read_buf[100];

	if (read_cnt <= 0) {
	again:
		if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
			if (errno == EINTR)
				goto again;
			return -1;
		}
		else if (read_cnt == 0)
			return 0;
		read_ptr = read_buf;
	}
	read_cnt--;
	*ptr = *read_ptr++;

	return 1;
}

ssize_t Readline(int fd, void* vptr, size_t maxlen)
{
	ssize_t n, rc;
	char    c, * ptr;

	ptr = vptr;
	for (n = 1; n < maxlen; n++) {
		if ((rc = my_read(fd, &c)) == 1) {
			*ptr++ = c;
			if (c == '\n')
				break;
		}
		else if (rc == 0) {
			*ptr = 0;
			return n - 1;
		}
		else
			return -1;
	}
	*ptr = 0;

	return n;
}

int tcp4bind(short port, const char* IP)
{
	struct sockaddr_in serv_addr;
	int lfd = Socket(AF_INET, SOCK_STREAM, 0);
	bzero(&serv_addr, sizeof(serv_addr));
	if (IP == NULL) {
		//如果这样使用 0.0.0.0,任意ip将可以连接
		serv_addr.sin_addr.s_addr = INADDR_ANY;
	}
	else {
		if (inet_pton(AF_INET, IP, &serv_addr.sin_addr.s_addr) <= 0) {
			perror(IP);//转换失败
			exit(1);
		}
	}
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);

	//端口复用代码,在bind之前
	int opt = 1;
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	return lfd;
}

编译命令

gcc socket_server_support_one_clientconnect.c wrap.c -o socket_server_support_one_clientconnect.out
这里使用的是 gcc build, 如果使用g++,则会有build error,因为g++的语法检查,要比 gcc 严格一些。
error 信息如下



 g++ socket_server_support_one_clientconnect.c wrap.c -o socket_server_support_one_clientconnect.out
wrap.c: In function ‘ssize_t Readn(int, void*, size_t)’:
wrap.c:111:8: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
  ptr = vptr;
        ^~~~
wrap.c: In function ‘ssize_t Writen(int, const void*, size_t)’:
wrap.c:136:8: error: invalid conversion from ‘const void*’ to ‘const char*’ [-fpermissive]
  ptr = vptr;
        ^~~~
wrap.c: In function ‘ssize_t Readline(int, void*, size_t)’:
wrap.c:180:8: error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
  ptr = vptr;

二 . 可以被多个 client 链接 socket 例子,使用 父子进程完成。

2.1 添加 将 SIGCHLD 信号屏蔽

    //信号屏蔽是指进程主动阻止某些信号的递达,直到屏蔽解除,被屏蔽的信号在屏蔽期间仍然会产生,但不会被处理,直到屏蔽解除后才会被处理,信号屏蔽通常用于保护临界区代码,防止信号处理程序与普通代码同时执行导致的数据不一致问题。
 

            sigset_t set;
            sigemptyset(&set);
            sigaddset(&set, SIGCHLD);
            sigprocmask(SIG_BLOCK, &set, NULL);

2.2 端口复用

        server IP 和 port可以复用

        setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 

2.3 父进程完成的任务有:

        2.3.1.负责监听lfd功能.因此先要关闭 cfd

        2.3.2 父进程还需要回收子进程,以避免子进程完成后变成僵尸进程

                sigaction 在windows 上使用vs2019 编写linux代码,即使添加了#include <signal.h>,也无法识别,原因未知,但是在 linux环境下 build 是可以pass 的。

                            struct sigaction act;
                            act.sa_handler = catchchild;
                            sigemptyset(&act.sa_mask);
                            act.sa_flags = 0;
                            sigaction(SIGCHLD, &act, NULL);

        2.3.3 屏蔽解除 sigprocmask(SIG_UNBLOCK, &set, NULL);

2.4 子进程完成的任务有:

        2.4.1 子进程,负责 cfd 和 client的交换数据发送信息功能。因此先要关闭 lfd

                close(lfd);

        2.4.2 连接上后,循环的 使用read函数 读取客户端发送过来的数据了。注意的是,read函数默认是阻塞读取的
 

            char readbuffer[256] = { 0 };
            int readbufferlen = 0;

            while (1) {
                readbufferlen = Read(cfd, readbuffer, 256);
                printf("read buffer from client %s \n", readbuffer);
                for (int i = 0; i < readbufferlen; ++i) {
                    readbuffer[i] = toupper(readbuffer[i]);
                }
                Write(cfd, readbuffer, readbufferlen);
                Write(STDOUT_FILENO, readbuffer, readbufferlen);
            }

2.5 完整代码:

socket_server_support_more_clientconnect_use_fork.c


/*
此例子用于socket 例子,
该例子只能用于多个客户端连接server。

基于 父子进程 实现
父进程 用于 lfd ,
子进程 用于 cfd.

测试的问题1: listen(lfd,num) 监听的上限数量问题。




*/
#include "wrap.h"
#define SERVER_PORT 8888

void catchchild(int single) {
	pid_t pid;
	int status;
	while (1) {
		pid = waitpid(-1, &status, WNOHANG);
		if (pid <=0 ) {

			break;
		}
		else if (pid > 0) {
			printf("child pid = %\d exit \n",pid);
			if (WIFEXITED(status)) {//WIFEXITED(status)为真,说明是子线程是正常结束的。。使用WEXITSTATUS(status)  ---  获取进程退出状态 (exit的参数)
				printf("child normal die ,die information : %d \n", WEXITSTATUS(status));
			}
			if (WIFSIGNALED(status)) {//WIFSIGNALED(status)为真,说明子线程异常终止。。使用WTERMSIG(status) --- 取得使进程终止的那个信号的编号
				printf("child not normal die ,die information : %d \n", WTERMSIG(status));
			}
		}
	}
}

int main(int argc, const char* argv[]) {

	//第零步:将 SIGCHLD 信号屏蔽,这样做的原因是,有可能当父进程还没有 注册 完成 SIGCHID 的时候,子进程已经执行完毕了,因此在main 函数的最前面,将信号屏蔽了
	//信号屏蔽是指进程主动阻止某些信号的递达,直到屏蔽解除,被屏蔽的信号在屏蔽期间仍然会产生,但不会被处理,直到屏蔽解除后才会被处理,信号屏蔽通常用于保护临界区代码,防止信号处理程序与普通代码同时执行导致的数据不一致问题。
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set, SIGCHLD);
	sigprocmask(SIG_BLOCK, &set, NULL);


	int lfd;
	// 第一步:创建 lfd
	lfd = Socket(AF_INET, SOCK_STREAM, 0);

	// 第一步 让 server IP 和 port可以复用。
	int opt = 1;
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));


	// 第二步: 给lfd 绑定 IP 和 PORT,这里需要将 port 和IP 从小端转成大端
	struct sockaddr_in sockaddrin_server;
	sockaddrin_server.sin_family = AF_INET;
	sockaddrin_server.sin_port = htons(SERVER_PORT);
	sockaddrin_server.sin_addr.s_addr = htonl(INADDR_ANY);

	Bind(lfd, (struct sockaddr*)&sockaddrin_server, sizeof(sockaddrin_server));

	//第三步:设定监听上限,这个监听上限的意思是:同时只能有2个客户端和此 server连接,我们这个server只能要2个客户端和我们连接,测试如果有3个的客户端连接的情况下,server端发生的现象 和 client端发生的现象,因此还需要写一个客户端
	Listen(lfd, 2);

	
	// 第四步 :accept 阻塞监听客户端连接,并 fork出子进程。
	// 4.1 阻塞,并等待客户端连接. accept的第二个参数是传入传出参数,代表的是:成功于服务器连接的客户端的 地址结构

	struct sockaddr_in sockaddrin_client;
	socklen_t sockaddrin_clientsocklen_t = sizeof(sockaddrin_client);
	pid_t pid;
	while (1) {

		int  cfd = Accept(lfd, (struct sockaddr*)&sockaddrin_client, &sockaddrin_clientsocklen_t);
		char clientip[33] = { 0 };
		const char* clientipresult = inet_ntop(AF_INET, &sockaddrin_client.sin_addr.s_addr, clientip, 32);

		if (clientipresult == NULL) {
			printf("inet_ntop error\n");
		}
		else {
			printf("client addr port = %d,addr ip = %s \n", ntohs(sockaddrin_client.sin_port), clientipresult);
		}
		pid = fork();

		if (pid == -1) {
			//fork 函数的 error 处理
			perr_exit("fork error");
		}
		else if (pid > 0) {
			//4.1 父进程,负责监听lfd功能.因此先要关闭 cfd
			close(cfd);
			//4.2 父进程还需要回收子进程,以避免子进程完成后变成僵尸进程。
			// 那么如何回收呢? 如果是 阻塞回收,那么while(1)的循环在父进程这里就卡在这里了
			// 如果是 不阻塞忙轮询 回收呢?也不行,怎么忙轮询呢?
			// 因此这里只能用信号捕捉函数 ,捕捉 SIGCHID 信号



			//struct sigaction {
			//	void     (*sa_handler)(int);
			//	void     (*sa_sigaction)(int, siginfo_t*, void*);
			//	sigset_t   sa_mask;
			//	int        sa_flags;
			//	void     (*sa_restorer)(void);
			//};

			//int sigaction(int signum, const struct sigaction* act,struct sigaction* oldact);
			
			struct sigaction act;
			act.sa_handler = catchchild;
			sigemptyset(&act.sa_mask);
			act.sa_flags = 0;
			sigaction(SIGCHLD, &act, NULL);

			//4.3 屏蔽解除
			sigprocmask(SIG_UNBLOCK, &set, NULL);

		}
		else if (pid == 0 ) {
			//4.4 子进程,负责 cfd 和 client的交换数据发送信息功能。因此先要关闭 lfd
			close(lfd);

			//4.5 连接上后,循环的 使用read函数 读取客户端发送过来的数据了。注意的是,read函数默认是阻塞读取的
			char readbuffer[256] = { 0 };
			int readbufferlen = 0;

			while (1) {
				readbufferlen = Read(cfd, readbuffer, 256);
				printf("read buffer from client %s \n", readbuffer);
				for (int i = 0; i < readbufferlen; ++i) {
					readbuffer[i] = toupper(readbuffer[i]);
				}
				Write(cfd, readbuffer, readbufferlen);
				Write(STDOUT_FILENO, readbuffer, readbufferlen);
			}
		}
	}
}

2.6 编译命令

gcc wrap.c socket_server_support_more_clientconnect_use_fork.c -o socket_server_support_more_clientconnect_use_fork.out

*****关于 listen 第二个参数的 问题研究

在代码中,设定的listen(lfd,2); 那么这个参数2刚开始的自己的想法是:最多有两个客户端连接上server,但是实际测试中,用了5客户端连接,都没有问题,这说明啥呢?说明自己对 listen的第二个参数理解是不对的。
 

listen(lfd,2); 这个listen的第二个参数是啥意思呢?赋值多少比较合理呢?

我们先来看 listen参数的说明:

       The backlog argument defines the maximum length to which the  queue  of
       pending  connections  for  sockfd  may  grow.   If a connection request
       arrives when the queue is full, the client may receive an error with an
       indication  of  ECONNREFUSED  or,  if  the underlying protocol supports
       retransmission, the request may be ignored so that a later reattempt at
       connection succeeds.

  backlog :就是你设置的参数值

中文翻译就是:

backlog 参数定义了待处理连接队列的最大长度,sockfd 可能会增长。如果连接请求在队列已满时到达,客户端可能会收到带有 ECONNREFUSED 指示的错误,或者如果底层协议支持重传,该请求可能会被忽略,以便稍后重新尝试连接成功。

这两个博客说的很仔细:

https://zhuanlan.zhihu.com/p/634606981

listen()函数的第二个参数详解_listen函数的第二个参数-CSDN博客

整理核心:所以,backlog 目前的真正含义就是:

(1)在linux 2.2 内核之前,backlog是指半连接队列(syns_queue)的长度。

(2)在linux2.2及之后,backlog是指已经完全建立连接,但是还没有被应用层accept之前,socket所处全连接队列(accetp_queue)的长度。

全连接队列是什么?

全连接队列存储3次握手成功并已建立的连接,将其称为全连接队列,也可称为接收队列(Accept队列),本文中的描述将称为Accept队列或全连接队列。如下红框中所示,全连已成功建立三次握手,当前的TCP状态为ESTABLISHED,但是服务端还未Accept的队列。

要核心学习这,实际上要学习linux 内核的整体课程才能有深入的了解,这里只是对这个知识点比较迷惑,记录一下。

三. 可以被多个 client 链接 socket 例子,使用 子线程完成

socket_server_support_more_clientconnect_use_pthread.c

/*
使用线程完成 socket 并发服务器
主线程socket,bind ,listen,
在循环中,accept得到 cfd ,并创建 pthread,在pthread函数中 

*/

#include "wrap.h"
#include <pthread.h>


typedef struct c_info
{
	int cfd;
	struct sockaddr_in cliaddr;

}CINFO;

void* client_fun(void* arg);

int main(int argc, char* argv[]) {

	//当 pthread_attr_t 是全局变量的时候,可以直接使用静态方式初始化,目的是 设置pthread的属性为 分离
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	short port = 8888;
	int lfd = tcp4bind(port, NULL);//创建套接字 绑定 

	Listen(lfd, 128);
	struct sockaddr_in cliaddr;
	socklen_t len = sizeof(cliaddr);
	CINFO* info;
	while (1)
	{
		int cfd = Accept(lfd, (struct sockaddr*)&cliaddr, &len);
		char ip[16] = "";
		pthread_t pthid;

		//将accpet后的 cfd 做为参数传递到 子线程。
		info = malloc(sizeof(CINFO));
		info->cfd = cfd;
		info->cliaddr = cliaddr;
		//在前面已经将 attr 设置为分离态了,因此不存在 主线程回收的问题
		pthread_create(&pthid, &attr, client_fun, info);
	}

	return 0;

}

//子线程启动调用函数
void* client_fun(void* arg)
{
	CINFO* info = (CINFO*)arg;
	char ip[16] = "";

	printf("new client ip=%s port=%d\n", inet_ntop(AF_INET, &(info->cliaddr.sin_addr.s_addr), ip, 16),
		ntohs(info->cliaddr.sin_port));
	while (1)
	{
		char buf[1024] = "";
		int count = 0;
		count = read(info->cfd, buf, sizeof(buf));
		if (count < 0)
		{
			perror("");
			break;

		}
		else if (count == 0)
		{
			printf("client close\n");
			break;
		}
		else
		{
			printf("%s\n", buf);
			write(info->cfd, buf, count);

		}
	}
	close(info->cfd);
	//记得最后要释放info,避免内存泄漏
	free(info);
}

编译命令

gcc wrap.c socket_server_support_more_clientconnect_use_pthread.c -lpthread -o socket_server_support_more_clientconnect_use_pthread.out

四. select 模型

五 poll 模型

六 epoll 模型

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

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

相关文章

Java基础 (一)

基础概念及运算符、判断、循环 基础概念 关键字 数据类型 分为两种 基本数据类型 标识符 运算符 运算符 算术运算符 隐式转换 小 ------>>> 大 强制转换 字符串 拼接符号 字符 运算 自增自减运算符 ii赋值运算符 赋值运算符 包括 强制转换 关系运算符 逻辑运算符 …

项目集成RabbitMQ

文章目录 1.common-rabbitmq-starter1.创建common-rabbitmq-starter2.pom.xml3.自动配置1.RabbitMQAutoConfiguration.java2.spring.factories 2.测试使用1.创建common-rabbitmq-starter-demo2.目录结构3.pom.xml4.application.yml5.TestConfig.java 配置交换机和队列6.TestCon…

Codeforces Round 1000 (Div. 2) B and C

B. Subsequence Update 链接&#xff1a;Problem - B - Codeforces 题意&#xff1a;给定一个数组 可以选择任意个元素 后对这些元素进行排序 问你给定一个区间 这个区间的最小值 算法&#xff1a;贪心 排序 思路&#xff1a;下标1到r的最小个&#xff08;r-l1&#xff09;…

进程的哪些内存类型容易引起内存泄漏

相信你在平时的工作中&#xff0c;应该遇到过下面这些场景&#xff1a; 伴随着服务器中的后台任务持续地运行&#xff0c;系统中可用内存越来越少&#xff1b; 应用程序正在运行时忽然被 OOM kill 掉了&#xff1b; 进程看起来没有消耗多少内存&#xff0c;但是系统内存就是不够…

如何给自己的域名配置免费的HTTPS How to configure free HTTPS for your domain name

今天有小伙伴给我发私信&#xff0c;你的 https 到期啦 并且随手丢给我一个截图。 还真到期了。 javapub.net.cn 这个网站作为一个用爱发电的编程学习网站&#xff0c;用来存编程知识和面试题等&#xff0c;平时我都用业余时间来维护&#xff0c;并且还自费买了服务器和阿里云…

Glarysoft Malware Hunter 多语检测和删除各种恶意软件和间谍软件 v1.195.0.824

Glarysoft Malware Hunter 是一款专业的安全工具&#xff0c;旨在帮助用户检测和删除各种恶意软件和间谍软件。它可以扫描和删除计算机上的病毒、木马、广告软件和其他安全威胁。 软件功能 病毒扫描&#xff1a;Malware Hunter可以快速而全面地扫描计算机&#xff0c;以查找潜…

通过Ukey或者OTP动态口令实现windows安全登录

通过 安当SLA&#xff08;System Login Agent&#xff09;实现Windows安全登录认证&#xff0c;是一种基于双因素认证&#xff08;2FA&#xff09;的解决方案&#xff0c;旨在提升 Windows 系统的登录安全性。以下是详细的实现方法和步骤&#xff1a; 1. 安当SLA的核心功能 安…

Windows远程连接Docker服务

问题背景 本地开发了一个SpringBoot项目&#xff0c;想通过Docker部署起来&#xff0c;我本地是Window11系统&#xff0c;由于某些原因不能虚拟化并且未安装Docker-Desktop&#xff0c;所以我在想有没有办法本地不需要虚拟化也不需要安装Docker-Desktop来实现支持Docker命令远…

Ubuntu20.04 运行 Cartographer demo bag

官方文档&#xff1a; Running Cartographer ROS on a demo bag — Cartographer ROS documentation Running Cartographer ROS on a demo bag Now that Cartographer and Cartographer’s ROS integration are installed, you can download example bags (e.g. 2D and 3D b…

【R语言】流程控制

一、流程控制 R语言中&#xff0c;常用的流程控制函数有&#xff1a;repeat、while、for、if…else、switch。 1、repeat循环 repeat函数经常与 break 语句或 next 语句一起使用。 repeat ({x <- sample(c(1:7),1)message("x ", x, ",你好吗&#xff1f…

2025年最新深度学习环境搭建:Win11+ cuDNN + CUDA + Pytorch +深度学习环境配置保姆级教程

本文目录 一、查看驱动版本1.1 查看显卡驱动1.2 显卡驱动和CUDA对应版本1.3 Pytorch和Python对应的版本1.4 Pytorch和CUDA对应的版本 二、安装CUDA三、安装cuDANN四、安装pytorch五、验证是否安装成功 一、查看驱动版本 1.1 查看显卡驱动 输入命令nvidia-smi可以查看对应的驱…

Go学习:常量

变量&#xff1a;程序运行期间&#xff0c;可以改变的量&#xff0c;变量声明需要使用 var 常量&#xff1a;程序运行期间&#xff0c;不可以改变的量&#xff0c;常量声明需要使用 const 目录 1. 常量不允许修改 2. 常量赋值不使用 : 3. 常量能够自动推导类型 1. 常量不允许…

字符串和正则表达式(System.String类)

在C#string关键字实际上指向.NET基类System.String。System.String是一个功能非常强大且用途非常广泛的基类&#xff0c;但它不是.NET库中唯一与字符串相关的类。 主要内容&#xff1a; 创建字符串——如果多次修改一个字符串&#xff0c;例如&#xff0c;在显示字符串或将其传…

WPF实战案例 | C# WPF实现大学选课系统

WPF实战案例 | C# WPF实现大学选课系统 一、设计来源1.1 主界面1.2 登录界面1.3 新增课程界面1.4 修改密码界面 二、效果和源码2.1 界面设计&#xff08;XAML&#xff09;2.2 代码逻辑&#xff08;C#&#xff09; 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&a…

对数的换底公式及其证明

一、换底公式 二、证明 设 &#xff0c;由于对数和指数之间可以相互转换&#xff0c;不难得到&#xff1a;。 将 等式两边分别取以c为底的对数&#xff0c;得到&#xff1a; 联立&#xff08;1&#xff09;&#xff08;2&#xff09;式&#xff0c;得到&#xff1a; &#x…

STM32补充——IAP

0 前置知识&#xff1a; FLASH相关内容&#xff1a;前往STM32补充——FLASH STM32三种烧录方式&#xff08;看看就行&#xff09;&#xff1a; 1.ISP&#xff1a;In System Programming&#xff08;在系统编程&#xff09; 执行芯片厂商的 Bootloader 程序进入 ISP 模式&…

【2024年华为OD机试】(C/D卷,200分)- 5G网络建设 (JavaScriptJava PythonC/C++)

一、问题描述 题目描述 现需要在某城市进行5G网络建设&#xff0c;已经选取N个地点设置5G基站&#xff0c;编号固定为1到N。接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通。不同基站之间假设光纤的成本各不相同&#xff0c;且有些节点之间已经存在光纤相连。 …

计算机毕业设计hadoop+spark股票基金推荐系统 股票基金预测系统 股票基金可视化系统 股票基金数据分析 股票基金大数据 股票基金爬虫

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

【Linux】深刻理解动静态库

1.什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个⼈的代码都从零开始&#xff0c;因此库的存在意义⾮同寻常。本质上来说库是⼀种可执⾏代码的⼆进制形式&#xff0c;可以被操作系统载…

CrypTen——基于pytorch的隐私保护机器学习框架

目录 一、CrypTen概述 二、应用场景 三、CrypTen优势 四、CrypTen技术解析 1.基于pytorch的构建基础 2.核心密码学原语 3.加密模型训练流程 五、传统隐私保护技术与CrypTen的对比 1.传统隐私保护技术介绍 2.CrypTen与传统隐私保护技术的区别 六、CrypTen的环境配置…