Boost ASIO - Why basic_stream_socket::available() return zero?

250 Views Asked by At

I am using Boost Asio and FlatBuffers to send messages over TCP.

Here is how I start the server:

#include "Config.h"
#include "Database/MySQLConnection.h"
#include "Implementation/LoginDatabase.h"
#include "Banner.h"
#include "Server/Server.h"
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using namespace std;
using namespace Vibranium;

int main() {
    //Don't mind Logger::FatalError it's just for coloring!
    Banner::Show(Logger::Error,"AuthServer");
    Config config("AuthServer");
    std::string defaultPort = "8080";
    MySQLConnectionInfo mySqlConnectionInfo(config, "LoginDatabaseInfo");
    LoginDatabaseConnection loginDatabaseConnection(mySqlConnectionInfo);
    loginDatabaseConnection.LoadDatabase();

    try
    {
        boost::asio::io_service io_service;
        Server s(io_service, std::stoi(config.GetConfigValue("AuthServerPort", defaultPort)));
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

Server.h:

using boost::asio::ip::tcp;
namespace Vibranium {
    class Server {
    public:
        Server(boost::asio::io_service &io_service, short port)
                : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
                  socket_(io_service) {
            do_accept();
            Logger::Log("Server Started! Listening on Port("+std::to_string(port)+")", Logger::Success, true);
        }
        static std::deque<std::shared_ptr<Client>> Clients;

    private:
        void do_accept();
        int incrementor = 0;
        tcp::acceptor acceptor_;
        tcp::socket socket_;
    };

}

Server.cpp:

#include "Server.h"
#include "Client.h"

using namespace Vibranium;
std::deque<std::shared_ptr<Client>> Server::Clients;
void Server::do_accept()
{
    acceptor_.async_accept(socket_,
   [this](boost::system::error_code ec)
   {
       if (!ec)
       {
           incrementor++;
           std::string sIncrementor = std::to_string(incrementor);
           Logger::Log("New Connection (ID: " + sIncrementor + ")",Logger::Success);
           std::shared_ptr<Client> c = std::make_shared<Client>(std::move(socket_));
           c->start();
           c->connectionId = incrementor;
           Server::Clients.push_back(c);
       }

       do_accept();
   });
}

Client.h:

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

namespace Vibranium{
    class Client: public std::enable_shared_from_this<Client>
    {
    public:
        Client(tcp::socket socket)
        : socket(std::move(socket))
        {
        }
        void start();
        int connectionId;
        tcp::socket socket;
        void Send(ServerOpcode serverOpcode, const std::string& message);
        void Disconnect() const;
    private:
        void read_message();
        Packet _packet;
    };
}

Client.cpp:

#include "Client.h"
#include "Server.h"
#include <iomanip>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include "AccountRole_Packet_generated.h"
#include "string"

using namespace Vibranium;

void Vibranium::Client::start() {
    read_message();
}

void Vibranium::Client::Send(ServerOpcode serverOpcode, const std::string& message) {
/*    auto self(shared_from_this());
    _packet.PreparePacket(serverOpcode, message);
    size_t request_length = sizeof(_packet.data_)/sizeof(*_packet.data_);
    boost::asio::async_write(socket, boost::asio::buffer(_packet.data_, request_length),
     [this, self](boost::system::error_code ec, std::size_t *//*length*//*)
     {
         if (ec)
             Logger::Log("Message sent failed to " + std::to_string(connectionId),Logger::Error);
     });*/
}

void Vibranium::Client::read_message() {
    auto self(shared_from_this());
    std::cout << "Wholesize:" << socket.available() << std::endl;
    if(socket.available() == 0){
        read_message();
        return;
    }
    _packet.body_.prepare(socket.available());
    boost::asio::async_read(socket,
    boost::asio::buffer(&_packet.body_, socket.available()),
    [this, self](boost::system::error_code ec,std::size_t bytes_transferred)
    {
        if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec))
        {
            Disconnect();
        }
        else
        {

            const char *HEADER = "ABVG";
            const char* buffIdentifier = flatbuffers::GetBufferIdentifier(&_packet.body_);
            bool hasIdentifier = flatbuffers::BufferHasIdentifier(&_packet.body_,HEADER);

            std::cout << "Builder Identifier" << buffIdentifier << std::endl;
            std::cout << "Builder Has Identifier" << hasIdentifier << std::endl;
            auto ac = GetAccountRole_Packet(&_packet.body_);
            std::cout << "ID" << ac->id() << std::endl;
            std::cout << "NAME:" << ac->name()->c_str() << std::endl;
            //read_body_size();
        }
    });
}


void Vibranium::Client::Disconnect() const {
    Logger::Log("Disconnected ID: " + std::__cxx11::to_string(this->connectionId), Logger::Error, true);
    for (int i = 0; i < Vibranium::Server::Clients.size(); ++i) {
        if(Vibranium::Server::Clients[i]->connectionId == this->connectionId)
            Vibranium::Server::Clients.erase(Vibranium::Server::Clients.begin() + i);
    }
}

Here is how I send Message:

flatbuffers::FlatBufferBuilder builder;
std::string str = "ABCDEFG";
auto name = builder.CreateString(str);
auto accountRole = Vibranium::CreateAccountRole_Packet(builder,5,name);
const char *HEADER = "ABVG";
builder.Finish(accountRole, HEADER);
size_t size = builder.GetSize();
uint8_t *buf = builder.GetBufferPointer();
boost::asio::write(s, boost::asio::buffer(buf,size));

Packet.h:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "string"
#include "Protocol/ServerOpcode.h"
#include "boost/asio.hpp"

class Packet {
public:
    Packet()
    {
    }
    boost::asio::streambuf body_;
    void PreparePacket(ServerOpcode serverOpcode, std::string message);
};

I've added into read_message in order to stop crashing when socket.available() == 0.

if(socket.available() == 0){
    read_header();
    return;
}

What I can't understand is why when I start the server and send several messages the out for the first few is as expected:

New Connection (ID: 1213410638)
Wholesize:40
Builder IdentifierABVG
Builder Has Identifier1
ID5
NAME:ABCDEFG
New Connection (ID: 1213410639)
Wholesize:40
Builder IdentifierABVG
Builder Has Identifier1
ID5
NAME:ABCDEFG
New Connection (ID: 1213410640)
Wholesize:40
Builder IdentifierABVG
Builder Has Identifier1
ID5
NAME:ABCDEFG
New Connection (ID: 1213410641)

And then suddenly out of nowhere on the next message I see this:

Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Wholesize:0
Builder IdentifierABVG
Builder Has Identifier1
ID5

Why this Wholesize:0 is happening on such randomness?

I track the messages I send over tcp with:

sudo ngrep -W byline -d any port 8085 -q

and for every message I send does not matter it shows Wholesize:0 or not I see the following output:

T 127.0.0.1:33174 -> 127.0.0.1:8085 [AP]
....ABVG........................ABCDEFG.

So where is my mistake, why Wholesize:0 keeps on showing here and there on complete random behavior, is there something wrong I am doing?

0

There are 0 best solutions below