3

I'm writing a simple ProxyServer that analyzes packages and sends them to another server instance, e.g. something like this:

client -> MyProxy -> SQLServer ->
client <- MyProxy <- SQLServer <-

It should run in an infinite loop. My problem now is that the proxy seems to loose packages, sometimes it even hangs. When I add a lot of debug information (which is written to the console), the ProxyServer is much more stable. It seems like the ProxyServer is too fast.. :-)

I'm pretty sure I'm doing something wrong, here is code of my session class (the code is derived from the Boost::Asio examples).

#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));
    }
}

Do I need to use other methods instead of async_* for my type of proxy? I'm porting the code from some old project that my company wants to restart again, but with boost instead of the Winsock stuff that was used before.

Any idea what could be the problem?

The old code did something like this: The main method with the accept method call created two threads

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

and the threads called the following functions:

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]: I tried to run the async_read commands for the client and the sqlserver simultaneously, but now I get crashes all the time, sometimes in boost::bind, sometimes in other parts of the boost library.

What seems to happen is that 2 or three conections are created ( 3 sessions). While closing the first session, the crash seems to happen in the second session.

Is boost asio not treadsafe or am I doing something terribly wrong here :-) ?

I posted the code for the little ProxyServer here:

session.h : link

session.cpp : link

server.h: link

server.cpp: link

ProxyServer.cpp: link

4
  • Why are you mixing and matching recv() and send() calls with ASIO? I think you need to change your error handling for recv(2) so that it checks for success and then assumes failure vs. assuming the only return code is SOCKET_ERROR. Commented Jun 4, 2011 at 2:05
  • @Sean I don't think @user is mixing them, the code using send and recv is some old winsock code. Commented Jun 4, 2011 at 14:47
  • Yes, the Winsock code is just an old project to show how it was done before, the Boost code is the new project, so I am not mixing the two API calls. Commented Jun 13, 2011 at 6:57
  • @Sam Miller: Session data is boost::array<char, 8192> data_; Commented Jun 13, 2011 at 7:11

1 Answer 1

4

I suspect what is happening is that one of your async_read_some calls is returning "some" data, but not enough for the SQL server to be satisfied that it has received a complete request. You code always follows the path of read_from_client -> send_to_server -> read_from_server -> send_to_client. It does not handle the case where you need read_from_client -> send_to_server - >read_from_client -> send_to_server -> read_from_server -> send_to_client.

The code you have written so far does not perform the same operations as the original. Specifically, the old code was simultaneously listening for reads on both sockets. Fortunately, since you're using ASIO, you don't need to mess with threads. Just issue issue simultaneous async_read_some requests on both sockets and deal with them asynchronously.

Sign up to request clarification or add additional context in comments.

2 Comments

+1 I just had to do the same sort of thing with async_read_some(). When implementing some sort of request processor, you must provide some processing in the handle_read() to put together one or more reads into complete requests.
Thanks for the answer, I will try to check if there is more data to be read from the socket and, if so, handle it in my read callback methods.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.