我正在尝试编写一个在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搜索正确的短语来提出我正在寻找的结果。
答案 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>
)并安排读取该长度。请注意,发件人可以将两者一起发送,这对于从流中读取来说很复杂......要总结一下:
关于你的第二个问题,你想要的并不是很清楚,但如果你想在asio运行的时候做自己的事情,你可能会想看看io_service::poll()
。
答案 1 :(得分:0)
boost::asio::buffer( &_command, sizeof(_command) )
表示您要覆盖sizeof(string)
对象的4个第一个字节(或_command
},但这显然不是您想要的。如果您需要自动调整大小的输入缓冲区,请改用asio::streambuf
。
io_service::run
阻止调用线程,因此您的mainLoop
将无法运行。您可以在单独的线程中执行io_service::run
,也可以手动轮询io_serivce
,使用您自己的应用程序循环的迭代交叉调用run_one
/ poll_one
(请参阅参考资料)。