Create an iostream using boost asio specifying ip and port
Asked Answered
B

3

6

I have a problem concerning boost asio libraries. I successfully tried to create a socket between a client and a server, this involves creation of resolvers in order to specify ip and port to the server (the server only requires port) and other objects, but, most importantly, it is necessary to use write and read_some as functions to read and write from/in the socket. I would really appreciate to use a stream, and this is possible in boost asio, but that's strange... In almost all examples using streams, to create a server it is necessary to provide port, ok, let's talk about the client... client side, it is necessary to use the iostream constructor to specify coordinates for connecting the stream, here's the code:

tcp::iostream() s(argv[1], "daytime");

Well, I don't really understand what is passed in the first parameter and really don't know what daytime might ever represent... Basically, here, I'm telling: "Hey stream, you must connect to this server..." but how can I specify ip and port of that server? Note that, on the opposite, everything is almost clear server side:

boost::asio::io_service io_s;
tcp::acceptor acc(io_s, tcp::endpoint(tcp::v4(), 1950));
for (;;) {
   tcp::iostream stream;
   acc.accept(*stream.rdbuf());
   stream << "Message" << std::endl;
}

Using this model, I would like to use

stream << mymessage_to_send << std::endl;
stream >> a_string_containing_my_message;

in order to send and receive. How can I do this? Thank you very much.

Bottali answered 25/11, 2010 at 8:32 Comment(0)
T
1

I've written a client/server system using Boost.Asio. The source is available on GitHub: Client.cpp and Server.cpp. Using Boost.Serialization together with Boost.Asio allows me to send arbitrary datastructures over the wire. I must say it is quite impressive!

Timeless answered 25/11, 2010 at 8:54 Comment(7)
OK, going to check it immediately... Thank youBottali
You're free to ask me questions about my implementation, if you wish. Good luck!Celestinacelestine
Well, I'm trying some things looking at your code... I'm not as experienced as you... well, questions are going to arrive, now I can just say: Amazing code!!!!!Bottali
Thank you Mr. Lidstrom... I could enstablish a connection using streams... Well, I am very interested in Serialization combined with Asio. In your project, can you tell me where I can find serialization process? I suppose this code in is another file (not server.cpp or client.cpp).Bottali
Great! Have a look at the commands. For example, FetchCommand and SyncCommand2. They are used in Server.cpp and Client.cpp.Celestinacelestine
Not everyone can use github. Some companies block it. And answers here shouldn't rely on someone downloading something from github. Bad answer. Just answer the question.Catalonia
@Catalonia this is the accepted answer, so I guess I did…Celestinacelestine
N
19

The boost asio sample code you quoted:

tcp::iostream s(argv[1], "daytime");

uses "daytime" as a lookup into the services table (usually in /etc/services on a linux system), which would identify that the port for the daytime service is 13.

If you want to connect to a port that is not one of the well known services, you can do so with something like:

tcp::iostream s("localhost", "57002"); 

Note that the port number is supplied as a string, not as an unsigned short integer as one might be tempted to try.

Of course, "localhost" can be replaced with an IP address "127.0.0.1"

Novitiate answered 18/10, 2013 at 18:29 Comment(4)
This should be the selected answer.Ray
Other than the fact that there should be no () after iostream otherwise you're returning a function?Catalonia
@Catalonia - lol! I just copied from the OP, and clarified. But good catch!Novitiate
That is good for creating the iostream but not good for the streaming itself unfortunately due to the poor nature of iostream itself. Therefore I suggest to use an archive object which handles 2-way inbound/outbound and will block until the full object is streamed in.Catalonia
C
2

Let's solve all 3 issues here:

Creating the iostream around the socket client side.

This is really simple:

boost::asio::ip::tcp::iostream socketStream;
socketStream.connect( hostname, std::to_string( port ) );

You have to check the state of the stream to see if it connected successfully.

Creating the iostream around the socket server side.

Assuming you have your acceptor object and it is bound and listening..

boost::asio::ip::tcp::iostream connectionSocketStream; // from the connection object
acceptor.accept( *connectionSocketStream.rdbuf() );

// or

acceptor.async_accept( *connectionSocketStream.rdbuf(), callback );

where callback is a function that takes an error code.

Streaming the objects

Now for the streaming itself and here your issue is that when you stream out the string "Message" the client side will need to know where this message begins and ends, and the regular iostream won't write anything to specify this. This is a flaw in iostream itself really.

The answer therefore is to use a boost archive, and you can use a text or binary archive as long as you use the same both ends. It even doesn't matter if one side is using 64-bit big-endian and the other side 32-bit little endian or any other mix.

Using binary archive you would send a message this way:

boost::archive::binary_oarchive oarch( socketStream, boost::archive::no_header );
oarch << "Message";

Remember to flush the stream (socketStream, not oarch) when you have completed sending all you wish to send at this point.

and receive a message

boost::archive::binary_iarchive iarch( socketStream, boost::archive::no_header );
iarch >> message;

You would potentially create one archive and use it throughout, especially for outbound. For inbound you may have issues if you get a streaming error as it will break your archive.

You can use a text archive instead of a binary one.

The boost archive will automatically put in header information so it knows when an object is complete and will only return to you once it has a complete object or something has broken.

Note: primitive types, eg std::string and even vector< int > etc. are automatically handled by an archive. Your own classes will need special overloads as to how to stream them. You should read boost::archive documentation.

Note: You can connect the archive object to the stream before the stream has been opened. The archive works around the streambuf object which does not change dependent on the stream opening successfully.

Creating without no_header would be an issue though as the archives immediately try to use the stream on construction (to read or write their header)

Catalonia answered 19/7, 2017 at 9:15 Comment(0)
T
1

I've written a client/server system using Boost.Asio. The source is available on GitHub: Client.cpp and Server.cpp. Using Boost.Serialization together with Boost.Asio allows me to send arbitrary datastructures over the wire. I must say it is quite impressive!

Timeless answered 25/11, 2010 at 8:54 Comment(7)
OK, going to check it immediately... Thank youBottali
You're free to ask me questions about my implementation, if you wish. Good luck!Celestinacelestine
Well, I'm trying some things looking at your code... I'm not as experienced as you... well, questions are going to arrive, now I can just say: Amazing code!!!!!Bottali
Thank you Mr. Lidstrom... I could enstablish a connection using streams... Well, I am very interested in Serialization combined with Asio. In your project, can you tell me where I can find serialization process? I suppose this code in is another file (not server.cpp or client.cpp).Bottali
Great! Have a look at the commands. For example, FetchCommand and SyncCommand2. They are used in Server.cpp and Client.cpp.Celestinacelestine
Not everyone can use github. Some companies block it. And answers here shouldn't rely on someone downloading something from github. Bad answer. Just answer the question.Catalonia
@Catalonia this is the accepted answer, so I guess I did…Celestinacelestine

© 2022 - 2024 — McMap. All rights reserved.