繁体   English   中英

无法使用 C++ 中的 Boost 库通过管道发送和执行正确的命令

[英]Cannot send and execute correct command through pipes using Boost library in C++

使用问题中的答案: 使用 boost.process 同时读取和写入孩子的标准输入输出

我重构了代码并使用 Boost 库混合了新方法。 我已经成功地与 Stockfish 建立了管道连接,但这也是我遇到以前从未见过的错误的地方,甚至谷歌也没有帮助。

这是我尝试过的:

#include <stdio.h>
#include <time.h>
#include <string>
#include <memory.h>
#include <unistd.h>
#include <iostream>
#include <stddef.h>
#include <execinfo.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fstream>
#include </usr/local/include/backtrace.h>
#include </usr/local/include/backtrace-supported.h>
#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <boost/process/async.hpp>
#include <vector>
#include <iomanip>
#include <stdlib.h>
#include <string.h>

using namespace std;
namespace bp = boost::process;
using boost::system::error_code;
using namespace std::chrono_literals;

string errDetails = "Error Details: ";

void delay(int number_of_seconds) {
    int ms = 1000 * number_of_seconds;
    clock_t start_time = clock();

    while (clock() < start_time + ms) 
        ;
}

static void full_write(int fd, const char* buf, size_t len) {
    while (len > 0) {
        ssize_t ret = write(fd, buf, len);

        if ((ret == -1) && (errno != EINTR)) {
            break;
        }

        buf += (size_t) ret;
        len -= (size_t) ret;
    }
}

void print_backtrace() {
    static const char start[] = "--------BACKTRACE--------\n\n";
    static const char end[] = "-------------------------\n\n";

    void *bt[1024];
    int bt_size;
    char **bt_syms;
    int i;

    bt_size = backtrace(bt, 1024);
    bt_syms = backtrace_symbols(bt, bt_size);
    full_write(STDERR_FILENO, start, strlen(start));
    full_write(STDERR_FILENO, errDetails.c_str(), strlen(errDetails.c_str()));

    for (i = 1; i < bt_size; i++) {
        size_t len = strlen(bt_syms[i]);
        full_write(STDERR_FILENO, bt_syms[i], len);
        full_write(STDERR_FILENO, "\n", 1);
    }

    full_write(STDERR_FILENO, end, strlen(end));

    free(bt_syms); 
}

void abort_application() {
    size_t memLeakCount, staticMemLeakCount;
    uint64_t memLeakSize, staticMemLeakSize;

    for (int i = 0; i < 3; i++) {
        /**
         * Delay
         */
        delay(1);
    }

    print_backtrace();

    abort();
}

inline bool stockfish_check_exists(const std::string& name) {
    struct stat buffer;
    return (stat(name.c_str(), &buffer) == 0);
}

int main() {
    std::future<std::string> data;
    boost::asio::io_service svc;
    bp::async_pipe in{svc}, out{svc};
    string proc = "";
    char command[64];
    string output = "";

    if (stockfish_check_exists("stockfish")) {
        proc = "stockfish"; } else {
        errDetails = "Stockfish not found!\n\n";

        abort_application();
    }

    std::string const program_dir = proc;

    auto on_exit = [](int code, std::error_code ec) {
        std::cout << "Exited " << code << "(" << ec.message() << ")\n";
    };

    bp::child process(proc, bp::std_in < in, svc);

    boost::asio::streambuf recv_buffer;

    std::cout << "uci send" << std::endl;
    boost::asio::async_write(in, boost::asio::buffer("uci\n"),
        [&](boost::system::error_code ec, size_t transferred) {
            std::cout << "Write: " << transferred << "\n" << std::endl;
            in.close();
        }
    );

    std::cout << "isready send" << std::endl;
    boost::asio::async_write(in, boost::asio::buffer("isready\n"),
        [&](boost::system::error_code ec, size_t transferred) {
            std::cout << "Write: " << transferred << "\n" << std::endl;
            in.close();
        }
    );

    cout << "Enter your command: ";
    cin >> command;
    cout << "Your command is: " << command << endl;

    if (strcmp(command, "quit") == 0) {
        cout << "Quiting......." << endl;
        boost::asio::async_write(in, boost::asio::buffer("quit"),
            [&](boost::system::error_code ec, size_t transferred) {
                std::cout << "Write: " << transferred << std::endl;
                in.close();

                cout << "Engine quit!" << endl;
            }
        );
    }

    svc.run();

    return 0;
}

为了更容易理解,我在该行省略了std::std_out > out

bp::child process(proc, bp::std_in < in, svc); 

以便引擎结果立即显示在终端 window 中,所以我会知道我是否误入歧途。 这就是我发现奇怪的事情的时候

当我启动应用程序时,它在终端上输出如下:

[2022-01-14 20:25:55]
duythanh@DuyThanhs-MacBook-Pro:/Volumes/Data/ChessGUI$ ./ChessGUI
uci send
isready send
Enter your command: Stockfish 120122 by the Stockfish developers (see AUTHORS file)
id name Stockfish 120122
id author the Stockfish developers (see AUTHORS file)

option name Debug Log File type string default 
option name Threads type spin default 1 min 1 max 512
option name Hash type spin default 16 min 1 max 33554432
option name Clear Hash type button
option name Ponder type check default false
option name MultiPV type spin default 1 min 1 max 500
option name Skill Level type spin default 20 min 0 max 20
option name Move Overhead type spin default 10 min 0 max 5000
option name Slow Mover type spin default 100 min 10 max 1000
option name nodestime type spin default 0 min 0 max 10000
option name UCI_Chess960 type check default false
option name UCI_AnalyseMode type check default false
option name UCI_LimitStrength type check default false
option name UCI_Elo type spin default 1350 min 1350 max 2850
option name UCI_ShowWDL type check default false
option name SyzygyPath type string default <empty>
option name SyzygyProbeDepth type spin default 1 min 1 max 100
option name Syzygy50MoveRule type check default true
option name SyzygyProbeLimit type spin default 7 min 0 max 7
option name Use NNUE type check default true
option name EvalFile type string default nn-ac07bd334b62.nnue
uciok
Unknown command: isready

与上面的代码相比,这两个命令是通过管道发送的。 uciisready ,这很好。 第一个uci命令运行成功,但是isready命令没有返回readyok ,而是返回:

Unknown command: isready

我一直在尝试输入quit ,它会向 pipe 作为退出引擎发送quit命令,但它也失败了:

Your command is: quit
Quiting.......
Write: 5

Write: 9

Unknown command: quit
Write: 5
Engine quit!

然后程序将与引擎一起退出。 我仍然想知道当时发生了什么,但对于幕后发生的事情,线索真的很模糊。

请帮我。 非常感谢任何帮助。 谢谢大家!谢谢

更新:出现Unknown Command: Quit时错误继续。 我在终端中直接通过终端运行 Stockfish 时输入了这些命令,结果它们可以工作,但我的程序仍然不能

您正在打印到cout ,就好像异步操作立即发生一样。 事实并非如此。 异步操作仅在 io 服务运行时发生。

svc.run();

位于代码的最后。 所以在此之前没有async_操作完成(甚至开始)。

其他问题:

  1. out异步 pipe 从未使用过(甚至未连接)。 我不清楚您打算如何以这种方式与子进程进行通信。

  2. 公平地说,您只对子进程进行每次写入,因此您可能对 output 根本不感兴趣。 (但也许recv_buffer也可以被删除)。

  3. 您的缓冲区包括终止NUL字符。 asio::buffer("uci\n")发送{'u','c','i','\n','\0'} )。 这会打乱子进程的解析。

  4. 您执行in.close()以响应每个async_write完成。 这保证了后续写入永远不会发生,因为您关闭了 pipe。

  5. 然后,当您发送quit时,您也无法包含 '\n'

  6. 您正在使用operator>>读取char[64] ,这根本没有意义。 也许您使用的是 c++20(因此可能假定宽度为 64),但您从未设置宽度。 您很可能希望改为读入字符串。

  7. 但是,这样做不能接受带有空格的命令(因为std::ios::skipws是默认设置的)。 所以,可能你想要std::getline代替......

  8. 您包含大量 C 标头的事实让我认为您正在移植一些 C 代码(非常糟糕)。 strcmp的使用和其他也体现了这一点,例如不需要使用::stat

  9. 不要使用using namespace std; 为什么“使用命名空间标准;”被认为是不好的做法?

  10. 不要使用全局变量( errDetails

  11. 不要使用循环来等待时间延迟

  12. 无需手动打印回溯。 相反,使用 Boost:

     void abort_application(std::string const& errDetails) { std::cerr << errDetails << "\n"; std::cerr << boost::stacktrace::stacktrace{} << std::endl; std::this_thread::sleep_for(3s); abort(); }

现有 Stockfish 客户端:玩游戏

你很幸运:我在这个网站上有一个使用 stockfish 的书面完整演示: Interfacing with executable using boost in c++

此示例显示如何正确等待和解析来自子进程的预期回复。

你会注意到我为异步版本选择了协程:

为了完整起见,我想我会尝试一个异步实现。 使用默认的 Asio 回调样式可能会变得笨拙,所以我想将 Boost Coroutine 用于堆栈式协程。 这使得它的实现可以与同步版本有 99% 的相似性

只是为了比较,如果你不使用协程,你的代码应该是这样的:

修复你的代码

住在科利鲁

#include <boost/asio.hpp>
#include <boost/process.hpp>
#include <boost/process/async.hpp>
#include <boost/stacktrace/stacktrace.hpp>
#include <chrono>
#include <iomanip>
#include <iostream>

namespace bp = boost::process;
using boost::system::error_code;
using namespace std::literals;

static void abort_application(std::string const& errDetails) {
    std::cerr << errDetails << "\n";
    std::cerr << boost::stacktrace::stacktrace{} << std::endl;
    std::this_thread::sleep_for(3s);
    abort();
}

inline static bool stockfish_check_exists(std::string& name) {
    return boost::filesystem::exists(name);
}

int main() {
    boost::asio::io_service  svc;
    bp::async_pipe           in{svc};
    std::string              proc = "/usr/games/stockfish";

    if (!stockfish_check_exists(proc)) {
        abort_application("Stockfish not found!");
    }

    auto on_exit = [](int code, std::error_code ec) {
        std::cout << "Exited " << code << "(" << ec.message() << ")\n";
    };

    bp::child process(proc, bp::std_in < in, svc, bp::on_exit = on_exit);

    std::function<void()> command_loop;
    std::string           command_buffer;
    command_loop = [&] {
        std::cout << "Enter your command: " << std::flush;
        // boost::asio::streambuf recv_buffer;
        if (getline(std::cin, command_buffer)) {
            std::cout << "Your command is: " << command_buffer << std::endl;
            command_buffer += '\n';

            async_write( //
                in, boost::asio::buffer(command_buffer),
                [&](error_code ec, size_t transferred) {
                    std::cout << "Write: " << transferred << " (" << ec.message() << ")" << std::endl;

                    if (command_buffer == "quit\n") {
                        std::cout << "Quiting......." << std::endl;
                        // in.close();
                        std::cout << "Engine quit!" << std::endl;
                    } else {
                        command_loop(); // loop
                    }
                });
        }
    };

    std::cout << "uci send" << std::endl;
    async_write(
        in, boost::asio::buffer("uci\n"sv),
        [&](error_code ec, size_t transferred) {
            std::cout << "Write: " << transferred << "\n" << std::endl;
            std::cout << "isready send" << std::endl;
            async_write(in, boost::asio::buffer("isready\n"sv),
                        [&](error_code ec, size_t n) {
                            std::cout << "Write: " << n << std::endl;
                            command_loop(); // start command loop
                        });
        });

    svc.run(); // only here any of the operations start
}

印刷品,例如

在此处输入图像描述

或者,如果 Stockfish实际上已安装:

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM