Linux网络编程2-多进程和多线程版本服务器

Linux网络编程2-多进程和多线程版本服务器

    • 1.套接字相关函数的封装wrap.h wrap.c
    • 2.支持多并发的服务器
    • 3.多进程版本分析
    • 4.多进程版本实现
    • 5.多线程版本分析
    • 6.多线程版本实现


1.套接字相关函数的封装wrap.h wrap.c

像accept,read这样的能够引起阻塞的函数,若被信号打断,由于信号的优先级较高, 会优先处理信号, 信号处理完成后,会使accept或者read解除阻塞, 然后返回, 此时返回值为 -1,设置errno=EINTR;

在/usr/include/asm-generic/errno.h文件中包含了errno所有的宏和对应的错误描述信息.

#ifndef __WRAP_H_
#define __WRAP_H_
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.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);
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
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.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;elseperr_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;elsereturn -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;elsereturn -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;elsereturn -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;elsereturn -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;} elsereturn -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(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));return lfd;
}

2.支持多并发的服务器

如何支持多个客户端—支持多并发的服务器
由于accept和read函数都会阻塞, 如当read的时候, 不能调用accept接受新的连接, 当accept阻塞等待的时候不能read读数据.

第一种方案: 使用多进程, 可以让父进程接受新连接, 让子进程处理与客户端通信
思路: 让父进程accept接受新连接, 然后fork子进程, 让子进程处理通信, 子进程处理完成后退出, 父进程使用SIGCHLD信号回收子进程.

第二种方案: 使用多线程, 让主线程接受新连接, 让子线程处理与客户端通信; 使用多线程要将线程设置为分离属性, 让线程在退出之后自己回收资源.

如何不使用多进程或者多线程完成多个客户端的连接请求?
可以将accept和read函数设置为非阻塞, 调用fcntl函数可以将文件描述符设置为非阻塞, 让后再while循环中忙轮询.

3.多进程版本分析

阻塞函数在阻塞期间若收到信号,会被信号终端,errno设置为EINTR,这个错误不应该看成一个错误.

while(1)
{cfd = accept();while(1){n = read(cfd, buf, sizeof(buf));if(n<=0){break;}}
}

解决办法1:
将cfd设置为非阻塞: fcntl
缺点:假如有多个客户端连接请求, cfd只会保留最后一个文件描述符的值

解决方法2:
使用多进程: 让父进程监听接受新的连接, 子进程处理新的连接(接收和发送数据); 父进程还负责回收子进程


多进程版本思路:子进程复制父进程的文件描述符

//1 创建socket, 得到一个监听的文件描述符lfd---socket()
//2 将lfd和IP和端口port进行绑定-----bind();
//3 设置监听----listen()
//4 进入while
while(1)
{//等待有新的客户端连接到来cfd = accept();//fork一个子进程, 让子进程去处理数据pid = fork();if(pid<0){exit(-1);}else if(pid>0){//关闭通信文件描述符cfdclose(cfd);}else if(pid==0){//关闭监听文件描述符close(lfd);//收发数据while(1){//读数据n = read(cfd, buf, sizeof(buf));if(n<=0){break;}//发送数据给对方write(cfd, buf, n);}close(cfd);//下面的exit必须有, 防止子进程再去创建子进程exit(0);}
}
close(lfd);

还需要添加的功能: 父进程使用SIGCHLD信号完成对子进程的回收
注意点: accept或者read函数是阻塞函数,会被信号打断,此时不应该视为一个错误。errno=EINTR

父子进程能够共享的:
文件描述符(子进程复制父进程的文件描述符)
mmap共享映射区

4.多进程版本实现

//多进程版本的网络服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include "wrap.h"int main()
{//创建socketint lfd = Socket(AF_INET, SOCK_STREAM, 0);//绑定struct sockaddr_in serv;bzero(&serv, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(8888);serv.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv, sizeof(serv));//设置监听Listen(lfd, 128);pid_t pid;int cfd;char sIP[16];socklen_t len;struct sockaddr_in client;while(1){//接受新的连接len = sizeof(client);memset(sIP, 0x00, sizeof(sIP));cfd = Accept(lfd, (struct sockaddr *)&client, &len);printf("client:[%s] [%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));//接受一个新的连接, 创建一个子进程,让子进程完成数据的收发操作pid = fork();if(pid<0){perror("fork error");exit(-1);}else if(pid>0){//关闭通信文件描述符cfdclose(cfd);			}else if(pid==0){//关闭监听文件描述符close(lfd);int i=0;int n;char buf[1024];while(1){//读数据n = Read(cfd, buf, sizeof(buf));if(n<=0){printf("read error or client closed, n==[%d]\n", n);break;}//printf("client:[%s] [%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));printf("[%d]---->:n==[%d], buf==[%s]\n", ntohs(client.sin_port), n, buf);//将小写转换为大写for(i=0; i<n; i++){buf[i] = toupper(buf[i]);}//发送数据Write(cfd, buf, n);}//关闭cfdclose(cfd);exit(0);}}//关闭监听文件描述符close(lfd);return 0;
}

改进:增加对子进程的回收:

//多进程版本的服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include "wrap.h"//信号处理函数
void waitchild(int signo)
{pid_t wpid;while(1){wpid = waitpid(-1, NULL, WNOHANG);if(wpid>0){printf("child exit, wpid==[%d]\n", wpid);}else if(wpid==0 || wpid==-1){break;}}
}int main()
{//创建socketint lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));//绑定-bindstruct sockaddr_in serv;serv.sin_family = AF_INET;serv.sin_port = htons(8888);serv.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv, sizeof(serv));//监听-listenListen(lfd, 128);//阻塞SIGCHLD信号sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGCHLD);sigprocmask(SIG_BLOCK, &mask, NULL);int cfd;socklen_t len;char sIP[16];pid_t pid;struct sockaddr_in client;while(1){//等待客户端连接--acceptmemset(sIP, 0x00, sizeof(sIP));len = sizeof(client);bzero(&client, sizeof(client));cfd = Accept(lfd, (struct sockaddr *)&client, &len);	printf("client-->[%s]:[%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));//创建子进程pid = fork();if(pid<0){perror("fork error");close(lfd);return -1;}	else if(pid>0) //父进程{//关闭通信的文件描述符close(cfd);//注册SIGCHLD信号处理函数struct sigaction act;act.sa_handler = waitchild;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGCHLD, &act, NULL);//解除对SIGCHLD信号的阻塞sigprocmask(SIG_UNBLOCK, &mask, NULL);}else if(pid==0) //子进程{//关闭监听文件描述符close(lfd);	int i = 0;int n = 0;char buf[1024];while(1){memset(buf, 0x00, sizeof(buf));n = Read(cfd, buf, sizeof(buf));if(n<=0)	{printf("read error or client closed, n==[%d]\n", n);break;	}printf("read over, n==[%d],buf==[%s]\n", n, buf);for(i=0; i<n; i++){buf[i] = toupper(buf[i]);}write(cfd, buf, n);}close(cfd);exit(0);}}//关闭监听文件描述符close(lfd);return 0;
}

5.多线程版本分析

//1 创建socket, 得到一个监听的文件描述符lfd---socket()
//2 将lfd和IP和端口port进行绑定-----bind();
//3 设置监听----listen() 
//4 while循环
while(1)
{//接受新的客户端连接请求cfd = accept();//创建一个子线程pthread_create(&threadID, NULL, thread_work, &cfd);//设置线程为分离属性pthread_detach(threadID);}
close(lfd);void *thread_work(void *arg)
{//获得参数: 通信文件描述符int cfd = *(int *)arg;while(1){//读数据n = read(cfd, buf, sizeof(buf));if(n<=0){break;}//发送数据write(cfd, buf, n);}close(cfd);
}

问题:
1 子线程能否关闭lfd?
子线程不能关闭监听文件描述符lfd,原因是子线程和主线程共享文件描述符,而不是复制的.

2 主线程能否关闭cfd?
主线程不能关闭cfd,主线程和子线程共享一个cfd,而不是复制的,close之后cfd就会被真正关闭.

3 多个子线程共享cfd, 会有什么问题发生?
只有一个子线程能够通信。

6.多线程版本实现

//多线程版本的高并发服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include <pthread.h>
#include "wrap.h"//子线程回调函数
void *thread_work(void *arg)
{sleep(20);int cfd = *(int *)arg;printf("cfd==[%d]\n", cfd);int i;int n;char buf[1024];while(1){//read数据memset(buf, 0x00, sizeof(buf));n = Read(cfd, buf, sizeof(buf));if(n<=0){printf("read error or client closed,n==[%d]\n", n);break;}printf("n==[%d], buf==[%s]\n", n, buf);for(i=0; i<n; i++){buf[i] = toupper(buf[i]);}//发送数据给客户端Write(cfd, buf, n);	}//关闭通信文件描述符close(cfd);pthread_exit(NULL);
}int main()
{//创建socketint lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));//绑定struct sockaddr_in serv;bzero(&serv, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(8888);serv.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv, sizeof(serv));//设置监听Listen(lfd, 128);int cfd;pthread_t threadID;while(1){//接受新的连接cfd = Accept(lfd, NULL, NULL);//创建子线程pthread_create(&threadID, NULL, thread_work, &cfd);//设置子线程为分离属性pthread_detach(threadID);}//关闭监听文件描述符close(lfd);return 0;
}

上面代码中,accept接收3个连接,创建3个子线程,这些子线程共享threadId结构与cfd变量,因此需要一个线程对应一个。数组。

// 创建100个INFO结构,最多可以接收100个连接
struct INFO
{int cfd;pthread_t threadID;struct sockaddr_in client;
};
struct INFO info[100];//初始化INFO数组
for(i=0; i<100; i++)
{info[i].cfd=-1;
}// 接收连接以后,从数组中分配一个INFO结构
for(i=0; i<100; i++)
{if(info[i].cfd==-1){//这块内存可以使用}
}
if(i==100) // 没有空闲的INFO结构,拒绝接收新的连接
{//拒绝接受新的连接close(cfd);
}
//多线程版本的服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include <pthread.h>
#include "wrap.h"typedef struct info
{int cfd;   //若为-1表示可用, 大于0表示已被占用int idx;pthread_t thread;struct sockaddr_in client;
}INFO;INFO thInfo[1024];//线程执行函数
void *thread_work(void *arg)
{INFO *p = (INFO *)arg;printf("idx==[%d]\n", p->idx);char sIP[16];memset(sIP, 0x00, sizeof(sIP));printf("new client:[%s][%d]\n", inet_ntop(AF_INET, &(p->client.sin_addr.s_addr), sIP, sizeof(sIP)), ntohs(p->client.sin_port));int n;int cfd = p->cfd;struct sockaddr_in client;memcpy(&client, &(p->client), sizeof(client));char buf[1024];while(1){memset(buf, 0x00, sizeof(buf));//读数据n = Read(cfd, buf, sizeof(buf));if(n<=0){printf("read error or client closed, n==[%d]\n", n);Close(cfd);p->cfd =-1;  //设置为-1表示该位置可用pthread_exit(NULL);}for(int i=0; i<n; i++){buf[i] = toupper(buf[i]);}//发送数据Write(cfd, buf, n);}
}void init_thInfo()
{int i = 0;for(i=0; i<1024; i++){thInfo[i].cfd = -1;;}
}int findIndex()
{int i;for(i=0; i<1024; i++){if(thInfo[i].cfd==-1){break;}}if(i==1024){return -1;}return i;
}int main()
{//创建socketint lfd = Socket(AF_INET, SOCK_STREAM, 0);//设置端口复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));//绑定--将lfd 和 IP PORT绑定struct sockaddr_in serv;bzero(&serv, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(8888);serv.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv, sizeof(serv));//监听Listen(lfd, 128);//初始化init_thInfo();int cfd;int ret;int idx;socklen_t len;pthread_t thread;struct sockaddr_in client;while(1){len = sizeof(client);bzero(&client, sizeof(client));//获得一个新的连接cfd = Accept(lfd, (struct sockaddr *)&client, &len);//创建一个子进程, 让子进程处理连接---接收数据和发送数据//找数组中空闲的位置idx = findIndex();if(idx==-1){Close(cfd);continue;}//对空闲位置的元素的成员赋值thInfo[idx].cfd = cfd;thInfo[idx].idx = idx;memcpy(&thInfo[idx].client, &client, sizeof(client));//创建子线程---该子线程完成对数据的收发ret = pthread_create(&thInfo[idx].thread, NULL, thread_work, &thInfo[idx]);if(ret!=0){printf("create thread error:[%s]\n", strerror(ret));exit(-1);}//设置子线程为分离属性pthread_detach(thInfo[idx].thread);}Close(lfd);return 0;}

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

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

相关文章

【重拾C语言】五、模块化程序设计——函数(定义、调用、参数传递、结果返回、函数原型;典例:打印字符图形、验证哥德巴赫猜想)

目录 前言 五、模块化程序设计——函数 5.1 计算三角形的重心 5.2 函数 5.2.1 函数定义 5.2.2 函数调用 a. 函数调用的形式和过程 b. 参数传递 值传递 指针传递 c. 函数结果返回 5.2.3 函数原型&#xff08;先调用后定义&#xff09; 5.3 程序设计实例 5.3.1 打印…

C/S架构学习之TCP的三次握手和四次挥手

TCP的三次握手&#xff1a;一定由客户端主动发起的&#xff0c;发生在建立连接的过程中。此过程发生在客户端的connect()函数和服务器的accept()函数之间。第一次握手&#xff1a;客户端向服务器发送一个带有SYN标志的数据包&#xff0c;表示客户端请求建立连接。并且客户端会选…

【Python】eval

eval()函数是Python中的一个内置函数&#xff0c;用于将字符串作为代码进行执行&#xff0c;并返回执行结果。 eval()函数的语法如下&#xff1a; eval(expression, globalsNone, localsNone)expression参数是一个字符串&#xff0c;表示要执行的代码。globals参数是一个字典…

3D孪生场景搭建:模型区域摆放

前面介绍完了NSDT场景编辑器的线性绘制和阵列绘制&#xff0c;本章将讲述下编辑器的另一种绘制方式&#xff1a;区域绘制。 1、区域绘制功能简介 在场景中绘制资产时&#xff0c;除使用上述两个的方式外&#xff0c;NSDT 编辑器还支持使用区域绘制的方式进行绘制。先选取需要…

python修改unittestreport中的用例条数

背景: 自动化框架中使用yaml文件作为数据配置&#xff0c;使用ddt作为数据驱动来运行测试用例&#xff0c;由于测试用例都是基于场景去编写&#xff0c;目前都是一个测试类算是一条测试用例&#xff0c;但基于测试报告里面一个类运行的测试方法有多个&#xff0c;因此统计的测试…

计算机毕业设计 基于SpringBoot的图书馆管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

香蕉叶病害数据集

1.数据集 第一个文件夹为数据增强&#xff08;旋转平移裁剪等操作&#xff09;后的数据集 第二个文件夹为原始数据集 2.原始数据集 Cordana文件夹&#xff08;162张照片&#xff09; healthy文件夹&#xff08;129张&#xff09; Pestalotiopsis文件夹&#xff08;173张照片&…

【Java 进阶篇】JDBC 数据库连接池 C3P0 详解

数据库连接池是数据库编程中常用的一种技术&#xff0c;它可以有效地管理数据库连接&#xff0c;提高数据库访问的性能和效率。在 Java 编程中&#xff0c;有多种数据库连接池可供选择&#xff0c;其中之一就是 C3P0。本文将详细介绍 C3P0 数据库连接池的使用&#xff0c;包括原…

Linux CentOS7 vim重复行

在用vim编辑处理文件时&#xff0c;会有重复行。有的是情境需要&#xff0c;有的可能是误操作而形成。对于正常形成的重复行&#xff0c;我们不作讨论&#xff0c;我们仅讨论什么情况下会出现重复行&#xff0c;如何避免&#xff0c;如何处理。 在文件中的单行或多个连续空白行…

【Unity】3D贪吃蛇游戏制作/WebGL本地测试及项目部署

本文是Unity3D贪吃蛇游戏从制作到部署的相关细节 项目开源代码&#xff1a;https://github.com/zstar1003/3D_Snake 试玩链接&#xff1a;http://xdxsb.top/Snake_Game_3D 效果预览&#xff1a; 试玩链接中的内容会和该效果图略有不同&#xff0c;后面会详细说明。 游戏规则 …

【C语言】内存函数的详细教学和模拟实现

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是gugugu。希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f194;本文由 gugugu 原创 CSDN首发&#x1f412; 如需转载还请通知⚠…

互联网Java工程师面试题·Dubbo篇·第一弹

目录 1、为什么要用 Dubbo&#xff1f; 2、Dubbo 的整体架构设计有哪些分层? 3、默认使用的是什么通信框架&#xff0c;还有别的选择吗? 4、服务调用是阻塞的吗&#xff1f; 5、一般使用什么注册中心&#xff1f;还有别的选择吗&#xff1f; 6、默认使用什么序列化框架&…

学习记忆——宫殿篇——记忆宫殿——记忆桩——卧室——莫兰勋爵在地铁走失的案子

《神探夏洛克》第三季第一集中提到“思维殿堂”&#xff0c;其实指的就是记忆宫殿。讲述了一个名叫莫兰勋爵在地铁走失的案子&#xff0c;这里简单给大家罗列以下破案信息&#xff1a; 订阅报纸的男人、伦敦养狗的女人、穿着黑色运动的非裔女人、松木、云杉、雪松、新樟脑球、碳…

linux 列出网络上所有活动的主机

列出网络上所有活动的主机 #!/bin/bash# {start..end}会由shell对其进行扩展生成一组ip地址for ip in 192.168.0.{1..255} ;do ping $ip -c 2 &> /dev/null ; # $?获取退出状态&#xff0c;顺利退出则为0 if [ $? -eq 0 ]; then echo $ip is alive fidone https://zh…

SSR服务器端渲染

SSR 代表服务器端渲染&#xff08;Server-Side Rendering&#xff09;&#xff0c;它是一种用于构建 web 应用程序的技术。与传统的客户端渲染&#xff08;Client-Side Rendering&#xff09;不同&#xff0c;SSR 是在服务器上生成完整的 HTML 页面&#xff0c;并将其发送给浏览…

AtCoder Beginner Contest 232(A-G)

A - QQ solver (atcoder.jp)直接按题意模拟即可。 B - Caesar Cipher (atcoder.jp)按题意模拟即可 C - Graph Isomorphism (atcoder.jp)按题意模拟即可 D - Weak Takahashi (atcoder.jp) 一个非常套路的网格dp E - Rook Path (atcoder.jp) &#xff08;1&#xff09;题意 有…

Linux6.1中为什么用Radix树替换位图(bitmap)来管理进程pid

在过去的几十年中&#xff0c;Linux内核为了有效地管理进程&#xff0c;采用了位图&#xff08;bitmap&#xff09;数据结构来记录和跟踪进程的PID。我们知道Linux支持的最大进程数量为65535个&#xff0c;那么用位图来表示的话只需要16位bit就够了&#xff0c;这大大节约了内存…

探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

【Java】接口 interface

目录 概述 示例代码&#xff1a; 接口成员访问特点 示例代码&#xff1a; 概述 什么是接口 接口就是一种公共的规范标准&#xff0c;只要符合规范标准&#xff0c;大家都可以调用。 Java 中的接口更多的体现在对行为的抽象&#xff01; 1. 接口 用关键字 interface 修饰 pub…

【Windows】安装Microsoft Store,Microsoft Store离线包

用了大半年的Windows&#xff0c;今天发现没有Microsoft store 安装方法有2&#xff1a; 方法一、到这里找相关程序 https://store.rg-adguard.net/ …… 方法二、用下边的离线包 二选一下载&#xff1a; 离线包下载&#xff1a;https://wwon.lanzout.com/i4xPL1atnotc离…