boost::async_write 跳过字符串的一部分

boost::async_write skips parts of the string

本文关键字:字符串 一部分 write async boost      更新时间:2023-10-16

我正在聊天。出于某种原因,在其他客户端之间分发用户消息时,Server::msgHandler组合在一起的字符串被async_writeConnection::write屠宰,使其看起来只有该字符串的一部分实际上被读取。例:

构造消息:"Hello people by Jack">
显示为:"Jack">

也就是说,string str=Hello people没有打印出来。起初我以为这与末尾的隐式有关,但这没有任何意义,此外,当我尝试消息中字符串的各种位置时,我注意到如果str前面有其他文本,文本将显示完全发出str,或者将其放置在意想不到的地方。例如


writeMsg("It was said: n"+str+" by "name"n");将显示为:
用"名字"说
的你好人

完整、最小、可编译的示例:

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

#include <iostream>
#include <vector>
#include <deque>
typedef boost::asio::io_service io_service;
typedef boost::asio::ip::tcp tcp;
class Server;
class Connection : public boost::enable_shared_from_this<Connection> {
io_service::strand strand;
tcp::socket soc;
std::deque<std::string> msgBuff;
boost::asio::streambuf buf;
Server* server;
void(Server::*serverHandler)(std::string);
private:
Connection(io_service& service) :soc(service), strand(service){
}
void writeStranded(std::string msg){
msgBuff.push_back(msg);
if (msgBuff.size() > 1)return;
write();
}
void write(){
std::string& tmpMsg = msgBuff[0];
boost::asio::async_write(
soc,
boost::asio::buffer(tmpMsg.c_str(), tmpMsg.size()),
strand.wrap(
boost::bind(&Connection::handleWrite,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
)
); 
}
void handleWrite(const boost::system::error_code&, size_t s){
msgBuff.pop_front();
if (!msgBuff.empty())write();
}
void handleRead(const boost::system::error_code&, size_t s){
std::istream is(&buf);
std::string tmpMsg;
std::getline(is, tmpMsg);
(server->*serverHandler)(tmpMsg);
readMsg();
}

public:
typedef boost::shared_ptr<Connection> pointer;
static  pointer createInstance(io_service& service){
return pointer(new Connection(service));
}
void init(Server* server, void(Server::*serverHandler)(std::string)){
this->server = server;
this->serverHandler = serverHandler; 
writeMsg("hellon");
readMsg();
}
void writeMsg(std::string msg){
strand.dispatch(boost::bind(&Connection::writeStranded, this, msg));
}
void readMsg(){
const char delim = 'n';
boost::asio::async_read_until(soc, buf, delim,
boost::bind(&Connection::handleRead, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
tcp::socket& getSocket(){
return soc;
}
};
class Server{
tcp::acceptor accept;
std::vector<Connection::pointer> connections;
public:
Server(io_service& io_service, int port = 23) :accept(io_service, tcp::endpoint(tcp::v4(), port)){
awaitConnection();
};

private:
void awaitConnection(){
Connection::pointer con = Connection::createInstance(accept .get_io_service());
accept.async_accept(con->getSocket(), boost::bind(&Server::conAcceptor, this, con, boost::asio::placeholders::error));
}
void conAcceptor(Connection::pointer con, const boost::system::error_code& err){
if (err)return;
con->init(this, &Server::msgHandler);
awaitConnection();
connections.push_back(con);
}
void msgHandler(std::string str){
for (Connection::pointer ptr : connections){
ptr->writeMsg(str+" by "name"n");
}
}
};

int main(){
io_service io_service;
Server s(io_service);
io_service.run();
system("pause");
}

Upd原来,async_read正在附加带有回车符的字符串,
回车符在name字符串中添加到分量仪之前时存储,每次我尝试显示名称时,它前面的所有内容都会被后面的所有内容覆盖。有时,回车会变得疯狂并跳过名称前面的一些字符,这进一步使搜索此错误变得复杂。

我让它运行了。我不得不为它写一个客户端...

在投入生产之前,您需要查看生命周期处理。正常方式是连接对象在其绑定处理程序中保存自身的shared_ptr。

我使用了 c++14 lambda,因为我发现它们没有那么繁重,可以提升::绑定。

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

#include <iostream>
#include <vector>
#include <deque>
#include <iterator>
typedef boost::asio::io_service io_service;
typedef boost::asio::ip::tcp    tcp;
class Server;
class Connection
: public boost::enable_shared_from_this<Connection>
{
io_service::strand strand;
tcp::socket        soc;
// use double-buffering for the message sending
std::deque<std::string> sending, to_send;
boost::asio::streambuf buf;
Server                 *server;
void (Server::*serverHandler)(std::string);
private:
Connection(io_service& service)
: strand(service)
, soc(service)
{
}
void writeStranded(std::string msg)
{
assert(strand.running_in_this_thread());  // sanity check
to_send.push_back(std::move(msg));
maybe_write();
}
void maybe_write()
{
assert(strand.running_in_this_thread());  // sanity check
if (sending.empty() and not to_send.empty()) {
sending.swap(to_send);
// make a buffer sequence
auto buffers = std::vector<boost::asio::const_buffers_1>();
buffers.reserve(sending.size());
for (auto& data : sending) {
buffers.push_back(boost::asio::buffer(data));
}
boost::asio::async_write(soc, buffers,
strand.wrap([this](auto&& ec, size_t size)
{
this->sending.clear();
if (not ec) maybe_write();
}));
}
}
void handleRead(const boost::system::error_code&, size_t s)
{
std::istream is(&buf);
std::string  tmpMsg;
std::getline(is, tmpMsg);
(server->*serverHandler)(tmpMsg);
readMsg();
}

public:
typedef boost::shared_ptr<Connection> pointer;
static pointer createInstance(io_service& service)
{
return pointer(new Connection(service));
}
void init(Server *server, void(Server::*serverHandler)(std::string))
{
this->server        = server;
this->serverHandler = serverHandler;
writeMsg("hellon");
readMsg();
}
void writeMsg(std::string msg)
{
strand.dispatch(boost::bind(&Connection::writeStranded, this, msg));
}
void readMsg()
{
const char delim = 'n';
boost::asio::async_read_until(soc, buf, delim,
boost::bind(&Connection::handleRead, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
tcp::socket& getSocket()
{
return soc;
}
};
class Server
{
tcp::acceptor                    accept;
std::vector<Connection::pointer> connections;
public:
Server(io_service& io_service, int port = 2333)
: accept(io_service, tcp::endpoint(tcp::v4(), port))
{
awaitConnection();
};

private:
void awaitConnection()
{
Connection::pointer con = Connection::createInstance(accept.get_io_service());
accept.async_accept(con->getSocket(),
boost::bind(&Server::conAcceptor, this, con, boost::asio::placeholders::error));
}
void conAcceptor(Connection::pointer con, const boost::system::error_code& err)
{
if (err)return;
con->init(this, &Server::msgHandler);
awaitConnection();
connections.push_back(con);
}
void msgHandler(std::string str)
{
for (Connection::pointer ptr : connections) {
ptr->writeMsg(str + " by "name"n");
}
}
};
struct Client
{
using protocol = boost::asio::ip::tcp;
Client(boost::asio::io_service& exec)
: executor_(exec) {}

void run(int port)
{
resolver_.async_resolve(protocol::resolver::query("localhost", std::to_string(port)),
strand_.wrap([this](auto&& ec, auto iter)
{
std::cout << "resolve: " << ec.message() << std::endl;
if (not ec) start_connect(iter);
}));
}
void start_connect(protocol::resolver::iterator iter)
{
boost::asio::async_connect(socket_, iter,
strand_.wrap([this](auto&& ec, auto iter)
{
std::cout << "connect: " << ec.message() << std::endl;
if (not ec) {
this->start_reading();
auto data = std::make_shared<std::string>(
"The quick brown fox jumps over the lazy dogn"
"Farmer bob has a cool tractorn");
boost::asio::async_write(socket_, boost::asio::buffer(*data),
           strand_
               .wrap([data](auto&& ec, auto size)
                     {
                         std::cout << "written: "
                                   << size
                                   << std::endl;
                     }));
}
}));
}
void start_reading()
{
auto buffer = read_buffer_.prepare(1024);
socket_.async_read_some(read_buffer_.prepare(1024), [this](auto&& ec, auto size)
{
read_buffer_.commit(size);
std::istream is(std::addressof(read_buffer_));
std::string s;
while(std::getline(is, s)) {
std::cout << s << std::endl;
}
start_reading();
});
}
boost::asio::io_service& executor_;
boost::asio::io_service::strand strand_{executor_};
protocol::resolver              resolver_{executor_};
protocol::socket                socket_{executor_};
boost::asio::streambuf          read_buffer_;
};
int main()
{
io_service io_service;
Server     s(io_service);
Client     c(io_service);
c.run(2333);
io_service.run();
system("pause");
}

输出(程序不终止(:

resolve: Undefined error: 0
connect: Undefined error: 0
written: 74
hello
The quick brown fox jumps over the lazy dog by "name"
Farmer bob has a cool tractor by "name"

请注意,invoid readMsg()delim设置为'n'。尽管看起来无害,但它没有考虑到在Windows中换行符表示为CR + LF的事实,即:"\r"。因此,每次您使用read_until(delim)从套接字读取内容时,除分量表之外的所有内容都将保留在缓冲区中,包括r(回车(。如果稍后附加这样的字符串,则预计r之后的所有内容都将覆盖原始文本。