使用 Boost.Asio 的简单服务器抛出异常

Simple server using Boost.Asio throws an exception

我正在尝试使用 Boost.Asio 库编写一个简单的服务器。我希望我的服务器从客户端接收消息并在控制台上打印该消息。这是我的服务器程序的代码:

#include <iostream>
#include <string>
#include <memory>

#include <boost/asio.hpp>

using namespace boost::asio;
using namespace boost::system;
using boost::asio::ip::tcp;

class Session : public std::enable_shared_from_this<Session> {
public:
    Session(tcp::socket socket);

    void start();
private:
    tcp::socket socket_;
    std::string data_;
};

Session::Session(tcp::socket socket) : socket_(std::move(socket))
{}

void Session::start()
{
    socket_.async_read_some(buffer(data_), [this](error_code errorCode, size_t length) {
        if (!errorCode) {
            std::cout << "received: " << data_ << std::endl;
        }
        start();
    });
}

class Server {
public:
    Server(io_context& context);
private:
    tcp::acceptor acceptor_;

    void accept();
};

Server::Server(io_context& context) : acceptor_(context, tcp::endpoint(tcp::v4(), 8888))
{
    accept();
}

void Server::accept()
{
    acceptor_.async_accept([this](error_code errorCode, tcp::socket socket) {
        if (!errorCode) {
            std::make_unique<Session>(std::move(socket))->start();
        }
        accept();
    });
}

int main()
{
    boost::asio::io_context context;
    Server server(context);
    context.run();
    return 0;
}

这是我的客户端程序的代码:

#include <iostream>
#include <string>

#include <boost/asio.hpp>

using namespace boost::asio;
using boost::asio::ip::tcp;

int main()
{
    io_context context;
    tcp::socket socket(context);
    tcp::resolver resolver(context);
    connect(socket, resolver.resolve("127.0.0.1", "8888"));
    while (true) {
        try {
            std::string data;
            std::cin >> data;
            write(socket, buffer(data));
        } catch (const std::exception& exception) {
            std::cerr << exception.what() << std::endl;
        }
    }
    return 0;
}

但是当我启动客户端时,服务器抛出异常"read access violation"。我做错了什么?

您正在使用 enable_shared_from_this,但没有任何东西可以让您的 Session 保持活动状态,因为您只使用 unique_ptr<Session>.

这意味着您的 Session 在 运行 操作期间消失。

修复它:

std::make_shared<Session>(std::move(socket))->start();

接下来,在您的完成处理程序中保存一个共享指针:

void Session::start()
{
    auto self = shared_from_this();
    socket_.async_read_some(buffer(data_), [this, self](error_code errorCode, size_t /*length*/) {
        if (!errorCode) {
            std::cout << "received: " << data_ << std::endl;
        }
        start();
    });
}

接下来,如果出现错误(否则您的会话将无限循环),则切断异步循环:

socket_.async_read_some(buffer(data_), [this, self](error_code errorCode, size_t length) {
    if (!errorCode && length) {
        std::cout << "received: " << data_ << std::endl;
        start();
    }
});

最后,调整缓冲区大小,以便您可以实际接收数据 (!):

data_.resize(32);
socket_.async_read_some(buffer(data_), [this, self](error_code errorCode, size_t length) {
    if (!errorCode) {
        data_.resize(length);
        std::cout << "received: '" << data_ << "'" << std::endl;
        start();
    }
});

还有一些问题,但是嘿,程序不会立即崩溃,你有 一些 结果。

更新

添加了显示更多建议的现场演示Live On Coliru

#include <iostream>
#include <string>
#include <memory>

#include <boost/asio.hpp>

using namespace boost::asio;
using namespace boost::system;
using boost::asio::ip::tcp;

class Session : public std::enable_shared_from_this<Session> {
public:
    Session(tcp::socket socket);

    void start();
private:
    tcp::socket socket_;
    boost::asio::streambuf _sb;
};

Session::Session(tcp::socket socket) : socket_(std::move(socket))
{}

void Session::start()
{
    auto self = shared_from_this();
    async_read_until(socket_, _sb, '\n', [this, self](error_code errorCode, size_t /*length*/) {
        std::cout << "completion " << errorCode.message() << "\n";
        if (!errorCode) {
            std::string line;
            {
                std::istream is(&_sb);
                if (getline(is, line)) {
                    std::cout << "received: '" << line << "'" << std::endl;
                }
                start();
            }
        }
    });
}

class Server {
public:
    Server(io_context& context);
private:
    tcp::acceptor acceptor_;

    void accept();
};

Server::Server(io_context& context) : acceptor_(context, tcp::endpoint(tcp::v4(), 8888))
{
    accept();
}

void Server::accept()
{
    acceptor_.async_accept([this](error_code errorCode, tcp::socket socket) {
        if (!errorCode) {
            std::make_shared<Session>(std::move(socket))->start();
        }
        accept();
    });
}

int main(int argc, char**) {
    if (argc>1) {
        io_context context;
        tcp::socket socket(context);
        tcp::resolver resolver(context);
        connect(socket, resolver.resolve("127.0.0.1", "8888"));
        std::string data;
        while (getline(std::cin, data)) {
            try {
                data += '\n';
                write(socket, buffer(data));
            } catch (const std::exception& exception) {
                std::cerr << exception.what() << std::endl;
            }
        }
    } else {
        boost::asio::io_context context;
        Server server(context);
        context.run();
    }
}