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?