Data buffer wrapper problem.

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.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
	}


This looks suspicious. When the size isn't large enough to store the item, you reserve space, which does not increase the size of the vector, and you do not add the item. And since you don't increase the size of the vector, future calls to this function will fail in the same way.

Btw, if you're going to use memcpy you should probably include the appropriate header (<cstring>).
Thanks for the heads up cire, I'll use resize rather than reserve!

Unfortunately this doesn't solve the original problem, a size of 128 (the start size) should be more than enough for 3 chars and the header.
Last edited on
Topic archived. No new replies allowed.