Question

I present a simple client and server written in Asio to show something that might be a bug. client repeatedly sends a fixed string of length 102 to the server and server checks the string to be correct and writes an error message if not so and exits.

The code is compiled and run in debian 7 (amd64)
my processor has 2 cores
boost version is 1.55

when running server and client, after a few thousand packets are sent a wrong packet is received.(it may be required to repeat the test a few times)

what is the problem?

Makefile

CXX=g++
LIBS = -lboost_thread -lboost_filesystem -lboost_system

all: server asyncclient
server: server.o
    $(CXX) $(LIBS) server.o -o server
asyncclient: asyncclient.o
    $(CXX) $(LIBS) asyncclient.o -o asyncclient
server.o: server.cpp
    $(CXX) -c server.cpp
asyncclient.o: asyncclient.cpp
    $(CXX) -c asyncclient.cpp

server.cpp

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
char teststr[]="1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n";
void session(tcp::socket* sock)
{
    boost::array<char, 102> buf;// 102 == sizeof(teststr)
    int i=0;
    try
    {
            for (;;)
            {
                    std::cout<<i++<<std::endl;
                    boost::system::error_code error;
                    size_t length = boost::asio::read(*sock, boost::asio::buffer(buf), error);
                    if (error == boost::asio::error::eof)
                            break; // Connection closed cleanly by peer.
                    else if (error)
                            throw boost::system::system_error(error); // Some other error.
                    if(strcmp(buf.data(), teststr)!=0)
                    {
                            std::cerr<<"error"<<std::endl;
                            std::cerr<<"    buf.data() = "<<
                                    std::string(buf.data(), buf.size())<<std::endl;
                            return;
                    }
            }
    }
    catch (std::exception& e)
    {
            std::cerr << "Exception: " << e.what() << "\n";
    }
}

int main()
{
    std::cerr<<"sizeof(teststr) = "<<sizeof(teststr)<<std::endl;//DEBUG
    boost::asio::io_service ios;
    tcp::acceptor a(ios, tcp::endpoint(tcp::v4(), 41000));
    tcp::socket sock(ios);
    a.accept(sock);
    boost::thread t(boost::bind(session, &sock));
    t.join();
}

asyncclient.cpp

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

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

char teststr[]="1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n";

void handle_write(size_t num_bytes, boost::system::error_code e)
{
    static int i=0;
    if(!e)
    {
        std::cout<<i++<<std::endl;
    }
    else
    {
        std::cout<<"Error: "<<e.message()<<std::endl;
        exit(1);
    }
}

int main()
{
    boost::asio::io_service ios;

    tcp::resolver r(ios);
    tcp::resolver::query q(tcp::v4(), "127.0.0.1", "41000");
    tcp::resolver::iterator it = r.resolve(q);

    tcp::socket sock(ios);
    boost::asio::connect(sock, it);

    boost::thread t(boost::bind(&boost::asio::io_service::run,&ios));
    try
    {
        for(;;)
        {
            boost::asio::async_write(sock, boost::asio::buffer(teststr,102),
                    boost::bind(&handle_write,
                    boost::asio::placeholders::bytes_transferred,
                    boost::asio::placeholders::error));
        }
    }
    catch(std::exception& e)
    {
        std::cout<<"Exception: "<<e.what()<<std::endl;
    }
    t.join();
}
Was it helpful?

Solution

the problem comes from the following for loop:

for(;;)
    {
        boost::asio::async_write(sock, boost::asio::buffer(teststr,102),
                boost::bind(&handle_write,
                boost::asio::placeholders::bytes_transferred,
                boost::asio::placeholders::error));
    }

an async_write operation may be begin before the completion of previous async_write which is not allowed.
REF: async_write

the correct way is to eliminate the for loop and call async_write in the handle_write function:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

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

char teststr[]="1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n";

void handle_write(tcp::socket* sock, size_t num_bytes, boost::system::error_code e)
{
    static int i=0;
    if(!e)
    {
        std::cout<<i++<<std::endl;
        boost::asio::async_write(*sock, boost::asio::buffer(teststr,102),
                boost::bind(&handle_write, sock,
                    boost::asio::placeholders::bytes_transferred,
                    boost::asio::placeholders::error));
    }
    else
    {
        std::cout<<"Error: "<<e.message()<<std::endl;
        exit(1);
    }
}

int main()
{
    boost::asio::io_service ios;

    tcp::resolver r(ios);
    tcp::resolver::query q(tcp::v4(), "127.0.0.1", "41000");
    tcp::resolver::iterator it = r.resolve(q);

    tcp::socket sock(ios);
    boost::asio::connect(sock, it);

    boost::thread t(boost::bind(&boost::asio::io_service::run,&ios));
    boost::asio::async_write(sock, boost::asio::buffer(teststr,102),
            boost::bind(&handle_write, &sock,
                boost::asio::placeholders::bytes_transferred,
                boost::asio::placeholders::error));
    t.join();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top