Synchronous read/write operations

Once the connection is established, the client and server can communicate with each other. Like classical UNIX socket programming, boost::asio also provides send and receive functions. Use basic_stream_socket as an example and one pair of the implementations is like this:

  1. template <typename ConstBufferSequence>
  2. std::size_t send(const ConstBufferSequence& buffers)
  3. {
  4. ......
  5. }
  6. ......
  7. template <typename MutableBufferSequence>
  8. std::size_t receive(const MutableBufferSequence& buffers)
  9. {
  10. ......
  11. }

Please notice the buffer types of send/receive are ConstBufferSequence/MutableBufferSequence, and we can use boost::asio::buffer function to construct related types.

Below is a simple client program which sends “Hello world!“ to server after connection is established:

  1. #include <boost/asio.hpp>
  2. #include <iostream>
  3. int main()
  4. {
  5. try
  6. {
  7. boost::asio::io_context io_context;
  8. boost::asio::ip::tcp::endpoint endpoint{
  9. boost::asio::ip::make_address("10.217.242.61"),
  10. 3303};
  11. boost::asio::ip::tcp::tcp::socket socket{io_context};
  12. socket.connect(endpoint);
  13. std::cout << "Connect to " << endpoint << " successfully!\n";
  14. socket.send(boost::asio::buffer("Hello world!"));
  15. }
  16. catch (std::exception& e)
  17. {
  18. std::cerr << e.what() << '\n';
  19. return -1;
  20. }
  21. return 0;
  22. }

There is the server program which waits receiving greeting from client:

  1. #include <boost/asio.hpp>
  2. #include <iostream>
  3. int main()
  4. {
  5. try
  6. {
  7. boost::asio::io_context io_context;
  8. boost::asio::ip::tcp::acceptor acceptor{
  9. io_context,
  10. boost::asio::ip::tcp::endpoint{boost::asio::ip::tcp::v4(), 3303}};
  11. while (1)
  12. {
  13. boost::asio::ip::tcp::socket socket{io_context};
  14. acceptor.accept(socket);
  15. std::cout << socket.remote_endpoint() << " connects to " << socket.local_endpoint() << '\n';
  16. char recv_str[1024] = {};
  17. socket.receive(boost::asio::buffer(recv_str));
  18. std::cout << "Receive string: " << recv_str << '\n';
  19. }
  20. }
  21. catch (std::exception& e)
  22. {
  23. std::cerr << e.what() << '\n';
  24. return -1;
  25. }
  26. return 0;
  27. }

Build and run programs. Client outputs following:

  1. $ ./client
  2. Connect to 10.217.242.61:3303 successfully!

Server outputs following:

  1. $ ./server
  2. 10.217.242.21:64776 connects to 10.217.242.61:3303
  3. Receive string: Hello world!

If no error occurs, send can guarantee at least one byte is sent successfully, and you should check the return value to see whether all bytes are sent successfully or not. receive is similar as send. boost::asio::basic_stream_socket also provides read_some and write_some which have the same functions as receive and send.

If we don’t bother to check the middle state (partial bytes are sent successfully), and only care whether all bytes are sent successfully or not, we can use boost::asio::write which actually uses wrtie_some under the hood. Correspondingly, it is not hard to guess what boost::asio::readdoes.