Search code examples
pythonc++socketsboostdata-conversion

How to convert received bytes in Boost C++ to a unsigned int and to a vector of unsigned int


I'm sending via Python TCP Socket an unsigned integer to a client written in Boost C++. I would like to know how to convert the received bytes in the Boost C++ socket into an unsigned integer.

Python Socket Server

import socket
import struct

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    print("listening..")
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)

        # SENDING 10 AS AN UNSIGNED INTEGER
        conn.send(struct.pack("I",10))

C++ Boost Client Socket

#include <boost/asio/local/stream_protocol.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>

using namespace std;

// CREATE SOCKET
boost::asio::io_service io_context;
boost::asio::streambuf sb;
boost::system::error_code ec;
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 10000);
boost::asio::ip::tcp::socket socket(io_context);
socket.connect(ep);

// READ 4 BYTES
boost::asio::read(objSocket, sb,boost::asio::transfer_exactly(4), ec))

cout << "\nreceived: '" << &sb << "'\n";

// CONVERT 4 BYTES TO UNSIGNED INT
boost::asio::streambuf::const_buffers_type bufs = sb.data();

string str(boost::asio::buffers_begin(bufs), boost::asio::buffers_begin(bufs) + sb.size());

sb.consume(sb.size());

std::stringstream sstr(str);

unsigned int receivedInt = 0;

sstr >> receivedInt;

cout << "\nReceivedInt: "<< length;

PS: The C++ client receives the data (4bytes) but I can't convert it to uint.

And how to convert bytes to a vector of unsigned int?

Thank you in advance!


Solution

  • At the Python side you don't specify byte order for your bytes stream. So it will depend on the machine, it could be little-endian or big-endian. It may lead to problems.

    You can force bytes stream to be sent for example in little-endian by:

    conn.send(struct.pack("<I",10))    # added < 
    

    In C++, by calling sstr >> receivedInt you perform formatted reading of data. It could work if your stream held "10" as string - 2 characters, but your stream contains 4 bytes - binary representation of 10 as decimal. You have to just merge values of all bytes into unsigned int, which is easy because you know the order of bytes:

    boost::asio::streambuf::const_buffers_type bufs = sb.data();
    string str(boost::asio::buffers_begin(bufs), boost::asio::buffers_begin(bufs) + sb.size());
    sb.consume(sb.size());
    uint32_t received = 
        (str[0] & 0xFF) 
      | (str[1] << 8) & 0xFF00 
      | (str[2] << 16) & 0xFF0000
      | (str[3] << 24);
    std::cout << received << std::endl;  // 10 
    

    Data is sent in little-endian order so when merging it into uint32 data must be shifted like this:

    [str[3] | str[2] | str[1] | str[0]]
    

    Before shifting char is promoted to be int with extending a bit of sign, so you have to perform bitwise AND with proper masks to zero most left bits.