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

秒懂Linux之Socket编程(四)

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

代码前身

多线程远程命令执行

命令处理

疑难杂症

基本框架 

添加指令判断函数

最终命令处理

Main主函数

safe.txt

实现效果

全部代码


 

代码前身

verson2:多线程&&线程池 · 516aa13 · 玛丽亚后/keep - Gitee.com

多线程远程命令执行

基本流程:由客户端发送命令,然后服务端接收并执行命令~

当前我们是把处理命令的业务交给回调函数去执行,服务端只需要读取命令即可~

命令处理

接下来我们来对命令方面的处理进行封装~

疑难杂症

但在学习前我将提出几点疑问~

按照我的逻辑客户端发送命令,服务端接收并读取命令后将命令重定向到输入端相当于执行命令,这样就够了吧~

但真正的情况是有两种方法:

第一种:1.fork&&pipe  2.dup(管道重定向)3.exec(执行外部命令函数)  3.dup  

其一:为什么创建子进程,万一该命令是杀死进程那服务端岂不是被中断服务了,所以创建子进程可以保证服务端不会因为其他异常而退出,因为父子进程之间是独立的。

其二:为什么要创建管道,通过管道,父进程可以捕获子进程的标准输出(stdout),从而获取外部命令的执行结果。父进程读取命令,创建子进程去执行命令,子进程执行结果通过管道返回给父进程,父进程拿到结果再返回给客户端~

但这种方法有一个问题:命令本身就是要创建进程,而我们的背景又是多线程~这样就会出现线程去创建进程的结果,不太推荐~

第二种:popen:底层自动建立管道与子进程 

  • popen 会返回一个 FILE * 类型的指针,这个指针可以用于读取子进程的输出(如果模式为 "r")或向子进程发送输入(如果模式为 "w")。

使用popen函数只需要知道它默认让父进程去读取子进程执行的结果,我们只要确保父进程成功接收命令即可,其他事情不用我们担心,它会内部实现,我们等待结果就好了~

基本框架 

//以空格作分隔符
const std::string sep = " ";

class Command
{
public:
    Command()
    {}

    //对命令的处理
    std::string Excute(const std::string &cmd) // ls -a -l
    {
        //popen :由底层自动去建立管道与子进程 再通过file* 去读取cmd执行结果
        FILE* fp = popen(cmd.c_str(),"r");
        if(fp==nullptr)
        {
            return "failed";
        }
        std::string result;
        //作为读取命令的缓冲区
        char buffer[1024];
        while(fgets(buffer,sizeof(buffer),fp)!=NULL)
        {
            //把最终命令读取并记录到result中
            result+=buffer;
        }
        pclose(fp);
        return result;
    }

    ~Command()
    {}


private:
};

客户端启动参考

int main(int argc, char *argv[])
{
    Command cmd;
    std::string res = cmd.Excute("ls -a -l");
    std::cout<<res;
    return 0;
}

接下来我们要把一些安全指令加载进Command中,只有识别到是安全命令才会去执行

private:
    //加载安全指令到Command中
    void LoadConf(const std::string &conf)
    {
        //打开路径下的安全指令配置文件
        std::ifstream in(conf);
        if (!in.is_open())
        {
            LOG(FATAL, "open %s error\n", conf.c_str());
            return;
        }
        std::string line;
        while (std::getline(in, line))
        {
            LOG(DEBUG, "load command [%s] success\n", line.c_str());
            _safe_cmd.insert(line);
        }
        in.close();
    }

添加指令判断函数

 //安全指令判断
    bool SafeCheck(const std::string & cmd)
    {
        //验证当前指令是否为已收录的安全指令
        auto iter = _safe_cmd.find(cmd);
        if(iter==_safe_cmd.end())
        {
            return false;
        }
        return true;
    }

最终命令处理

 // 对命令的处理
    std::string Excute(const std::string &cmd) // ls -a -l
    {
        // 检查是否为安全指令
        std::string result;
        if (SafeCheck(cmd))
        {
            // popen :由底层自动去建立管道与子进程 再通过file* 去读取cmd执行结果
            //父进程接收指令,子进程执行
            FILE *fp = popen(cmd.c_str(), "r");
            if (fp == nullptr)
            {
                return "failed";
            }
            std::string result;
            // 作为读取指令结果的缓冲区
            char buffer[1024];
            //按行读取
            while (fgets(buffer, sizeof(buffer), fp) != NULL)
            {
                // 把最终命令读取并记录到result中
                result += buffer;
            }
            pclose(fp);
            return result;
        }
        else
        {
            result = "非法指令\n";
        }
        return result;
    }

Main主函数

void Usage(std::string proc)
{
    std::cout << "Usage:\n\t" << proc << " local_port\n" << std::endl;
}

// ./tcpserver port
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    EnableScreen();
    uint16_t port = std::stoi(argv[1]);
    //初始化配置文件路径
    Command cmd("./safe.txt");
    //绑定命令处理接口,服务端只要接收到客户端的指令立马通过调用函数去调用该接口
    func_t cmdExec = std::bind(&Command::Excute, &cmd, std::placeholders::_1);
    std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port, cmdExec);
    tsvr->InitServer();
    tsvr->Loop();
    return 0;
}

safe.txt

ls -a -l
pwd
tree
whoami
who
uname
cat
touch

实现效果

不过也有太死板的地方,我们再来改进一下~

std::string PrefixCommand(const std::string &cmd)
    {
        if (cmd.empty())
            return std::string();
        auto pos = cmd.find(sep);
        if (pos == std::string::npos)
            return cmd;
        else
            return cmd.substr(0, pos);
    }
    // 安全指令判断
    bool SafeCheck(const std::string &cmd)
    {
        std::string prefix = PrefixCommand(cmd);
        // 验证当前指令是否为已收录的安全指令
        auto iter = _safe_cmd.find(prefix);
        if (iter == _safe_cmd.end())
        {
            return false;
        }
        return true;
    }

全部代码

远程命令服务 · dfdd1d1 · 玛丽亚后/keep - Gitee.com


http://www.kler.cn/a/376484.html

相关文章:

  • go的反射
  • Spring Boot 与 Vue 共铸卓越采购管理新平台
  • Android @JvmOverloads
  • 【Hello World 】
  • 电阻的颜色有什么含义
  • AI 原生时代,更要上云:百度智能云云原生创新实践
  • 支持向量机SVM与自然语言处理基础小结
  • 2024.10.2校招 实习 内推 面经
  • 函数基础,定义与调用。作用域,闭包函数
  • 升序数组两两不相等
  • C语言稀有关键词:柔性数组
  • 【创建型】单例模式
  • Hypack 应用于地形测量的高级设置操作要领
  • Nginx 的 Http 模块介绍(上)
  • Git - 两种方式撤销已提交到远端仓库的记录并删除提交记录
  • vite 创建了一个项目后,如何实现工程化
  • 【赵渝强老师】Memcached集群的架构
  • 数据结构,问题 G: 字符串匹配问题
  • Spring Boot接收参数的19种方式
  • DiffusionDet: Diffusion Model for Object Detection—用于对象检测的扩散模型论文解析
  • Vite常用插件配置
  • R学习笔记-单因素重复测量方差分析
  • 032_Tiledlayout_in_Matlab中的分块图布局
  • C++/Opengl编程实践
  • 代码随想录一刷——350.两个数组的交集II
  • 024集——CAD 动态显示图形——ed.Redraw(ent)实现(CAD—C#二次开发入门)