簡體   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