Boost ASIO - 如何编写控制台服务器2

时间:2012-07-12 07:36:35

标签: c++ console-application boost-asio nonblocking

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

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

This answer非常接近满足我的需求,但我还需要知道两件事:

1:从输入传递的“命令”似乎一次限制为1个字符。我需要的不仅仅是单键输入,例如“关闭”,“说'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();
}

但是,这会在输入几个字符后导致分段错误,并在输入任何输入后按Enter键不再导致出现“command:”。有没有办法让这个设置使用字符串?我确定将它们附加到一个单独的字符串中,一次只能使用一个字符,但我想这个设置本身可以用整个字符串工作。

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()的正确方法;那是什么?

如果已经做了好几千次,我很抱歉,但显然我没有用Google搜索正确的短语来提出我正在寻找的结果。

2 个答案:

答案 0 :(得分: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 - 很可能是std::vector<char>的一个重载,因为您可以在读取返回时轻松将其复制到字符串中。

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

  1. async_read将您的字符串长度转换为固定长度的某个整数
  2. 调整缓冲区大小以正确读取内容
  3. async_read您的内容
  4. 关于你的第二个问题,你想要的并不是很清楚,但如果你想在asio运行的时候做自己的事情,你可能会想看看io_service::poll()

答案 1 :(得分:0)

  1. boost::asio::buffer( &_command, sizeof(_command) )表示您要覆盖sizeof(string)对象的4个第一个字节(或_command},但这显然不是您想要的。如果您需要自动调整大小的输入缓冲区,请改用asio::streambuf

  2. io_service::run 阻止调用线程,因此您的mainLoop将无法运行。您可以在单独的线程中执行io_service::run,也可以手动轮询io_serivce,使用您自己的应用程序循环的迭代交叉调用run_one / poll_one(请参阅参考资料)。