So I've been learning boost asio, and I wish to implement my own wrapper for data. The wrapper class:
data_wrapper.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
|
#pragma once
#include <iostream>
#include <vector>
namespace util
{
///Packet data wrapper
/**
* Leftmost and/or first will be the first element, in both extraction and insertion.
*
* @example:
*
* Input:
* Chain calling:
* packet << first << second << n;
*
* Multiline calling:
* packet << first;
* packet << second
* packet << n;
*
* Output:
* Chain calling:
* packet >> n >> second >> first;
*
* Multiline calling:
* packet >> n;
* packet >> second;
* packet >> first;
*/
class data_wrapper
{
private:
std::size_t _dat_element = 0, _current_element = header_length;
std::vector<char> _data;
public:
enum
{
//Header contains the following:
/// Amount of bytes of the data,
/// Amount of different items
header_length = sizeof(std::size_t) * 2,
start_size = 128
};
data_wrapper();
~data_wrapper();
void update_header();
char* get_buffer();
std::vector<char>& get_vector();
std::size_t get_len();
template<typename _T>
data_wrapper& operator<<(const _T&);
template<typename _T>
data_wrapper& operator>>(_T&);
};
template<typename _T>
inline data_wrapper & data_wrapper::operator<<(const _T& type_arg)
{
if (this->_data.size() < this->_current_element + sizeof(_T))
{
std::size_t diff = this->_current_element + sizeof(_T);
this->_data.reserve(this->_data.size() + diff + 30);
}
else
{
//Here the dest is the data buffer, the source is the type and the size is the size of the type
std::memcpy(this->_data.data() + this->_current_element, &type_arg, sizeof(_T));
//Move the "data iterator" forward so the next data can be
this->_current_element += sizeof(_T);
//Keep track of element count
this->_dat_element++;
}
return *this;
}
template<typename _T>
inline data_wrapper & data_wrapper::operator>>(_T& type_arg)
{
if (header_length <= this->_current_element - sizeof(_T))
{
//Move the "data iterator" back so the earlier data can be extracted
this->_current_element -= sizeof(_T);
//Here the dest is the provided argument, the source is the data buffer and the size is the size of the type
std::memcpy(&type_arg, this->_data.data() + this->_current_element, sizeof(_T));
//Keep track of the element count
this->_dat_element--;
}
return *this;
}
}
| |
data_wrapper.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
#include "data_wrapper.h"
namespace util
{
void data_wrapper::update_header()
{
std::size_t* ptr = (std::size_t*)this->_data.data();
ptr[0] = this->_current_element;
ptr[1] = this->_dat_element;
}
data_wrapper::data_wrapper()
{
this->_data.resize(start_size);
}
data_wrapper::~data_wrapper()
{
}
char* data_wrapper::get_buffer()
{
update_header();
return this->_data.data();
}
std::vector<char>& data_wrapper::get_vector()
{
update_header();
return this->_data;
}
std::size_t data_wrapper::get_len()
{
return this->_current_element;
}
}
| |
tcp_client.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
#pragma once
#include <iostream>
#include <boost\asio.hpp>
#include "data_wrapper.h"
typedef boost::asio::ip::tcp tcp;
class tcp_client
{
private:
tcp::socket _sock;
tcp::resolver _resolv;
public:
tcp_client(boost::asio::io_service&, tcp::endpoint&);
~tcp_client();
void send_data(util::data_wrapper&);
void receive_data(util::data_wrapper&);
};
| |
tcp_client.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
#include "tcp_client.h"
tcp_client::tcp_client(boost::asio::io_service& service_arg, tcp::endpoint& dest_arg)
: _sock(service_arg), _resolv(service_arg)
{
boost::asio::connect(this->_sock, this->_resolv.resolve(dest_arg));
}
tcp_client::~tcp_client()
{
}
void tcp_client::send_data(util::data_wrapper& pack_arg)
{
this->_sock.send(boost::asio::buffer(pack_arg.get_buffer(), pack_arg.get_len()));
}
void tcp_client::receive_data(util::data_wrapper& pack_arg)
{
boost::asio::read(this->_sock, boost::asio::buffer(pack_arg.get_buffer(), util::data_wrapper::header_length));
//These lines are for debug purposes
std::size_t* ptr = (std::size_t*)&(pack_arg.get_buffer()[0]);
std::cout << "Len received: " << *ptr << ", len of message: " << *ptr - util::data_wrapper::header_length << std::endl;
//Debug end
pack_arg.update_header();
boost::asio::read(this->_sock, boost::asio::buffer(pack_arg.get_buffer() + util::data_wrapper::header_length, pack_arg.get_len()));
}
| |
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
#include <iostream>
#include "tcp_client.h"
#include "data_wrapper.h"
int main() {
try
{
boost::asio::io_service service;
boost::asio::io_service::work w(service);
boost::asio::ip::tcp::endpoint end(boost::asio::ip::address::from_string("127.0.0.1"), 12312);
util::data_wrapper packet, rec_packet;
char dat_1 = 'A', dat_2 = 'b', dat_3 = 'q';
packet << dat_1 << dat_2 << dat_3;
tcp_client client(service, end);
std::cout << "Len sent: " << packet.get_len() << std::endl;
client.send_data(packet);
client.receive_data(rec_packet);
char __1, __2, __3;
rec_packet >> __3 >> __2 >> __1;
std::cout << __1 << __2 << __3 << std::endl;
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
system("pause");
return 0;
}
| |
The server used is an echo server taken from the boost::asio examples, and it works like expected; it sends the exact bytes it receives back to the sender.
The packet class also works like intended (it might need some tweaks for ease of use, but ultimately it can insert and remove elements from a single char array easily), also the vector is only used for easing the reallocation by using the resize method.
The problem rises in the receive method in the tcp_client class:
Client side:
Len sent: 11
Len received: 8, len of message: 0
Server side:
11
3
Abq
I've just added some cout's that print the input, length and element count as the packet class provides this in the header.
As you see the "len of messasge: 0" should be 3. It receives 8 as the header length, which surprises me.
The receiving packet should first have its header overridden by the first read, then update its information with the update_header() method and finally receive the rest of the data and append it behind the header (pack_arg.get_buffer() + util::data_wrapper::header_length).
I noticed something funny when I used a char buffer instead of the pack_arg.getbuffer() :
New tcp_client::receive_data(util::data_wrapper&):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
void tcp_client::receive_data(util::data_wrapper& pack_arg)
{
char buf[128];
boost::asio::read(this->_sock, boost::asio::buffer(buf, util::data_wrapper::header_length));
//These lines are for debug purposes
std::size_t* ptr = (std::size_t*)&(buf[0]);
std::cout << "Len received: " << *ptr << ", len of message: " << *ptr - util::data_wrapper::header_length << std::endl;
//Debug end
pack_arg.update_header();
boost::asio::read(this->_sock, boost::asio::buffer(pack_arg.get_buffer() + util::data_wrapper::header_length, pack_arg.get_len()));
}
| |
Which in turn provided the output on the client side:
Len sent: 11
Len received: 11, len of message: 3
I simply do not understand what could be going on that causes the pack_arg.get_buffer() method to not behave as I expect.