当前位置: 首页 > news >正文

独立商城网站建设/随州今日头条新闻

独立商城网站建设,随州今日头条新闻,wordpress+高性能,wordpress点击网页效果作者:小萌新 专栏:网络 作者简介:大二学生 希望能和大家一起进步 本篇博客简介:简单介绍下各种类型的Tcp协议 各种类型Tcp服务器 多进程版的TCP网络程序捕捉SIGCHLD信号让孙子进程执行任务 多线程TCP网络程序线程池版多线程TCP网络…

作者:@小萌新
专栏:@网络
作者简介:大二学生 希望能和大家一起进步
本篇博客简介:简单介绍下各种类型的Tcp协议

各种类型Tcp服务器

  • 多进程版的TCP网络程序
    • 捕捉SIGCHLD信号
    • 让孙子进程执行任务
  • 多线程TCP网络程序
  • 线程池版多线程TCP网络程序

我们在前面的网络编程套接字(二)中写出了一个单执行流的服务器

我们再来回顾下它的运行

在这里插入图片描述
我们首先启动服务器 之后启动客户端1 最后启动客户端2

我们发现启动客户端1之后向服务器发送数据服务器很快的就回显了一个数据并且打印了得到一个新连接

可是在客户端2连接的时候却没有发生任何情况

在这里插入图片描述

当我们的客户端1退出的时候 服务器接受到了客户端2的连接并且回显了数据

单执行流服务器

这是因为我们的服务器是单执行流的 所以在同一时间只能有一个客户端接受服务

当服务端调用accept函数获取到连接后就给该客户端提供服务 但在服务端提供服务期间可能会有其他客户端发起连接请求 但由于当前服务器是单执行流的 只能服务完当前客户端后才能继续服务下一个客户端

客户端为什么会显示连接成功

服务器是处于监听状态的 在我们的客户端2发送连接请求的时候实际上已经被监听到了 只不过服务端没有调用accept函数将该连接获取上来

实际在底层会为我们维护一个连接队列 服务端没有accept的新连接就会放到这个连接队列当中 而这个连接队列的最大长度就是通过listen函数的第二个参数来指定的 因此服务端虽然没有获取第二个客户端发来的连接请求 但是在第二个客户端那里显示是连接成功的

如何解决?

单执行流的服务器一次只能给一个客户端提供服务 此时服务器的资源并没有得到充分利用 因此服务器一般是不会写成单执行流的 要解决这个问题就需要将服务器改为多执行流的 此时就要引入多进程或多线程

多进程版的TCP网络程序

我们将之前的单执行流服务器改为多进程服务器

当服务端调用accept函数获取到新连接后不是由当前执行流为该连接提供服务 而是当前执行流调用fork函数创建子进程 然后让子进程为父进程获取到的连接提供服务

由于父子进程是两个不同的执行流 当父进程调用fork创建出子进程后 父进程就可以继续从监听套接字当中获取新连接 而不用关心获取上来的连接是否服务完毕

子进程继承父进程的文件描述符表

需要注意的是 文件描述符表是隶属于一个进程的 子进程创建后会继承父进程的文件描述符表

比如父进程打开了一个文件 该文件对应的文件描述符是3 此时父进程创建的子进程的3号文件描述符也会指向这个打开的文件 而如果子进程再创建一个子进程 那么子进程创建的子进程的3号文件描述符也同样会指向这个打开的文件

在这里插入图片描述

但是当父进程创建出子进程之后 父子进程就会保持独立性了 此时父进程文件描述符表的变化不会影响子进程的文件描述符表

在我们之前学习的匿名管道通信时 我们就是使用的这个原理

父进程首先使用pipe函数得到两个文件描述符 一个是文件读端一个是文件的写端 此时父进程创建的子进程会继承这两个文件描述符

之后父子进程一个关闭管道的读端 另一个关闭管道的写端 这时父子进程文件描述符表的变化是不会相互影响的 此后父子进程就可以通过这个管道进行单向通信了

对于套接字文件也是一样的 父进程创建的子进程也会继承父进程的套接字文件 此时子进程就能够对特定的套接字文件进行读写操作 进而完成对对应客户端的服务

等待子进程问题

当父进程创建出子进程后 父进程是需要等待子进程退出的 否则子进程会变成僵尸进程 进而造成内存泄漏

因此服务端创建子进程后需要调用wait或waitpid函数对子进程进行等待

此时我们就有两种等待方式 阻塞式等待和非阻塞式等待:

  • 如果服务端采用阻塞的方式等待子进程 那么服务端还是需要等待服务完当前客户端 才能继续获取下一个连接请求此时服务端仍然是以一种串行的方式为客户端提供服务
  • 如果服务端采用非阻塞的方式等待子进程 虽然在子进程为客户端提供服务期间服务端可以继续获取新连接 但此时服务端就需要将所有子进程的PID保存下来 并且需要不断花费时间检测子进程是否退出

总之 服务端要等待子进程退出 无论采用阻塞式等待还是非阻塞式等待 都不尽人意 此时我们可以考虑让服务端不等待子进程退出

不等待子进程退出的方式

让父进程不等待子进程退出 常见的方式有两种:

  • 捕捉SIGCHLD信号 将其处理动作设置为忽略
  • 让父进程创建子进程 子进程再创建孙子进程 最后让孙子进程为客户端提供服务

捕捉SIGCHLD信号

实际当子进程退出时会给父进程发送SIGCHLD信号 如果父进程将SIGCHLD信号进行捕捉 并将该信号的处理动作设置为忽略 此时父进程就只需专心处理自己的工作 不必关心子进程了

下面是我们的处理代码 其中比较核心的代码是这一行

signal(SIGCHLD, SIG_IGN);
class TcpServer
{
public:void Start(){signal(SIGCHLD, SIG_IGN); //忽略SIGCHLD信号for (;;){//获取连接struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){std::cerr << "accept error, continue next" << std::endl;continue;}std::string client_ip = inet_ntoa(peer.sin_addr);int client_port = ntohs(peer.sin_port);std::cout << "get a new link->" << sock << " [" << client_ip << "]:" << client_port << std::endl;pid_t id = fork();if (id == 0){ //child//处理请求Service(sock, client_ip, client_port);exit(0); //子进程提供完服务退出}}}
private:int _listen_sock; //监听套接字int _port; //端口号
};

下面是在此运行的结果

在这里插入图片描述

我们可以发现 加上这几行代码之后我们就可以让服务器服务多个客户端了

让孙子进程执行任务

我们也可以让服务端创建出来的子进程再次进行fork 让孙子进程为客户端提供服务 此时我们就不用等待孙子进程退出了

命名:

  • 爷爷进程:在服务端调用accept函数获取客户端连接请求的进程
  • 爸爸进程:由爷爷进程调用fork函数创建出来的进程
  • 孙子进程:由爸爸进程调用fork函数创建出来的进程 该进程调用Service函数为客户端提供服务

我们让爸爸进程创建完孙子进程后立刻退出 此时服务进程(爷爷进程)调用wait/waitpid函数等待爸爸进程就能立刻等待成功 此后服务进程就能继续调用accept函数获取其他客户端的连接请求

不需要等待孙子进程退出

这里主要是利用了孤儿进程的原理 当孙子进程的父进程死亡后它就会被1号进程也就是init进程领养 当孙子进程运行完毕之后它的资源会由1号进程进行回收 我们也就不需要担心僵尸进程的问题了

关闭对应的文件描述符

服务进程(爷爷进程)调用accept函数获取到新连接后 会让孙子进程为该连接提供服务,此时服务进程已经将文件描述符表继承给了爸爸进程 而爸爸进程又会调用fork函数创建出孙子进程 然后再将文件描述符表继承给孙子进程。

而父子进程创建后 它们各自的文件描述符表是独立的 不会相互影响

因此服务进程在调用fork函数后 服务进程就不需要再关心刚才从accept函数获取到的文件描述符了 此时服务进程就可以调用close函数将该文件描述符进行关闭

同样的 对于爸爸进程和孙子进程来说 它们是不需要关心从服务进程(爷爷进程)继承下来的监听套接字的 因此爸爸进程可以将监听套接字关掉

关闭文件描述符的必要性:

  • 对于服务进程来说 当它调用fork函数后就必须将从accept函数获取的文件描述符关掉 因为服务进程会不断调用accept函数获取新的文件描述符(服务套接字) 如果服务进程不及时关掉不用的文件描述符 最终服务进程中可用的文件描述符就会越来越少
  • 而对于爸爸进程和孙子进程来说 还是建议关闭从服务进程继承下来的监听套接字 实际就算它们不关闭监听套接字 最终也只会导致这一个文件描述符泄漏 但一般还是建议关上 因为孙子进程在提供服务时可能会对监听套接字进行某种误操作 此时就会对监听套接字当中的数据造成影响
class TcpServer
{
public:void Start(){for (;;){//获取连接struct sockaddr_in peer;memset(&peer, '\0', sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){std::cerr << "accept error, continue next" << std::endl;continue;}std::string client_ip = inet_ntoa(peer.sin_addr);int client_port = ntohs(peer.sin_port);std::cout << "get a new link->" << sock << " [" << client_ip << "]:" << client_port << std::endl;pid_t id = fork();if (id == 0){ //childclose(_listen_sock); //child关闭监听套接字if (fork() > 0){exit(0); //爸爸进程直接退出}//处理请求Service(sock, client_ip, client_port); //孙子进程提供服务exit(0); //孙子进程提供完服务退出}close(sock); //father关闭为连接提供服务的套接字waitpid(id, nullptr, 0); //等待爸爸进程(会立刻等待成功)}}
private:int _listen_sock; //监听套接字int _port; //端口号
};

运行结果如下

在这里插入图片描述
我们可以发现当前服务器可以支持多个客户端访问并且得到的文件描述符都是4

多线程TCP网络程序

创建进程的成本是很高的,创建进程时需要创建该进程对应的进程控制块(task_struct)、进程地址空间(mm_struct)、页表等数据结构。而创建线程的成本比创建进程的成本会小得多,因为线程本质是在进程地址空间内运行,创建出来的线程会共享该进程的大部分资源,因此在实现多执行流的服务器时最好采用多线程进行实现

当服务进程调用accept函数获取到一个新连接后 就可以直接创建一个线程 让该线程为对应客户端提供服务

当然 主线程(服务进程)创建出新线程后 也是需要等待新线程退出的 否则也会造成类似于僵尸进程这样的问题 但对于线程来说 如果不想让主线程等待新线程退出 可以让创建出来的新线程调用pthread_detach函数进行线程分离 当这个线程退出时系统会自动回收该线程所对应的资源 此时主线程(服务进程)就可以继续调用accept函数获取新连接 而让新线程去服务对应的客户端

各个线程共享同一张文件描述符表

文件描述符表维护的是进程与文件之间的对应关系 因此一个进程对应一张文件描述符表

而主线程创建出来的新线程依旧属于这个进程 因此创建线程时并不会为该线程创建独立的文件描述符表 所有的线程看到的都是同一张文件描述符表

在这里插入图片描述

因此当服务进程(主线程)调用accept函数获取到一个文件描述符后 其他创建的新线程是能够直接访问这个文件描述符的

需要注意的是 虽然新线程能够直接访问主线程accept上来的文件描述符 但此时新线程并不知道它所服务的客户端对应的是哪一个文件描述符

因此主线程创建新线程后需要告诉新线程对应应该访问的文件描述符的值 也就是告诉每个新线程在服务客户端时 应该对哪一个套接字进行操作

文件描述符关闭的问题

由于此时所有线程看到的都是同一张文件描述符表 因此当某个线程要对这张文件描述符表做某种操作时 不仅要考虑当前线程 还要考虑其他线程

  • 对于主线程accept上来的文件描述符 主线程不能对其进行关闭操作 该文件描述符的关闭操作应该由新线程来执行 因为是新线程为客户端提供服务的 只有当新线程为客户端提供的服务结束后才能将该文件描述符关闭
  • 对于监听套接字 虽然创建出来的新线程不必关心监听套接字 但新线程不能将监听套接字对应的文件描述符关闭 否则主线程就无法从监听套接字当中获取新连接了

Service函数定义为静态成员函数

由于调用pthread_create函数创建线程时 新线程的执行例程是一个参数为void* 返回值为void*的函数 如果我们要将这个执行例程定义到类内 就需要将其定义为静态成员函数 否则这个执行例程的第一个参数是隐藏的this指针

在线程的执行例程当中会调用Service函数 由于执行例程是静态成员函数 静态成员函数无法调用非静态成员函数 因此我们需要将Service函数定义为静态成员函数 恰好Service函数内部进行的操作都是与类无关的 因此我们直接在Service函数前面加上一个static即可

Rontine函数

  static void* Rontine(void* arg){pthread_detach(pthread_self());int* p = (int*)arg;int sock = *p;Service(sock);return nullptr;}

Start函数

  void Start(){while(true){// accept struct sockaddr_in peer; memset(&peer , '\0' , sizeof(peer));socklen_t len = sizeof(peer);int sock = accept(_sockfd , (struct sockaddr*)&peer , &len);if (sock < 0){cout << "accept error" << endl; continue;}int* p = &sock; pthread_t tid;pthread_create(&tid , nullptr , Rontine , (void*)p);}}

运行结果如下
在这里插入图片描述

线程池版多线程TCP网络程序

当前多线程版的服务器存在的问题:

  • 每当有新连接到来时 服务端的主线程都会重新为该客户端创建为其提供服务的新线程 而当服务结束后又会将该新线程销毁 这样做不仅麻烦 而且效率低下 每当连接到来的时候服务端才创建对应提供服务的线程
  • 如果有大量的客户端连接请求 此时服务端要为每一个客户端创建对应的服务线程 计算机当中的线程越多 CPU的压力就越大 因为CPU要不断在这些线程之间来回切换 此时CPU在调度线程的时候 线程和线程之间切换的成本就会变得很高
  • 一旦线程太多 每一个线程再次被调度的周期就变长了 而线程是为客户端提供服务的 线程被调度的周期变长 客户端也迟迟得不到应答

解决思路

  • 可以在服务端预先创建一批线程,当有客户端请求连接时就让这些线程为客户端提供服务,此时客户端一来就有线程为其提供服务,而不是当客户端来了才创建对应的服务线程。
  • 当某个线程为客户端提供完服务后,不要让该线程退出,而是让该线程继续为下一个客户端提供服务,如果当前没有客户端连接请求,则可以让该线程先进入休眠状态,当有客户端连接到来时再将该线程唤醒。
  • 服务端创建的这一批线程的数量不能太多,此时CPU的压力也就不会太大。此外,如果有客户端连接到来,但此时这一批线程都在给其他客户端提供服务,这时服务端不应该再创建线程,而应该让这个新来的连接请求在全连接队列进行排队,等服务端这一批线程中有空闲线程后,再将该连接请求获取上来并为其提供服务。

我们可以发现 我们前面做的线程池可以完美解决上面的问题

线程池

服务类新增线程池成员

服务类新增线程池成员

  • 当实例化服务器对象时,先将这个线程池指针先初始化为空。
  • 当服务器初始化完毕后,再实际构造这个线程池对象,在构造线程池对象时可以指定线程池当中线程的个数,也可以不指定,此时默认线程的个数为5。
  • 在启动服务器之前对线程池进行初始化,此时就会将线程池当中的若干线程创建出来,而这些线程创建出来后就会不断检测任务队列,从任务队列当中拿出任务进行处理。

现在当服务进程调用accept函数获取到一个连接请求后,就会根据该客户端的套接字、IP地址以及端口号构建出一个任务,然后调用线程池提供的Push接口将该任务塞入任务队列

这实际也是一个生产者消费者模型,其中服务进程就作为了任务的生产者,而后端线程池当中的若干线程就不断从任务队列当中获取任务进行处理,它们承担的就是消费者的角色,其中生产者和消费者的交易场所就是线程池当中的任务队列。

    void Start()                                                           {                                                                      _tp->ThreadPoolInit();                                               while(true)                                                          {                                                                    // accept                                                          struct sockaddr_in peer;                                           memset(&peer , '\0' , sizeof(peer));                               socklen_t len = sizeof(peer);                                      int sock = accept(_sockfd , (struct sockaddr*)&peer , &len);       if (sock < 0)                                                      {                                                                  cout << "accept error" << endl;                                  continue;                                                        }                                                                                                                                           E>      Task task(port);                                                   _tp->Push(task);    }                                                                   }  

设计任务类

现在我们要做的就是设计一个任务类,该任务类当中需要包含客户端对应的套接字、IP地址、端口号,表示该任务是为哪一个客户端提供服务,对应操作的套接字是哪一个。

此外,任务类当中需要包含一个Run方法,当线程池中的线程拿到任务后就会直接调用这个Run方法对该任务进行处理,而实际处理这个任务的方法就是服务类当中的Service函数,服务端就是通过调用Service函数为客户端提供服务的。

我们可以直接拿出服务类当中的Service函数,将其放到任务类当中作为任务类当中的Run方法,但这实际不利于软件分层。我们可以给任务类新增一个仿函数成员,当执行任务类当中的Run方法处理任务时就可以以回调的方式处理该任务。

Handler类

  class Handler{Handler() = default;void operator()(int sock){cout << "get a new linl :  " << sock  << endl;char buff[1024];                                                                                                                                                   while(true)     {                          ssize_t size = read(sock , buff , sizeof(buff) - 1);if (size > 0)               {buff[size] = 0; // '\0'cout << buff << endl;write(sock , buff , size);   }else if (size == 0){       cout << "read close" << endl;break;                                                                                                                                  }else{         cout << "unknown error" << endl;      }}close(sock);cout << "Service end sock closed" << endl;}};

Task类

#pragma once     
#include "sever.cc"    
#include <iostream>    
using namespace std;    
class Task    
{    private:    int _sock;    Handler _handler;    public:    Task(int sock)    :_sock(sock)                                                                                                                                  {}    Task() = default;   void run()    {    _handler(_sock);    }    
};  

这样子我们线程池版本的TCP网络程序就基本完成了

下面是运行结果

在这里插入图片描述

相关文章:

【Hello Network】网络编程套接字(三)

作者&#xff1a;小萌新 专栏&#xff1a;网络 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;简单介绍下各种类型的Tcp协议 各种类型Tcp服务器 多进程版的TCP网络程序捕捉SIGCHLD信号让孙子进程执行任务 多线程TCP网络程序线程池版多线程TCP网络…...

3.4 只读存储器

学习目标&#xff1a; 学习只读存储器&#xff08;ROM&#xff09;的目标可以包括以下内容&#xff1a; 了解ROM的基本概念、分类以及适用场景。掌握ROM的电路原理、逻辑结构和读取方式。熟悉ROM的编程方式和编程工具。理解ROM与EPROM、EEPROM和闪存的区别和联系。了解ROM在计…...

从后端开发转大数据开发怎么样?

很多做后端的小伙伴&#xff0c;在某一个瞬间&#xff0c;都想转行大数据&#xff0c;那这种想法可行嘛&#xff1f; 转大数据的最初原因很简单&#xff0c;就是好几个同事都转了&#xff0c;他们的收入瞬间提高了好多&#xff0c;于是在同事的内推我也就跟着转了&#xff0c;…...

编程式导航路由跳转到当前路由(参数不变),多次执行会抛出NavigatorDuplicated的禁告错误?

重写push与replace方法 编程式导航路由跳转到当前路由&#xff08;参数不变&#xff09;&#xff0c;多次执行会抛出NavigatorDuplicated的禁告错误&#xff1f; 路由跳转有俩种形式&#xff1a;声明式导航&#xff0c;编程式导航 声明式导航没有这类问题的&#xff0c;因为…...

AppArmor无内核及系统日志的问题及解决

在AppArmor中&#xff0c;正常情况下&#xff0c;一旦违反了规则&#xff0c;是能够在内核及系统日志中看到相关信息的。比如&#xff1a;在Ubuntu下正常产生的日志信息&#xff08;示例&#xff09;如下&#xff1a; kernel: [140321.028000] audit(1191433716.584:1578): t…...

本地更改配置ssh密钥和更改github网址

配置 SSH 密钥以进行身份验证&#xff0c;可以遵循以下步骤&#xff1a; 生成SSH密钥 打开 Git Bash 终端 在 Windows 上&#xff0c;可以打开 Git Bash 终端。通常&#xff0c;可以在开始菜单中搜索 Git Bash 并启动它。一旦打开了 Git Bash 终端&#xff0c;将进入一个基于…...

MATLAB函数封装2:QT调用封装函数

在利用MATLAB进行封装函数之后&#xff0c;最主要的目的是对函数进行调用&#xff0c;能够对矩阵运算和其他算法的运行进行快捷处理。 在有了MATLAB函数之后封装成DLL文件之后&#xff0c;在QT中添加动态链接库&#xff0c;就可以实现函数的调用过程&#xff0c;这个过程相对简…...

AJAX和JSON

1、什么是AJAX? AJAX&#xff08;ASynchronous JavaScript And XML&#xff09;异步的JavaScript 和 XML&#xff1b; 由Jesse James Garrett 在他的文章AJAX&#xff1a;A New Approoch to Web Applications中首次提出。 ajax&#xff08;Web数据交互方式&#xff09;_百…...

源码:SharedPreferences分析

一、持久化方式&#xff1a; DataStore&#xff1a;稳定性 MMKV&#xff1a;效率 SharedPreferneces 区别&#xff1a; 功能MMKVJetpack DataStoreSharedPreferneces是否阻塞主线程否否是是否线程安全是 是 是是否支持跨进程是否否是否类型…...

大二一个学期学这么点内容,没有概念,只有实操

如何查看所有的数据库&#xff1a; Show databases; 如何进入某个数据库&#xff1a; use xxx; 如何新进数据库&#xff1a; Create database jx; 如何删除数据库&#xff1a; Drop database jx; 如何查看所有的表格&#xff1a; Show tables; 如何创建数据表&#xf…...

AppWeb 身份验证绕过漏洞 (CVE-2018-8715)

当前漏洞环境部署在vulhub,当前验证环境为vulhub靶场&#xff08;所有实验均为虚拟环境&#xff09; 实验环境&#xff1a;攻击机----kali 靶机&#xff1a;centos7 1、进入靶场&#xff0c;启动环境 2、访问AppWeb控制台&#xff1a;http://your-ip:8080 使用用户名、密码adm…...

为什么监控摄像头画面不如手机拍摄视频画面清晰

一天和一个做餐饮的朋友吃饭聊天&#xff0c;他提出一个问题&#xff0c;几百块的监控摄像头就是纯粹做监控功能 &#xff0c;视频拍摄的画面为什么还没有几百元的手机拍摄的视频画面清晰&#xff0c;对于此特意查了一下技术资料&#xff0c;整理一下&#xff0c;以备下次再详细…...

EU GMP附录一与关键区域空气微生物取样方案及相关法规标准解读

2022版EU GMP附录一与关键区域空气微生物取样方案疑问解答 3月30日2022版EU GMP附录一与关键区域空气微生物取样方案网络研讨会期间&#xff0c;我们收集到了部分参会听众针对该主题所提出的常见问题。根据以下这些问题&#xff0c;lighthouse微生物应用专家将来为您答疑解惑。…...

【软件测试】自动化测试日志问题该怎么解决?测试老鸟总结方案...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…...

快速响应 智慧应急|大势智慧亮相第三届武汉国际安全应急博览会

4月26日至4月28日&#xff0c;第三届武汉国际安全应急博览会&#xff08;后简称“应博会”&#xff09;在湖北武汉顺利举办。本次展会&#xff0c;大势智慧以实时三维重建能力为核心&#xff0c;提供各类应急场景的技术支撑&#xff0c;助力应急处置和救援等方面的应用。 展会…...

MySQL数据库——MySQL DELETE:删除数据

在 MySQL 中&#xff0c;可以使用 DELETE 语句来删除表的一行或者多行数据。 删除单个表中的数据 使用 DELETE 语句从单个表中删除数据&#xff0c;语法格式为&#xff1a; DELETE FROM <表名> [WHERE 子句] [ORDER BY 子句] [LIMIT 子句] 语法说明如下&#xff1a; …...

管家婆安装导致电脑蓝屏问题解决方案

安装完管家婆后&#xff0c;电脑蓝屏&#xff0c;重启还是蓝屏&#xff0c;这该怎么办&#xff1f; 导致的原因&#xff1a;因加密狗驱动不适配于Windows10系统&#xff0c;导致电脑蓝屏 修复方案&#xff1a;进入电脑安全模式&#xff08;怎么进入问度娘&#xff09;&#…...

Compiler Lab1- 自制词法分析器

由于编译原理课的Lab1为自制词法分析器&#xff0c;所以笔者用C实现了一个极简的C语言词法分析器&#xff0c;用于分析C语言源代码。它可以处理关键字、标识符、整数、实数、浮点数的科学计数法表示、运算符、分隔符、字符串字面量、字符字面量、注释和预处理指令。请注意&…...

构建API的战斗——与来自Kong的Marco Palladino的问答

Kong是一个开源的API网关&#xff0c;可用于管理、安全性和监视微服务和API的所有流量。以下是Kong官方网站的介绍&#xff1a; Kong是一个云原生、快速、可扩展的分布式微服务抽象层&#xff08;也称为API网关、API中枢、API发布器或API服务的网关&#xff09;。 Kong即可充当…...

华为OD机试 - 对称美学(Python)

题目描述 对称就是最大的美学,现有一道关于对称字符串的美学。已知: 第1个字符串:R 第2个字符串:BR 第3个字符串:RBBR 第4个字符串:BRRBRBBR 第5个字符串:RBBRBRRBBRRBRBBR 相信你已经发现规律了,没错!就是第 i 个字符串 = 第 i - 1 号字符串取反 + 第 i - 1 号字符…...

argparse.ArgumentParser

文章目录 argparse.Namespace() Python参数解析工具argparse.ArgumentParser()和实例详解 创建解析器 parserargparse.ArgumentParser() 添加参数 parser.add_argument(name or flags…[, action][, nargs][, const][, default][, type][, choices][, required][, help][, meta…...

大数据Doris(五):FE 扩缩容

文章目录 FE 扩缩容 一、通过MySQL客户端连接Doris 二、FE Follower扩缩容 1、准备 FE 安装包...

react相关概念

真实DOM和虚拟DOM区别 react关于虚拟DOM和真实DOM 虚拟DOM比较“轻”&#xff0c;真实DOM比较“重”&#xff0c;因为虚拟DOM是React在用&#xff0c;无需真实DOM上那么多属性 虚拟DOM最终一定会转为真实DOM放入页面 JSX JSX: 全称JavsScript XML 是react定义的一种类似于XM…...

计算机的硬件系统的组成

微型计算机是指一种体积小、功能强大的计算机系统&#xff0c;通常用于个人或小型企业的日常办公、娱乐等需求。微型计算机的硬件系统主要由以下几个部分组成&#xff1a; 一、中央处理器&#xff08;CPU&#xff09; 中央处理器&#xff0c;简称CPU&#xff08;Central Proc…...

Python基础-列表元组

列表元组 列表元组的操作符 len在列表元组中的使用 len函数可以计算除数字类型之外,其他所有数据类型的长度 列表(元组)之间的累加与乘法 两个列表相加可以使用同一个列表多次累加可以使用* in和not in在列表(元组)中的用法 in用于判断某个成员(元素)是否在该数据结构中…...

【校招VIP】拿到offer就躺平?转正前需要知道的这些事儿...

现在春招基本上结束了&#xff0c;拿到offer的同学就觉得可以直接躺平了。 但是拿到offer只是我们取经路上九九八十一难的第一关&#xff0c;后面还有很多的关卡等着考验我们。 近些年来在实习期间或者试用期间&#xff0c;无法转正的例子比比皆是&#xff0c;令人心动的offe…...

考研拓展:汇编基础

一.说明 本篇博客是基于考研之计算机组成原理中的程序机器级代码表示进行学习的&#xff0c;并不是从汇编语言这一门单独的课程来学习的&#xff0c;涉及的汇编语言知识多是帮助你学习考研之计算机组成原理中对应的考点。 二.相关寄存器 1.相关寄存器 X86处理器中有8个32位…...

10 【Sass语法介绍-继承】

1.前言 在我们编写样式的时候&#xff0c;很多情况下我们几个不同的类会有相同的样式代码&#xff0c;同时这几个类又有其自己的样式代码&#xff0c;这使我们就可以通过 Sass 提供的继承 extend 来实现。本节内容我们将讲解 Sass 继承的语法以及继承的多重延伸等等&#xff0…...

魔兽worldserver.conf 服务端配置文件说明

魔兽worldserver.conf 服务端配置文件说明 我是艾西&#xff0c;今天把很多小伙伴需要的魔兽worldserver.conf 服务端配置文件说明分享给大家&#xff0c;大家可以自己研究参考下 worldserver.conf 这个文件是服务端的配置文件&#xff0c;可以在这里做很多个性化修改 注意&a…...

关于电信设备进网许可制度若干改革举措的通告

Q&#xff1a;3月1日后&#xff0c;不再实行进网许可管理的11种电信设备是否还需要继续申请和使用标志&#xff1f; A&#xff1a;3月1日起&#xff0c;对不再实行进网许可管理的11种电信设备停止核发进网许可标志&#xff0c;已申请的标志可在证书有效期内继续使用。 Q&#…...