开发者

int/char conversion in network frames with C++/boost::asio

开发者 https://www.devze.com 2023-04-07 22:35 出处:网络
Using g++ and boost::asio, I\'m trying to format network message frames containing the size of the data to be transmited:

Using g++ and boost::asio, I'm trying to format network message frames containing the size of the data to be transmited:

+----------------+------------------------+
|  Length        |  Message string        |
|        4 bytes |                n bytes |
+----------------+------------------------+

Using the examples of asio blocking tcp echo client/server under a 32 byte Linux, I'm stucked with transmitting the correct size in the frame. The client is supposed to be a Windows machine.

CLIENT:

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
#include <stdint.h>
#include <arpa/inet.h>

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

enum { max_length = 1024 };

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 3)
    {
      std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
    tcp::resolver::iterator iterator = resolver.resolve(query);

    tcp::socket s(io_service);
    s.connect(*iterator);

    std::cout << "sizeof uint32_t=" << sizeof(uint32_t) << std::endl;

    uint32_t len = 1234;
    std::cout << "len=" << len << std::endl;

    // uint => char 4
    char char_len[4];
    char_len[0] = (len >> 0);
    char_len[1] = (len >> 8);
    char_len[2] = (len >> 16);
    char_len[3] = (len >> 24);

    std::cout << "char[4] len=[" 
        << char_len[0] << ',' << char_len[1] << ',' 
        << char_len[2] << ',' << char_len[3] << ']' 
        << std::endl;

    // char 4 => uint
    uint32_t uint_len = *(reinterpret_cast<uint32_t *>( char_len ));
    std::cout << "uint len=" << uint_len << std::endl;

    // network bytes order
    uint32_t net_len = htonl( len );
    std::cout << "net_len=" << net_len << std::endl;

    // uint => char 4
    char net_char_len[4];
    net_char_len[0] = (net_len >> 0);
    net_char_len[1] = (net_len >> 8);
    net_char_len[2] = (net_len >> 16);
    net_char_len[3] = (net_len >> 24);

    std::cout << "net char[4] len=[" 
        << net_char_len[0] << ',' << net_char_len[1] << ',' 
        << net_char_len[2] << ',' << net_char_len[3] << ']' 
        << std::endl;

    boost::asio::write(s, boost::asio::buffer(char_len, 4));

    char reply[max_length];
    size_t reply_length = boost::asio::read(s,
        boost::asio::buffer(reply, 1 ));
    std::cout << "Reply is: ";
    std::cout.write(reply, reply_length);
    std::cout << "\n";
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

SERVER:

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

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

const int max_length = 1024;

typedef boost::shared_ptr<tcp::socket> socket_ptr;

void session(socket_ptr sock)
{
  try
  {
    for (;;)
    {
      char data[max_length];

      boost::system::error_code error;
      size_t length = soc开发者_如何学运维k->read_some(boost::asio::buffer(data), error);
      if (error == boost::asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw boost::system::system_error(error); // Some other error.

      uint32_t net_len;
      size_t len_len = sock->read_some( boost::asio::buffer( reinterpret_cast<char*>(&net_len), 4), error );
      std::clog << "net len=" << net_len << std::endl;
      uint32_t len = ntohl( net_len );
      std::clog << "uint len=" << len << std::endl;

      char char_len = (char)len;
      std::clog << "char len=" << len << std::endl;

      boost::asio::write(*sock, boost::asio::buffer( &char_len, size_t(1)));
    }
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception in thread: " << e.what() << "\n";
  }
}

void server(boost::asio::io_service& io_service, short port)
{
  tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
  for (;;)
  {
    socket_ptr sock(new tcp::socket(io_service));
    a.accept(*sock);
    boost::thread t(boost::bind(session, sock));
  }
}

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    std::cout << "Started" << std::endl;
    std::cout << "sizeof uint32_t=" << sizeof(uint32_t) << std::endl;
    using namespace std; // For atoi.
    server(io_service, atoi(argv[1]));
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

Should compile with a simple script:

g++ -o client -I/usr/include/boost -L/usr/lib -lboost_system -lboost_thread-mt  client.cpp
g++ -o server -I/usr/include/boost -L/usr/lib -lboost_system -lboost_thread-mt  server.cpp

The client is unable to decode the length and the server does not seems to receive the right irt. What am I missing?


One problem seems to be that the client send some data, then want to receive something and send it back to the server, but the server does 2 reads and then it sends something. So, the 2nd read() will probably receive 0 bytes.

Also, the read() function reads as long there is some bytes available. In that case, even if the client do 2 write(), the server can read all the bytes with the first read() and the 2nd read() will still see 0 bytes. You have to handle the data stored in the buffer based on your protocol.

Your conversion from len to char_len will not work as expected. As the char_len array contains chars, the cout try to display the char on the screen, but any value below 32 is not displayable. If you want to display the ASCII value of each char, you have to cast them in int just before giving them to cout.

You should simply send "len" as-is:

boost::asio::write(s, boost::asio::buffer(&len, 4));

But I did not saw any message sent in you sample. I only see some lenght being exchanged between the client and the server.

That's all that I can say based on that code.

Edit: I left the byte-order out the keep the respond short.


From what I can see in your code, you're actually not sending network byte order, your send statement is like: boost::asio::write(s, boost::asio::buffer(char_len, 4)); and char_len looks like it's filled in host order...

May be you ought to provide the output from your log statements - it may make things a little clearer...


I finally manage to find what went wrong.

There was a residual read at the bottom of the server code, that was preventing the second one to read the correct bytes, as said @PRouleau.

The client was also writing the wrong variable, as @Nim pointed out.

The sevrer's error check was not at the right place.

Finally I get the following outpout:

Client:

sizeof uint32_t=4
len=1234
uint len=1234
net_len=3523477504
net char[4] len=[0,0,4,-46]

Server:

Started
sizeof uint32_t=4
net len=3523477504
uint len=1234

With the following code :

CLIENT:

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
#include <stdint.h>
#include <arpa/inet.h>

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

enum { max_length = 1024 };

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 3)
    {
      std::cerr << "Usage: blocking_tcp_echo_client <host> <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
    tcp::resolver::iterator iterator = resolver.resolve(query);

    tcp::socket s(io_service);
    s.connect(*iterator);

    std::cout << "sizeof uint32_t=" << sizeof(uint32_t) << std::endl;

    uint32_t len = 1234;
    std::cout << "len=" << len << std::endl;

    // uint => char 4
    char char_len[4];
    char_len[0] = (len >> 0);
    char_len[1] = (len >> 8);
    char_len[2] = (len >> 16);
    char_len[3] = (len >> 24);

    std::cout << "char[4] len=[" 
        << char_len[0] << ',' << char_len[1] << ',' 
        << char_len[2] << ',' << char_len[3] << ']' 
        << std::endl;

    // char 4 => uint
    uint32_t uint_len = *(reinterpret_cast<uint32_t *>( char_len ));
    std::cout << "uint len=" << uint_len << std::endl;

    // network bytes order
    uint32_t net_len = htonl( len );
    std::cout << "net_len=" << net_len << std::endl;

    // uint => char 4
    char net_char_len[4];
    net_char_len[0] = (net_len >> 0);
    net_char_len[1] = (net_len >> 8);
    net_char_len[2] = (net_len >> 16);
    net_char_len[3] = (net_len >> 24);

    std::cout << "net char[4] len=[" 
        << net_char_len[0] << ',' << net_char_len[1] << ',' 
        << net_char_len[2] << ',' << net_char_len[3] << ']' 
        << std::endl;

    std::cout << "net char[4] len=[" 
        << (int)net_char_len[0] << ',' << (int)net_char_len[1] << ',' 
        << (int)net_char_len[2] << ',' << (int)net_char_len[3] << ']' 
        << std::endl;


    boost::asio::write(s, boost::asio::buffer( net_char_len, 4));

    /*
    char reply[max_length];
    size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply, 1 ));

    std::cout << "Reply is: ";
    std::cout.write(reply, reply_length);
    std::cout << "\n";
    */
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

SERVER:

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

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

const int max_length = 1024;

typedef boost::shared_ptr<tcp::socket> socket_ptr;

void session(socket_ptr sock)
{
  try
  {
    for (;;)
    {
      char data[max_length];

      boost::system::error_code error;

      uint32_t net_len;
      size_t len_len = sock->read_some( boost::asio::buffer( reinterpret_cast<char*>(&net_len), 4), error );

      if (error == boost::asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw boost::system::system_error(error); // Some other error.

      std::cout << "net len=" << net_len << std::endl;

      uint32_t len = ntohl( net_len );
      std::cout << "uint len=" << len << std::endl;

      //char reply = '1';
      //std::cout << "char len=" << char_len << std::endl;
      //boost::asio::write(*sock, boost::asio::buffer( &reply, size_t(1)));
    }
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception in thread: " << e.what() << "\n";
  }
}

void server(boost::asio::io_service& io_service, short port)
{
  tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
  for (;;)
  {
    socket_ptr sock(new tcp::socket(io_service));
    a.accept(*sock);
    boost::thread t(boost::bind(session, sock));
  }
}

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: blocking_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    std::cout << "Started" << std::endl;
    std::cout << "sizeof uint32_t=" << sizeof(uint32_t) << std::endl;
    using namespace std; // For atoi.
    server(io_service, atoi(argv[1]));
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号