boost :: async_write跳过字符串

时间:2017-07-09 19:34:56

标签: c++ boost

我正在聊天。出于某种原因,在其他客户端之间分发用户的消息时,由Server::msgHandler async_writeConnection::write进行屠宰的字符串,使其仅显示为部分该字符串实际上已被读取。例如:

  

构造信息:"杰克"你好的人   出现为:"由Jack"

string str=Hello people未打印出来。起初我认为这与隐含的\0结尾有关,但是这并没有任何意义,而且,当我在消息中尝试了各种字符串位置时,我注意到{{1}在其他文本之前,文本将完全显示为str,或将其放置在意外的位置。 E.g。

  

str
  将显示为:
  有人说      by" name" Hello People

完整,简约,可编辑的例子:

writeMsg("It was said: \n"+str+" by \"name\"\n");

UPD
事实证明#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("hello\n"); 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"); } 附加了带回车符的字符串,该字符串在async_read字符串中的分隔符之前添加时存储,并且每次我尝试使名称出现时,其前面的所有内容都将得到被随后的一切覆盖。有时候回车会变得疯狂,并且会跳过名字前面的一些字符,这使得对这个错误的搜索变得更加复杂。

2 个答案:

答案 0 :(得分:1)

我跑了。我不得不为它写一个客户......

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

我使用了c ++ 14 lambdas,因为我发现它们比boost :: bind更加繁重。

#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("hello\n");
        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 dog\n"
                                                                "Farmer bob has a cool tractor\n");
                                                        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"

答案 1 :(得分:0)

请注意,void readMsg()中的delim被设置为'\n'。尽管看上去无害,但它没有考虑到在Windows中换行符表示为CR + LF的事实,即:“ \ r \ n 。因此,每次您使用read_until(delim)从套接字读取内容时,除定界符外的所有内容都会保留在缓冲区中,包括\r(回车)。如果以后再附加类似的字符串,则可以预期\r之后的所有内容都会覆盖原始文本。