2

I am quite new to boost asio and I am experiencing random End of File in a multi threaded server.

I could reproduce my problem in this small example:

Server:

This is a simple echo server. The protocol is straightforward :

  • (1) A client Connect
  • (2) The server reads one byte. This byte is the length of the string to read and send back.
  • (3) The server reads N bytes.
  • (4) The server send back N+1 bytes to the client and goes back to (2).

When the Client disconnect An EOF is captured in (3) and the handler loop stops.

class MySocket{
public:
    char buffer[257];
    boost::asio::ip::tcp::socket socket;
    MySocket(boost::asio::io_service*ios):socket(*ios){}
    ~MySocket(){}
};

//Handlers

void readN(std::shared_ptr<MySocket>server,const boost::system::error_code&ec);

//(4)
void echo(std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
    if(ec){
        throw std::exception(("This is NOT OK: "+ec.message()).c_str());}
    size_t n=server->buffer[0]&0xFF;
    std::cout<<std::string(server->buffer+1,n)<<std::endl;
    boost::asio::async_write(server->socket,boost::asio::buffer(server->buffer,n+1),boost::bind(readN,server,boost::asio::placeholders::error));}

//(3)
void read(std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
    if(ec){
        throw std::exception(("This is OK: "+ec.message()).c_str());}
    size_t n=server->buffer[0]&0xFF;
    boost::asio::async_read(server->socket,boost::asio::buffer(server->buffer+1,n),boost::bind(echo,server,boost::asio::placeholders::error));}

//(2)
void readN(std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
    if(ec){
        throw std::exception(("This is also NOT OK: "+ec.message()).c_str());}
    boost::asio::async_read(server->socket,boost::asio::buffer(server->buffer+0,1),boost::bind(read,server,boost::asio::placeholders::error));}

//Server

void serve(boost::asio::io_service*ios){
    for(;;){
        try{ios->run();break;}
        catch(const std::exception&e){std::cout<<e.what()<<std::endl;}}}

//(1)
void accept(boost::asio::io_service*ios,boost::asio::ip::tcp::acceptor*acceptor,std::shared_ptr<MySocket>server,const boost::system::error_code&ec){
    if(server.get()!=nullptr){
        server->socket.set_option(boost::asio::ip::tcp::no_delay(true));
        readN(server,ec);}
    server.reset(new MySocket(ios));
    acceptor->async_accept(server->socket,boost::bind(accept,ios,acceptor,server,boost::asio::placeholders::error));}

int main(){
    boost::asio::io_service ios;
    boost::asio::ip::tcp::acceptor acceptor(ios,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),1207));
    boost::asio::io_service::work work(ios);
    accept(&ios,&acceptor,nullptr,boost::system::error_code());
//    std::thread other(boost::bind(serve,&ios));
    serve(&ios);
    acceptor.close();
    ios.stop();
//    other.join();
    return 0;}

Client:

The client is connecting once to the server and sending 1000 strings.

int main(){
    try{
        boost::asio::io_service ios;
        boost::asio::ip::tcp::socket socket(ios);
        boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"),1207);
        socket.connect(endpoint);
        socket.set_option(boost::asio::ip::tcp::no_delay(true));
        char buf[257];
        for(size_t i=0;i<1000;++i){
            size_t n=(i%127)+1;
            buf[0]=(char)n;
            for(size_t j=0;j<n;++j){
                buf[j+1]=(char)('A'+(j+i)%26);}
            socket.send(boost::asio::buffer(buf,n+1));
            socket.receive(boost::asio::buffer(buf,1));
            if((buf[0]&0xFF)!=n){
                throw std::exception("Oups!");}
            socket.receive(boost::asio::buffer(buf+1,n));
            for(size_t j=0;j<n;++j){
                if(buf[j+1]!=(char)('A'+(j+i)%26)){
                    throw std::exception("Oups!");}}
            std::cout<<i<<": "<<std::string(buf+1,n)<<std::endl;}}
    catch(const std::exception&e){
        std::cout<<e.what()<<std::endl;}
    return 0;}

When The server uses only one thread (the tread other is commented) the server echos correctly the 1000 strings.

When The server uses the other thread, An EOF is captured in (4) after a random number of printed strings. This should never happen.

  • I tried wrapping all the async calls with a strand, but it did not work.
  • As far as I can see, there is no data race issue. Handlers should be called one after another.

What did I miss ?

What is the correct idiom to handle a multithreaded asio application ?

EDIT :

I did a few tests and it appears that if I replace this line

throw std::exception(("This is NOT OK: "+ec.message()).c_str());

with:

std::cout<<"This is not OK: "<<ec.message()<<std::endl;

The server echos correctly the 1000 lines even if I see that a few EOF were incorrectly passed as arguments a few times.

So I guess the question is why do I get an incorrect boost::asio::error::eof when the socket is obviously not closed ?

This is not what is stated here.

Community
  • 1
  • 1
Arnaud
  • 3,765
  • 3
  • 39
  • 69
  • I can't reproduce your issue with the code you posted - it seems to run fine here. If you observe the issue with this code, could you give more details on how to reproduce it ? – Sander De Dycker Sep 06 '13 at 12:57
  • I have Windows 7 64 bit, Visual Studio 2012 and boost 1.54 what else would you need ? – Arnaud Sep 06 '13 at 13:24
  • 1
    I have no windows system available to me - probably time to bring out the debugger :) Good luck ! – Sander De Dycker Sep 06 '13 at 13:37

2 Answers2

3

This is a bug of boost::asio 1.54.0

I found two similar threads on the internet:

There is also a bug report here.

I installed boost 1.53 and it is now working just fine.

Community
  • 1
  • 1
Arnaud
  • 3,765
  • 3
  • 39
  • 69
3

This issue was a regression and has been fixed in Boost 1.55: http://www.boost.org/users/history/version_1_55_0.html