进程间通信—进程池设计

news2025/1/19 14:22:26

进程池设计

文章目录

  • 进程池设计
      • 代码目的
        • 头文件
    • 对子进程操作
      • 建立子进程对象并把子进程对象放进数组里
      • 建立子进程需要执行的任务表
      • 创建子进程和父进程通信的管道,并且让子进程阻塞读取
      • 对父进程操作
      • 回收子进程
      • 整体代码
      • 子进程具有读端未关闭的bug

代码目的

创建一个父进程和多个子进程,父子进程之间通过匿名管道完成进程间通信。让父进程作为写端,子进程作为读端,父进程随机给任意一个子进程写入数据让子进程完成相应任务。

头文件

#include<iostream>
#include<vector>
#include<unistd.h>
#include<string.h>
#include<cassert>
#include<ctime>
#include<sys/types.h>
#include<sys/wait.h>

对子进程操作

建立子进程对象并把子进程对象放进数组里

//创建子进程对象
class  SubEp//endpoint---子进程对象
{

public:
SubEp(pid_t subid,int writefd)//第一个参数是子进程的pid,第二个参数是该子进程读端对于父进程的写端fd
:_subid(subid)
,_writefd(writefd)
{
   char namebuffer[1024];
   //第一个参数是表示第几号子进程,第二个参数是子进程的pid,第三个参数是该子进程读端对于的父进程的写端
   snprintf(namebuffer,sizeof namebuffer,"process: %d [pid(%d) - fd(%d)]",num++,_subid,_writefd);
   _name=namebuffer;
}
public:
static int num;
string _name;
pid_t _subid;
int _writefd;//该子进程与父进程匿名管道对于的父进程的写端fd
};
int SubEp::num=0;
  • 子进程对象需要传递两个参数来初始化成员变量_subid和 _writefd。一是子进程的pid二是该子进程读端对应父进程写端的文件描述符fd

  • 成员变量num表示是第几个创建出来的子进程,第一个创建出来的子进程为0,使用后++后续子进程的num依次是1,2等等。因此num不能由于出了SubEp对象作用域后被销毁,所以定义为static,变量num生命周期取决于SubEp类的生命周期

  • 成员变量 _name用namebuffer初始化,用来标识该子进程的其他成员变量

建立子进程需要执行的任务表

//创建父进程给子进程派发的任务列表
typedef void(*func_t)();//函数指针类型,函数返回值为void

void downloadTask()//模拟下载任务
{
    cout<<getpid()<<": 下载任务\n"<<endl;
    sleep(1);
}

void fflushTask()//模拟刷新任务
{
    cout<<getpid()<<": 刷新任务\n"<<endl;
    sleep(1);
}

void subscribeTask()//模拟订阅任务
{
    cout<<getpid()<<": 订阅任务\n"<<endl;
    sleep(1);
}
//把上面的三种任务load到列表中即让存放函数指针的vector的各个指针能够指向上面的函数,为了后面方便调用
void loadTaskFunc(vector<func_t>*out)
{
    assert(out);//vector创建成功
    out->push_back(downloadTask);
    out->push_back(fflushTask);
    out->push_back(subscribeTask);
}
  • 子进程需要执行的任务都是函数对象,建立一个对象是函数指针的数组out,通过loadTaskFunc函数把任务函数尾插到数组out里,然后通过输出型参数返回。

创建子进程和父进程通信的管道,并且让子进程阻塞读取

void CreateSubProcesses( vector<SubEp>*subs,vector<func_t>& funcMap)
{
    vector<int> deleteFd;
//创建子进程并且创建好父进程与各个子进程通信的管道
int fds[2];
for(size_t i=0;i<PROCESS_NUM;i++)//创建子进程
{
        int n=pipe(fds);//建立父子间进程的匿名管道--建立成功返回0,建立失败返回-1
        assert(n==0);//判断管道是否建立成功
        (void)n;

        pid_t id=fork();//创建子进程
        if(id==0)
        {
             for(size_t i=0;i<deleteFd.size();i++) close(deleteFd[i]);//因为有写实拷贝,所以这里关闭不会影响父进程
//因为子进程会继承父进程文件描述符表,所以上一个子进程的读端对应的父进程的写端这个进程也会继承下来,即当前子进程和上一个子进程之间也有匿名管道
//可能会导致上一个子进程的父进程读端关闭,而此时还有当前这个子进程的读端连接着上一个子进程,使得上一个子进程不能正常关闭读端而造成bug
//所以要手动关闭当前子进程对应上一个子进程的读端的写端。
            close(fds[1]);//关闭子进程的写端-保留读端负责读
           //对子进程操作
     while(true)
     {
        //1. 获取任务码,让子进程阻塞等待父进程写写入的任务码,
        int taskcode=receiveTask(fds[0]);
        //2.完成任务--调用对应任务码的函数
        if(taskcode>=0 && taskcode<funcMap.size())
         funcMap[taskcode]();//调用函数完成任务
        else if(taskcode==-1)  break;
     }
            exit(0);//子进程退出
        }
    //这里往后是父进程语句
  //写端关闭,读端读到0然后读端自己关闭
        close(fds[0]);//关闭当前子进程与父进程相联系的匿名管道的父进程的读端
        SubEp sub(id,fds[1]);//第一个参数传的是子进程的pid,第二个参数传的是此时子进程读端对于的父进程的写端
        subs->push_back(sub);
        deleteFd.push_back(fds[1]);//记录当前的写端供下个子进程用

}
  • 在函数CreateSubProcesses内,先建立父进程相连的匿名管道,然后创建子进程,子进程也拷贝了一份父进程的文件描述符表,能通过文件描述符连接到匿名管道,因此父子进程通信的管道建立完成。

  • 在父进程语句中,需要注意的是,通过传参数子进程的pid和此时子进程读端对于的父进程的写端fd给SubEP类构建子进程对象,并且将对象放进数组subs里。

  • 在子进程的语句中,通过receiveTask函数获取任务码

int receiveTask(int readfd)
{
    int retcode=0;//返回任务码
    ssize_t s= read(readfd,&retcode,sizeof(retcode));//从读端读出来的任务码放到retcode里
    cout<<"process has read the TaskCode: "<<retcode<<endl;
    if(s==sizeof(int)) return retcode;
    else if(s<=0)return -1;
    else return 0;
}
  • 让子进程在receiveTask函数中阻塞读取管道里的数据。前提已知父进程往匿名管道写入整数数据,数据范围为[0,任务个数-1]即任务数组对应的下标范围,子进程把读取到的数据存到变量retcode里,然后判断retcode是否是整数数据大小,如果是就返回数据给上层CreateSubProcesses函数,如果不是就返回-1。
  • 当变量taskcode接收到receiveTask函数返回的任务码时,如果任务码符合范围[0,任务个数-1]即父子进程按照我们的意愿通信正常,然后子进程拿着任务码调用funcMap数组执行任务;但如果接收的返回值是-1,则是父子进程间通信不正常,直接退出判断语句。

这里提到的子进程操作主要是子进程阻塞读取父进程写入的数据,还有子进程拿到数据执行任务。

对父进程操作

void loadBalanceContrl(const vector<SubEp>& subs,const vector<func_t> &funcMap,int comcode)
{
    int processnum=subs.size();//子进程的个数
    int tasknum=funcMap.size();//任务的个数
    bool numoftime=(comcode==0?true:false);//若命令码是0则一直运行,若命令码为正数x,则允许x次后退出
    while(true)
    {
   //rand()为伪随机数
   //1.找到哪一个子进程
   int subIndex=rand()%processnum;
    //2.找到哪一个执行哪一个任务
   int taskIndex=rand()%tasknum;
    //3.任务发送给选择的进程
     sendTask(subs[subIndex],taskIndex);//第一个参数传第几个子进程,第二个参数传第几个任务
     sleep(1);
  if(!numoftime)
  {
    comcode--;
    if(comcode==0)
    break;
  }
    }
    //走到这里则是父进程给子进程通信完了,需要逐个关闭子进程读端对于的写端--倒退关解决bug
    for(size_t i=0;i<subs.size();i++)
    {
      close(subs[i]._writefd);
      cout<<"close process: [ "<<i<<" ]'s writeeop"<<endl;
    }


}
  • loadBalanceContrl函数需要main函数传入子进程数组subs,任务数组funcMap和命令码comcode。comcode用来指定父进程发送多少次数据给子进程即子进程需要执行多少次任务

  • numoftime用来鉴别父进程需要写入多少次数据,当comcode为0时则numoftime为真,则父进程死循环往匿名管道里写数据;若命令码为正数x为非0,则numoftime为假,则父进程往匿名管道里写x次数据。

  • 通过sendTask函数让父进程选择指定的子进程,写入指定的任务码到匿名管道中

void sendTask(const SubEp& process, int tasknum)
{
    cout<<"send Task num: "<<tasknum<<" to the process: "<<process._name<<endl;//打印日志:任务几发送给几号子进程
    ssize_t n=write(process._writefd,&tasknum,sizeof(tasknum));//该子进程读端对于的写端往管道里写入任务几-4个字节的数据
    assert(n==sizeof(int));//判断写入的数据是否是4个字节
    (void)n;
}
  • 父子间进程通信完之后,按照子进程创建时间从先往后依次关闭子进程读端对应的父进程的写端。

回收子进程

void waitProcess(const vector<SubEp>& processes)
{
    for(size_t i=0;i<processes.size();i++)
    {
        waitpid(processes[i]._subid,nullptr,0);
        cout<<"wait success for process: "<<processes[i]._subid<<endl;
    }
}
  • 按照子进程创建时间从先往后依次回收子进程。

整体代码

#include<iostream>
#include<vector>
#include<unistd.h>
#include<string.h>
#include<cassert>
#include<ctime>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
#define PROCESS_NUM 3
#define MakeSeed() srand((unsigned long)time(nullptr)^getpid()^rand()%1234)//建立伪随机数种子
//创建父进程给子进程派发的任务列表
typedef void(*func_t)();//函数指针类型,函数返回值为void

void downloadTask()//模拟下载任务
{
    cout<<getpid()<<": 下载任务\n"<<endl;
    sleep(1);
}

void fflushTask()//模拟刷新任务
{
    cout<<getpid()<<": 刷新任务\n"<<endl;
    sleep(1);
}

void subscribeTask()//模拟订阅任务
{
    cout<<getpid()<<": 订阅任务\n"<<endl;
    sleep(1);
}
//把上面的三种任务load到列表中即让存放函数指针的vector的各个指针能够指向上面的函数,为了后面方便调用
void loadTaskFunc(vector<func_t>*out)
{
    assert(out);//vector创建成功
    out->push_back(downloadTask);
    out->push_back(fflushTask);
    out->push_back(subscribeTask);

}

//创建子进程对象
class  SubEp//endpoint---子进程对象
{

public:
SubEp(pid_t subid,int writefd)//第一个参数是子进程的pid,第二个参数是该子进程读端对于父进程的写端fd
:_subid(subid)
,_writefd(writefd)
{
   char namebuffer[1024];
   //第一个参数是表示第几号子进程,第二个参数是子进程的pid,第三个参数是该子进程读端对于的父进程的写端
   snprintf(namebuffer,sizeof namebuffer,"process: %d [pid(%d) - fd(%d)]",num++,_subid,_writefd);
   _name=namebuffer;
}
public:
static int num;
string _name;
pid_t _subid;
int _writefd;//该子进程与父进程匿名管道对于的父进程的写端fd
};
int SubEp::num=0;

int receiveTask(int readfd)
{
    int retcode=0;//返回任务码
    ssize_t s= read(readfd,&retcode,sizeof(retcode));//从读端读出来的任务码放到retcode里
    cout<<"process has read the TaskCode: "<<retcode<<endl;
    if(s==sizeof(int)) return retcode;
    else if(s<=0)return -1;
    else return 0;
}

void CreateSubProcesses( vector<SubEp>*subs,vector<func_t>& funcMap)
{
    vector<int> deleteFd;
//创建子进程并且创建好父进程与各个子进程通信的管道
int fds[2];
for(size_t i=0;i<PROCESS_NUM;i++)//创建子进程
{
        int n=pipe(fds);//建立父子间进程的匿名管道--建立成功返回0,建立失败返回-1
        assert(n==0);//判断管道是否建立成功
        (void)n;

        pid_t id=fork();//创建子进程
        if(id==0)//子进程进入判断语句
        {
            for(size_t i=0;i<deleteFd.size();i++) close(deleteFd[i]);//因为有写实拷贝,所以这里关闭不会影响父进程
//因为子进程会继承父进程文件描述符表,所以上一个子进程的读端对应的父进程的写端这个进程也会继承下来,即当前子进程和上一个子进程之间也有匿名管道
//可能会导致上一个子进程的父进程读端关闭,而此时还有当前这个子进程的读端连接着上一个子进程,使得上一个子进程不能正常关闭读端而造成bug
//所以要手动关闭当前子进程对应上一个子进程的读端的写端。
            close(fds[1]);//关闭子进程的写端-保留读端负责读
           //对子进程操作
     while(true)
     {
        //1. 获取任务码,让子进程阻塞等待父进程写写入的任务码,
        int taskcode=receiveTask(fds[0]);
        //2.完成任务--调用对应任务码的函数
        if(taskcode>=0 && taskcode<funcMap.size())
         funcMap[taskcode]();//调用函数完成任务
        else if(taskcode==-1)  break;
     }
            exit(0);
        }
  //写端关闭,读端读到0然后读端自己关闭
        close(fds[0]);//关闭当前子进程与父进程相联系的匿名管道的父进程的读端
        SubEp sub(id,fds[1]);//第一个参数传的是子进程的pid,第二个参数传的是此时子进程读端对于的父进程的写端
        subs->push_back(sub);
       deleteFd.push_back(fds[1]);//记录当前的写端供下个子进程用

}
}
void sendTask(const SubEp& process, int tasknum)
{
    cout<<"send Task num: "<<tasknum<<" to the process: "<<process._name<<endl;//打印日志:任务几发送给几号子进程
    ssize_t n=write(process._writefd,&tasknum,sizeof(tasknum));//该子进程读端对于的写端往管道里写入任务几-4个字节的数据
    assert(n==sizeof(int));//判断写入的数据是否是4个字节
    (void)n;
}

void loadBalanceContrl(const vector<SubEp>& subs,const vector<func_t> &funcMap,int comcode)
{
    int processnum=subs.size();//子进程的个数
    int tasknum=funcMap.size();//任务的个数
    bool numoftime=(comcode==0?true:false);//若命令码是0则一直运行,若命令码为正数x,则允许x次后退出
    while(true)
    {
        //rand()为伪随机数
   //1.找到哪一个子进程
   int subIndex=rand()%processnum;
    //2.找到哪一个执行哪一个任务
   int taskIndex=rand()%tasknum;
    //3.任务发送给选择的进程
     sendTask(subs[subIndex],taskIndex);//第一个参数传第几个子进程,第二个参数传第几个任务
     sleep(1);
  if(!numoftime)
  {
    comcode--;
    if(comcode==0)
    break;
  }
    }
    //走到这里则是父进程给子进程通信完了,需要逐个关闭子进程读端对于的父进程写端
    for(size_t i=0;i<subs.size();i++)
    {
      close(subs[i]._writefd);
      cout<<"close process: [ "<<i<<" ]'s writeeop"<<endl;
    //    waitpid(subs[i]._subid,nullptr,0);
    //     cout<<"wait success for process: "<<subs[i]._subid<<endl;
    }
}

void waitProcess(const vector<SubEp>& processes)
{
    for(size_t i=0;i<processes.size();i++)
    {
        waitpid(processes[i]._subid,nullptr,0);
        cout<<"wait success for process: "<<processes[i]._subid<<endl;
    }
}

int main()
{
    MakeSeed();//建立伪随机数种子

vector<SubEp> subs;//创建子进程对象并将子进程对象放进数组里
vector<func_t> funcMap;//建立一个任务表:父进程写入管道,子进程在管道读取,读取到的数据引导子进程去完成一些任务
loadTaskFunc(&funcMap);

//1.创建子进程并且创建好父进程与各个子进程通信的管道,并且让子进程阻塞等待父进程写入
CreateSubProcesses(&subs,funcMap);

//2.对父进程操作

//父进程给子进程发送命令码,为0则一直运行,为正数x则运行x次后退出
int Runcount=0;
cout<<"请输入需要执行几次任务,输入0则为一直循环执行任务,请输入: ";
cin>>Runcount;
cout<<endl;
//这个函数负责让父进程给子进程发送命令码,让子进程去执行任务,要求子进程做到负载均衡     
loadBalanceContrl(subs,funcMap,Runcount);//第一个参数是子进程列表,第二个参数任务列表,第三个参数是父进程给子进程发送的命令码 

//3.回收子进程
waitProcess(subs);
    return 0;
}

子进程具有读端未关闭的bug

下面通过画图来模拟父进程fork出多个子进程的流程

image-20230522164129612

image-20230522164147020

image-20230522164456853

  • 通过上面的图例可以看到,2号子进程有一个写端与1号子进程的读端通信着。

说明:假设父进程创建管道时文件描述符fd[3]是读端,fd[4]是写端。那么在创建1号子进程时子进程拷贝父进程的文件描述符表,然后再关闭父进程的读端fd[3],关闭子进程的写端fd[4],这样父进程(写端fd[4])和1号子进程(读端fd[3])就构成了进程间通信的管道。

  1. 当父进程在创建2号子进程时,2号子进程也拷贝了一份父进程的文件描述表,此时表上fd[4]写端连着1号子进程fd[3]的匿名管道,那么2号子进程也会继承下来。因为具有写实拷贝,子进程2在fd[3]打开读端并不会影响父进程。此时再次关闭父进程读端fd[3],子进程关闭写端fd[5],因为2号子进程的写端fd[4]存在不会影响与父进程进行通信,所以不会关闭fd[4]。

  2. 那么在后续关闭父进程的写端时,想要的效果是两个子进程的读端都读到0,然后子进程自动关闭读端。然而现实是父进程关闭写端,2号子进程的读端只对应父进程1个写端,那么2号子进程的读端会关闭。而1号子进程的读端对应父进程的写端和2号子进程的写端,当父进程的写端关闭时,匿名管道还与2号子进程写端相连,导致1号子进程的读端不会读到0所以1号子进程的读端不能正常关闭!,也就导致子进程无法正常退出,父进程无法正常回收子进程

得出结论:当父进程创建多个子进程,并且父进程作为写端而多个子进程作为读端从而进行进程间通信时,需要单独把子进程的所有写端都关闭。

这里提供两种方法关闭子进程的所有写端

  1. 方案一:在父进程创建子进程前构建一个vector对象,父进程创建子进程后,把父进程的写端放进vector里。等到父进程创建完下一个子进程时,vector里的写端即是当前子进程读端对应的上一个子进程的写端(有可能不只是一个写端),再把vector里的所有写端关闭即可。

image-20230522202611512

image-20230522203254968

  • 这次实现就是用的这个方案,其实不用也可以,因为当父进程往匿名管道里写完数据时,先把父进程对应各个子进程的写端全部关闭,然后再将全部子进程进行回收,这种顺序不会出现bug;但如果是按照创建子进程时间从旧往新关掉一个父进程的写端,然后立刻等待回收一个相应的子进程的话,会导致出现该子进程读端还有其他子进程的写端通信着,该子进程读端没有读到0导致子进程没有正常退出,那么父进程也就回收不到子进程。
  1. 方案二:在父进程关闭写端,需要所有子进程关闭读端时,依次按照创建的时间从新往旧(从后往前)关闭父进程的写端。由于最后创建的子进程的读端只对应父进程的写端,那么父进程关闭写端时,最后一个子进程的读端读到0正常关闭读端,那么该子进程的文件描述符表也会被关闭,进而该子进程正常退出,从而该子进程连接着前一个子进程的写端也会被关闭;那么轮到下一个子进程时,该子进程的读端也只会对应父进程的写端,父进程关闭写端,子进程读端读到0正常关闭读端,子进程正常退出。

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

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

相关文章

【libdatachannel】1 :cmake+vs2022 构建

libdatachannel libdatachannel 是基于c++17实现的cmake 链接openssl 可以参考【libcurl 】win32 构建 Release版本 修改cmakelist 链接openssl1.1.*构建 OpenSSL 找不到 Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.22621. The CXX compiler identifi…

机试打卡 -01 字母异位词(滑动窗口)

算法小白的代码如下↓ class Solution(object):def findAnagrams(self, s, p):""":type s: str:type p: str:rtype: List[int]"""# 输出列表answer_list[]# p的长度p_lenlen(p)# 索引遍历s的子串for i in range(len(s)):# 最后一次循环if ip_le…

Python对大量表格文件加以数据截取、逐行求差、跨文件合并等处理的方法

本文介绍基于Python语言&#xff0c;针对一个文件夹下大量的Excel表格文件&#xff0c;基于其中每一个文件&#xff0c;首先依据某一列数据的特征截取我们需要的数据&#xff0c;随后对截取出来的数据逐行求差&#xff0c;并基于其他多个文件夹中同样大量的Excel表格文件&#…

【NeRF】(一)NeRF论文学习笔记

文章目录 NeRF学习笔记1 实现过程1.1 相机参数&#xff1a;如何通过不同角度的照片得出输入数据1.2 MLP1.3 体积渲染及离散化1.4 优化点 NeRF学习笔记 概述&#xff1a; 重建&#xff1a;根据目前有的不同角度二维图片&#xff0c;重建三维物体。 用 MLP 网络学 Scene Represe…

《WEB安全漏洞30讲》(第5讲)任意文件上传漏洞

1.任意文件上传漏洞原理 文件上传漏洞,指攻击者利用程序缺陷绕过系统对文件的验证与处理策略将恶意程序上传到服务器并获得执行服务器端命令的能力。 这个漏洞其实非常简单,就是攻击者给服务器上传了恶意的木马程序,然后利用此木马程序执行操作系统命令,从而获得服务器权…

汇编实现点灯

循环亮灯 .text .global _start _start: bl LED1_INIT bl LED1_ON bl delay_1s bl LED1_OFF bl LED2_INIT bl LED2_ON bl delay_1s bl LED2_OFF bl LED3_INIT bl LED3_ON bl delay_1s bl LED3_OFF bl _start /**********LED1点灯PE10**************/ LED1_INIT: 1.通过RC…

UE5实现天际线分析效果

文章目录 1.实现目标2.实现过程2.1 后处理材质2.2 验证测试3.参考资料1.实现目标 UE5中使用CesiumForUnreal插件加载在线的地形影像与OSM建筑数据,再基于后处理材质实现天际线分析效果,GIF动图如下所示: 2.实现过程 依旧是通过边缘检测,得到天际线位置,再通过后处理材质将…

PointGPT 论文解读,点云的自回归生成预训练

PointGPT: Auto-regressively Generative Pre-training from Point Clouds 论文&#xff1a;https://arxiv.org/pdf/2305.11487.pdf 一种将GPT概念扩展到点云的方法&#xff0c;在多个3D点云下有任务中&#xff08;点云分类&#xff0c;part分割等&#xff09;上实现了最先进…

C++中set的用法

博主简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的人。 博主主页&#xff1a;陈童学哦 所属专栏&#xff1a;CSTL 前言&#xff1a;Hello各位小伙伴们好&#xff01;欢迎来到本专栏CSTL的学习&#xff0c;本专栏旨在帮助大家了解…

[SWPUCTF 2021 新生赛] (WEB一)

目录 gift_F12 jicao easy_md5​ caidao include easy_sql easyrce babyrce Do_you_know_http ez_unserialize gift_F12 1.开启环境 2.题目提示f12 ctrlf 搜索flag 得到flag "WLLMCTF{We1c0me_t0_WLLMCTF_Th1s_1s_th3_G1ft} jicao 1.开启环境 2.传参 jso…

MyBatis-Plus中AutoGenerator的详细使用案例

AutoGenerator是什么&#xff1f; AutoGenerator 是 MyBatis-Plus 的代码生成器&#xff0c;通过 AutoGenerator 可以快速生成 Pojo、Mapper、 Mapper XML、Service、Controller 等各个模块的代码 AutoGenerator能干什么&#xff1f; 对于单表而言&#xff0c;几乎是一个全能…

调用百度API实现菜品识别

调用百度API实现菜品识别 1、作者介绍2、百度API菜品识别接口介绍2.1图像识别接口介绍2.2接口使用说明2.2.1请求示例2.2.2请求参数 3、调用百度API流程4、代码实现 1、作者介绍 李延&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向&…

如何禁用网络共享打印?

139端口是为“NetBIOS Session Service”提供的&#xff0c;主要用于提供Windows文件和打印机共享以及Unix中的Samba服务。在Windows中要在局域网中进行文件的共享&#xff0c;必须使用该服务。 开启139端口虽然可以提供共享服务&#xff0c;但是常常被攻击者所利用进行攻击&am…

chatgpt赋能Python-python_chi

Python Chi&#xff1a;掌握Python语言的关键 Python是一种面向对象、直译式计算机程序设计语言&#xff0c;被广泛应用于各种领域。无论是数据分析、人工智能、Web开发、网络爬虫还是科学计算&#xff0c;Python都扮演着重要的角色。而Python Chi则是Python语言中一个重要的方…

HQL函数--打地鼠游戏及WordCount案例分析及实现

1.打地鼠 uid,hit,m 1,1,0 1,2,1 1,3,1 1,4,1 1,5,0 1,6,0 1,7,1 2,1,1 2,2,1 2,3,1 2,4,1 2,5,1 3,1,1 3,2,1 3,3,1 3,4,0 3,5,0 3,6,1 3,7,0 3,8,1 create table tb_ds( uid int , -- 用户名 hit int , -- 第几次打地鼠 m int -- 是否命中 1命中 0 …

Request响应和Response对象

什么是Request响应和Response对象 实际上就是指Request对象和Response对象。 一个get或者post请求实际上就是一些有规律的字符串&#xff0c;Tomcat将这些字符串获取并且封装成一个可操作的对象。 通过Response对象可以设置响应数据&#xff0c;也就是Tomcat想要给浏览器发送…

Redis安全性详解

目录 ​编辑 Redis安全性详解 1.Redis的访问控制措施。 2.Redis的密码认证机制 3.Redis的绑定IP地址 4.Redis的网络ACL 5.Redis的防火墙或网络安全组 Redis安全性详解 Redis是一个快速、开源的内存数据库&#xff0c;被广泛用于缓存、消息传递和数据存储等场景。然而&a…

sql查询每组数据中时间最大的一条

sql查询每组数据中时间最大的一条 前言代码总结 前言 先来了解一下业务需求。博主好久没写过后端了&#xff0c;这一写就直接碰到我知识盲区了 我们简单还原一下&#xff0c;这里使用一个表模拟下 表的字段如下 表的内容如下&#xff0c;我们的需求就是取出相同name的数据中…

五、AOP(1)

一、AOP基本概念 1.什么是AOP 面向切面编程&#xff08;方面&#xff09;&#xff0c;利用AOP可以对业务逻辑的各个部分进行隔离&#xff0c;从而使得业务逻辑各部分之间的耦合度降低&#xff0c;提高程序的可重用性&#xff0c;同时提高了开发的效率。不通过修改源代码方式添…

章节1:Burp Suite基本介绍

章节1&#xff1a;Burp Suite基本介绍 Burp [bɜ:p] Suite [swiːt] 01 Burp Suite基本介绍 官网介绍 https://portswigger.net/ 主要产品 Burp Suite培训和认证&#xff08;Academy&#xff09;研究院&#xff08;Research&#xff09;日报&#xff08;Daily&#xff09;…