0
votes

I am currently writing a TCP server using boost::asio TCP socket. I've used the examples provided to make the server be statefull by wrapping the socket object in my own session object.

Everything works fine except for the disconnect. In my setup I always have a async_read_some call for each opened socket.
Normal behavior is when I connect a client and disconnect it. The data read handler is called with the error being set so I know that I have a socket disconnected.
The bad behavior is when I connect 2 clients in one order and close them in the same order. When I close the first client I get no handler being called but when I close the second one I get also the call for the first client, both to notify the disconnect.
Except for this issue everything seems to be working nicely. Send and receive of data on multiple clients is working fine.

Anyone had this issue ? What could be the cause for this ?

bool TCPNetworkListener::StartListener()
{
    //check if already started
    if (m_SocketAcceptor.is_open())
    {
        return false;
    }

    //create the socket acceptor
    m_SocketAcceptor.open(m_TCPEndpoint.protocol());
    m_SocketAcceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
    m_SocketAcceptor.bind(m_TCPEndpoint);
    m_SocketAcceptor.listen();

    //accept async connections
    m_lastConnectedClientId++;
    TCPNetworkSession::SharedPtr new_session(new TCPNetworkSession(m_IOService, this, m_lastConnectedClientId));
    m_SocketAcceptor.async_accept(new_session->GetSocket(),
                                    boost::bind(&TCPNetworkListener::HandleNewConnection, this, new_session,
                                    boost::asio::placeholders::error));

    //all fine
    m_listenerState = NetworkListener::NLState_Running;
    return true;
}

void TCPNetworkListener::HandleNewConnection( TCPNetworkSession::SharedPtr session, const boost::system::error_code& error )
{
    if (error)
    {
        return;
    }

    session->StartNewSession();

    //accept new async connections
    m_lastConnectedClientId++;
    TCPNetworkSession::SharedPtr new_session(new TCPNetworkSession(m_IOService, this, m_lastConnectedClientId));
    m_SocketAcceptor.async_accept(new_session->GetSocket(),
        boost::bind(&TCPNetworkListener::HandleNewConnection, this, new_session,
        boost::asio::placeholders::error));
}

The session looks like this:

TCPNetworkSession::TCPNetworkSession( boost::asio::io_service& io_service , TCPNetworkListener* handler, long clientId) :
    m_socket(io_service),
    m_IOService(io_service),
    m_networkHandler(handler),
    m_clientId(clientId),
    m_sendingData(false)
{
}

tcp::socket& TCPNetworkSession::GetSocket()
{
    return m_socket;
}

void TCPNetworkSession::StartNewSession()
{
    //notify of new connection established
    if (m_networkHandler)
    {
        m_networkHandler->ClientConnected(shared_from_this());
    }

    m_socket.set_option(boost::asio::socket_base::keep_alive(true));

    //try to read the first message data
    m_socket.async_read_some(
        boost::asio::buffer(m_readBuffer, SOCKETBUFFERLENGTH),
        boost::bind(&TCPNetworkSession::HandleReadData, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

void TCPNetworkSession::HandleReadData( const boost::system::error_code& error, std::size_t bytes_transferred )
{
    if (error)
    {
        //we have got disconnected
        if (m_networkHandler)
        {
            m_networkHandler->ClientDisconnected(shared_from_this());
        }

        return;
    }

    //we received new data
    if (m_networkHandler)
    {
        m_readBuffer[bytes_transferred] = 0;
        std::string strData(reinterpret_cast<const char*>(m_readBuffer), bytes_transferred);
        m_networkHandler->ClientDataReceived(shared_from_this(), strData);
    }

    //try to read the next message data
    m_socket.async_read_some(
        boost::asio::buffer(m_readBuffer, SOCKETBUFFERLENGTH),
        boost::bind(&TCPNetworkSession::HandleReadData, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
1
@sehe Thank you for your suggestions but I'm not sure that understand: what do you mean when you say sharing too much information or binding on wrong instance ? The example I've used is the one in the Daytime TCP server from Boost. In the meantime I compiled the code on Linux and it works fine. The problem appears on Windows 7 64 bit with Visual Studio 2010 while on Centos 6 64bit is working fine. The Boost version is 1.41 on both platforms.tvlada
Perhaps, something in your code (maybe in a completion handler) temporarily blocks the thread running io_service::run, and when this blocking gets released you receive the both handlers? (BTW, 1.41 is a bit outdated)Igor R.
@tvlada All your circumstantial evidence doesn't make up for the lack of code.sehe
The cause is that your code is broken. There are a great many ways in which your code can be broken.Puppy
@sehe I think I added the relevant part of the code.I know that 1.41 is old but this is what is currently distributed by CentOS. Itvlada

1 Answers

0
votes

The problem was in putty. I was using it for testing the server using telnet and duplicating the sessions as needed. There seems to be a bug in putty that causes the socket not to get closed when you close the windows for a session. The sockets are all closed only when the last session of putty is closed.

Thank you all for the help provided