Я пытаюсь написать простой сервер с использованием библиотеки 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;
}
Но когда я запускаю клиента, сервер выдает исключение «нарушение доступа для чтения». Что я делаю не так?
Исключение генерируется функцией buffer(), которая вызывается из моего start().
Перечитайте ссылку .... вы найдете ее дубликат (вы обнаружите, что создание изменяемого буфера с помощью std :: string на самом деле не работает - вместо этого используйте std :: vector)
Спасибо за ваш комментарий! Я изменил std::string data_; на std::vector<char> data_ = std::vector<char>(1024); и buffer(data_) на buffer(data_, 1024), но теперь он терпит неудачу при отладочном утверждении с сообщением «векторный итератор не может быть разыменован».
«Часть этого сообщения в вашем отладчике будет включать трассировку стека. Посмотрите на это, и он точно расскажет, что происходит» Другая ошибка - другая трассировка стека ... дайте полную информацию в первый раз, и вы получите более точные ответы время ... но на самом деле вам следует потренироваться смотреть на это и понимать, что идет не так. Я предполагаю, что теперь он, вероятно, терпит неудачу в другом месте; возможно потому, что данные имеют размер 1024, но были записаны только байты длины.
Сейчас это от io_context::run().
@UKMonkey Я думаю, ты здесь совсем не в теме. Конечно, буфер имеет нулевую емкость (это бесполезно, но и не вызывает беспокойства). Проблема в том, что после запуска сеанс не существует.
@sehe хороший улов - радость от попытки ответить на пару вопросов перед тем, как отправиться домой на ночь;) Я только проверил, попал ли еще буфер в прицел!





Вы используете enable_shared_from_this, но ничто не поддерживает вашу сессию, потому что вы используете только unique_ptr<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();
}
}
Большое спасибо! Но у меня остались вопросы. Во-первых, зачем нам этот указатель self в функции start()? Как я вижу, мы не используем его в закрытии.
Это требует от вас понимания как общих указателей, так и асинхронных операций.
async_* всегда возвращается немедленно (то есть до завершения операции или даже до ее запуска). Это означает, что после выхода из start() вам все равно необходимо убедиться, что экземпляр Session «остается». В противном случае использование this в обработчике завершения (что вы делаете) незаконно.
Принцип работы shared_ptr заключается в том, что он поддерживает указатель в живых до тех пор, пока последний указывающий на него shared_ptr не исчезнет. Захватив этот указатель shared_from_this() (который представляет собой такой shared_ptr<SessIon>), вы убедитесь, что, пока обработчик завершения находится где-то в очереди, соответствующий экземпляр Session не может быть уничтожен, так что вы можете законно использовать this в лямбда («закрытие»).
Хорошо, теперь я понял! Не могли бы вы указать на другие вопросы, о которых вы рассказывали?
Я не думаю, что указание на них здесь очень полезно. Я предлагаю вам прочитать некоторые другие ответы, например stackoverflow.com/questions/40561097/… или stackoverflow.com/a/8377771/85371 - если у вас есть что-то, что не делает то, что вы хотите, вы должны увидеть это во время тестирования. Если вы не можете решить эту проблему, вы всегда можете поискать / опубликовать другой вопрос об этой задаче конкретный
Добавлена живая демонстрация, показывающая еще несколько предложений Live On Coliru
Спасибо вам за ваши предложения!
Часть этого сообщения в вашем отладчике будет включать трассировку стека. Посмотрите на это, и он точно скажет вам, что происходит. (Если бы я был букмекером, он бы вращался вокруг вызова boost :: buffer (string) - и он пытался бы записать в недопустимую память. stackoverflow.com/questions/4068249/…