2

我正在尝试编写一个在 Ubuntu Server(无 GUI)上运行的游戏服务器,但我在第 1 步遇到了问题。我是 C++ 新手,所以请多多包涵。

我需要能够在服务器继续运行时在任何给定点向服务器键入命令。由于 cin 是一个阻塞输入,所以它不会飞。我已经挖掘了,似乎要走的路是使用 Boost 的 ASIO 库。

这个答案非常接近满足我的需求,但我仍然需要知道另外两件事:

1:从输入传递的“命令”似乎一次限制为 1 个字符。我需要的不仅仅是单键输入,例如“shutdown”、“say 'Hello World!'”、“listPlayers -online”等。我尝试修改代码以使用字符串,而不是 char:

#include <boost/asio.hpp>

#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

#include <iostream>
#include <string>

using namespace boost::asio;

class Input : public boost::enable_shared_from_this<Input>
{
public:
    typedef boost::shared_ptr<Input> Ptr;

public:
    static void create(
            io_service& io_service
            )
    {
        Ptr input(
                new Input( io_service )
                );
        input->read();
    }

private:
    explicit Input(
            io_service& io_service
         ) :
        _input( io_service )
    {
        _input.assign( STDIN_FILENO );
    }

    void read()
    {
        async_read(
                _input,
                boost::asio::buffer( &_command, sizeof(_command) ),
                boost::bind(
                    &Input::read_handler,
                    shared_from_this(),
                    placeholders::error,
                    placeholders::bytes_transferred
                    ));
    }

    void read_handler(
            const boost::system::error_code& error,
            size_t bytes_transferred
            )
    {
        if ( error ) {
            std::cerr << "read error: " << boost::system::system_error(error).what() << std::endl;
            return;
        }

        if ( _command.compare( "\n" ) != 0 ) {
            std::cout << "command: " << _command << std::endl;
        }

        this->read();
    }

private:
    posix::stream_descriptor _input;
    std::string _command;
};



int main()
{
    io_service io_service;
    Input::create( io_service );
    io_service.run();
}

但是,这会在输入几个字符后导致分段错误,并且在输入任何输入后按回车不再导致“命令:”出现。有没有办法让这个设置使用字符串?我确信一次将它们附加到一个单独的字符串中会起作用,但我想认为这种设置可以在本机上与整个字符串一起使用。

2:(为澄清而编辑)我需要这个非阻塞输入与我的服务器代码的其余部分协同工作。问题是:该代码去哪里了?我提醒您注意上面的 main() 函数,修改为使用 while 循环,并调用 mainLoop 函数:

bool loopControl = true;

int main()
{
    io_service io_service;
    Input::create( io_service );

    // This loops continually until the server is commanded to shut down
    while( loopControl )
    {
        io_service.run();      // Handles async input 
        mainLoop();            // Where my actual program resides
    }
}

即使其他一切正常,在正常情况下控制仍然不会到达 mainLoop() 。换句话说,io_service.run()仍然是阻塞,打败了整个目的。这显然不是实现 io_service 和/或 mainLoop(); 的正确方法;那是什么?

如果这已经完成了数千次,我深表歉意,但显然我没有在谷歌上搜索正确的短语来显示我正在寻找的结果。

4

2 回答 2

0

boost::asio::buffer不直接支持从 中创建可变缓冲区std::string,主要是因为不能保证它们在 C++11 之前的内存中是连续的。

你称之为((void*, size_t)重载)的方式,你会让读取覆盖std::string的内部,这会导致你的段错误。您可能应该使用此列表中的其他重载之一:http: //www.boost.org/doc/libs/1_50_0/doc/html/boost_asio/reference/buffer.html - 很可能是一个 for std::vector<char>,因为您可以轻松当您的读取返回时,将其复制到一个字符串中。

现在的问题是您需要事先知道要读取多少个字符,因为您的字符串是可变长度的。为此,您需要async_read在阅读实际内容之前单独计算长度。然后你调整缓冲区的大小(正如我所说,最有可能std::vector<char>)并安排读取该长度。请注意,发件人可以同时发送两者,这只是从流中读取复杂......总结:

  1. async_read 你的字符串长度成固定长度的整数
  2. 适当调整读取内容的缓冲区大小
  3. async_read 你的内容

至于你的第二个问题,目前还不清楚你想要什么,但你可能想看看io_service::poll()你是否想在 asio 运行时做自己的事情。

于 2012-07-12T08:38:56.127 回答
0
  1. boost::asio::buffer( &_command, sizeof(_command) )意味着您要覆盖对象的 4 个前字节(或其他字节sizeof(string)_command,但这显然不是您想要的。如果您需要自动调整大小的输入缓冲区,请asio::streambuf改用。

  2. io_service::run 阻塞调用线程,所以你mainLoop不会运行。您可以io_service::run在单独的线程中执行,也可以手动轮询,将对/的io_serivce调用(参见参考资料)与您自己的应用程序循环的迭代交错。run_onepoll_one

于 2013-01-19T20:57:09.140 回答