Boost :: Asio: проблема с async_read - PullRequest
3 голосов
/ 03 июня 2011

Я пишу простой ProxyServer, который анализирует пакеты и отправляет их на другой экземпляр сервера, например, как то так:

клиент -> MyProxy -> SQLServer ->
клиент <- MyProxy <- SQLServer <- </p>

Он должен работать в бесконечном цикле. Моя проблема сейчас в том, что прокси, кажется, теряет пакеты, иногда даже зависает. Когда я добавляю много отладочной информации (которая записывается на консоль), ProxyServer гораздо стабильнее. Похоже, что ProxyServer слишком быстрый ..: -)

Я почти уверен, что делаю что-то не так, вот код моего сессионного класса (код взят из примеров Boost :: Asio).

#include "session.h"

#include <iostream>
using namespace std;

session::session(boost::asio::io_service& io_service)
: socket_(io_service)
, sqlsocket_(io_service)
, io_service_(io_service)
, resolver(io_service)
{
    cout << "session::session()" << endl;
}

session::~session()
{
    cout << "session::~session()" << endl;
    cout << "closing session ..." << endl;
}

tcp::socket& session::socket()
{
    return socket_;
}

void session::start()
{
    cout << "session::start()" << endl;
    cout << "starting session ..." << endl;

    // connect to the sqlserver database
    tcp::resolver::query query("192.168.1.50", "1317");
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
    tcp::endpoint endpoint = *endpoint_iterator;

    sqlsocket_.async_connect(endpoint,
        boost::bind(&session::handle_sqlserver_connect, this,
        boost::asio::placeholders::error, ++endpoint_iterator));

    // TODO: connect to the connector
}

void session::handle_read(const boost::system::error_code& error,
                          size_t bytes_transferred)
{
    cout << "session::handle_read()" << endl;
    if (!error)
    {
        cout << "session::handle_read() (read: " 
             << bytes_transferred << ")"
             << endl;
        boost::asio::async_write(sqlsocket_,
            boost::asio::buffer(data_, bytes_transferred),
            boost::bind(&session::handle_sqlserver_write, this,
            boost::asio::placeholders::error, bytes_transferred));
    }
    else
    {
        delete this;
    }
}

void session::handle_sqlserver_read(const boost::system::error_code& error,
                                    size_t bytes_transferred)
{
    cout << "session::handle_sqlserver_read()" << endl;
    if (!error)
    {
        cout << "session::handle_sqlserver_read() (read: " 
             << bytes_transferred << ")"
             << endl;
        boost::asio::async_write(socket_,
            boost::asio::buffer(data_, bytes_transferred),
            boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error, bytes_transferred));
    }
    else
    {
        delete this;
    }
}

void session::handle_write(const boost::system::error_code& error,
                           size_t bytes_transferred)
{
    static int count = 0;
    cout << ++count << ". session::handle_write()" << endl;
    if (!error)
    {
        cout << "session::handle_write() (read: " 
             << bytes_transferred << ")"
             << endl;
        socket_.async_read_some(boost::asio::buffer(data_, max_length),
            boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
        delete this;
    }
}

void session::handle_sqlserver_write(const boost::system::error_code& error,
                                     size_t bytes_transferred)
{
    cout << "session::handle_sqlserver_write()" << endl;
    if (!error)
    {
        cout << "session::handle_sqlserver_write() (read: " 
             << bytes_transferred << ")"
             << endl;
        sqlsocket_.async_read_some(boost::asio::buffer(data_, max_length),
            boost::bind(&session::handle_sqlserver_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
        delete this;
    }
}

void session::handle_sqlserver_connect(const boost::system::error_code& error,
                                       tcp::resolver::iterator endpoint_iterator)
{
    cout << "session::handle_sqlserver_connect()" << endl;
    if (!error)
    {
        socket_.async_read_some(boost::asio::buffer(data_, max_length),
            boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else if (endpoint_iterator != tcp::resolver::iterator())
    {
        sqlsocket_.close();
        tcp::endpoint endpoint = *endpoint_iterator;
        sqlsocket_.async_connect(endpoint,
            boost::bind(&session::handle_sqlserver_connect, this,
            boost::asio::placeholders::error, ++endpoint_iterator));
    }
}

Нужно ли использовать другие методы вместо async_ * для моего типа прокси? Я портирую код из какого-то старого проекта, который моя компания хочет перезапустить, но с бустом вместо того, что использовалось ранее в Winsock.

Есть идеи, в чем может быть проблема?

Старый код делал что-то вроде этого: Основной метод с вызовом метода accept создал два потока

CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenatclient, (LPVOID)cs, 0, 0);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenatserver, (LPVOID)cs, 0, 0);

и темы вызвали следующие функции:

void listenatclient(LPVOID connection)
{
    connection_s* cs = (connection_s*)connection;
    char inMessagecli[MSG_SIZE];
    int rcount = 0;

    ...

    do
    {
        memset(inMessagecli, 0, MSG_SIZE);
        rcount = recv((SOCKET)cs->client, inMessagecli, MSG_SIZE, 0);
        if (rcount != SOCKET_ERROR)
        {
            // analyze package
            ...

            send((SOCKET)cs->server, inMessagecli, rcount, 0);
        }
    } while (rcount > 0);
}

void listenatserver(LPVOID connection)
{
    connection_s* cs = (connection_s*)connection;
    char inMessageserv[MSG_SIZE];
    int rcount = 0;

    do
    {
        memset(inMessageserv, 0, MSG_SIZE);
        rcount = recv((SOCKET)cs->server, inMessageserv, MSG_SIZE, 0);
        if (rcount != SOCKET_ERROR)
        {
            send((SOCKET)cs->client, inMessageserv, rcount, 0);         
        }
    } while (rcount > 0);
}

[EDIT]: Я пытался запустить команды async_read для клиента и сервера sqlserver одновременно, но теперь у меня все время возникают сбои, иногда в boost :: bind, иногда в других частях библиотеки boost.

Кажется, что происходит то, что создается 2 или 3 соединения (3 сеанса). При закрытии первого сеанса происходит сбой во втором сеансе.

Является ли boost asio не безопасным или я что-то здесь не так делаю :-)?

Я разместил код для маленького ProxyServer здесь:

session.h: ссылка

session.cpp: ссылка

server.h: ссылка

server.cpp: ссылка

ProxyServer.cpp: ссылка

1 Ответ

4 голосов
/ 04 июня 2011

Я подозреваю, что происходит то, что один из ваших вызовов async_read_some возвращает «некоторые» данные, но этого недостаточно, чтобы SQL-сервер был удовлетворен тем, что он получил полный запрос.Ваш код всегда следует по пути read_from_client -> send_to_server -> read_from_server -> send_to_client.Он не обрабатывает случай, когда вам нужно read_from_client -> send_to_server -> read_from_client -> send_to_server -> read_from_server -> send_to_client.

Код, который вы написали до сих пор, не выполняет те же операции, что и оригинал.В частности, старый код одновременно прослушивал чтения на обоих сокетах.К счастью, поскольку вы используете ASIO, вам не нужно связываться с потоками.Просто выполните одновременные запросы async_read_some на обоих сокетах и ​​обрабатывайте их асинхронно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...