2
\$\begingroup\$

I'm new to socket programming and c++. I have a the following method that sends an array to server and receives sum of the array, I tried to make it async.

std::future<void> AsyncClient::ReqSum(const std::vector<double>& numbers)
{
    return std::async(
        std::launch::async,
        [&, numbers]()
        {
            // Setup connection. Can move to seperate method?
            boost::asio::io_service _ioservice;
            tcp::socket _socket(_ioservice);
            _socket.connect(m_endpoint);

            std::string requestJson = Serializer::ArraySumRequest(numbers);
            boost::system::error_code err;
            
            boost::asio::write(_socket, boost::asio::buffer(requestJson), err);
            if (err)
            {
                std::stringstream ss;
                ss << "Couldn't write to socket! error code: " << err;
                throw ss.str();
            }

            // getting response from server
            boost::asio::streambuf receiveBuffer;
            boost::asio::read(_socket, receiveBuffer, boost::asio::transfer_all(), err);

            if (err && err != boost::asio::error::eof)
            {
                std::stringstream ss;
                ss << "Receiving from the server failed! error code: " << err.message();
                throw ss.str();
            }

            const char* data = boost::asio::buffer_cast<const char*>(receiveBuffer.data());

            rapidjson::Document doc;
            doc.Parse<rapidjson::kParseDefaultFlags>(data);

            if (!doc.HasMember("result"))
                throw "No result found!";

            double result = doc["result"].GetDouble();

            std::cout << "Sum of the array: " << result << std::endl;

            _socket.shutdown(boost::asio::socket_base::shutdown_both);
            _socket.close();
        });
}

Actually I want to know is it possible to use one connection to for multiple requests(i.e move that socket and connection to another method)?

And if there are any fixes to my code please let me know.

\$\endgroup\$
  • \$\begingroup\$ Depends what is listening on the other end of the socket. A normal "modern" HTTP server yes you can leave the socket open and make multiple requests on the same connection. \$\endgroup\$ – Martin York Sep 26 at 18:22
  • \$\begingroup\$ @MartinYork My server is like this(it's in c#), except that I do not call close and shutdown on SendCallback. \$\endgroup\$ – H.Ebr Sep 26 at 19:09
2
\$\begingroup\$

I want to know is it possible to use one connection to for multiple requests(i.e move that socket and connection to another method)?

In principle, yes. The easiest way would be to not create a socket in this method, but leave it up to the caller to create a socket, and pass a reference to it to AsyncClient::ReqSum(), so that the socket can be reused for multiple requests.

However, as you mentioned this doesn't work, and that is because you rely on the server to close the connection right after sending the response to a single request:

boost::asio::streambuf receiveBuffer;
boost::asio::read(_socket, receiveBuffer, boost::asio::transfer_all(), err);

The read() command here doesn't know how large the response is, but you told it to transfer all data sent by the server. The only way to signal the end of all data is for the server to close the connection. If the server would support multiple requests on a connection, then you need some way to deliniate responses. And in that case, you will have to use a function like boost::asio::read_until() to read data up to a certain point.

Don't throw strings

First of all, constructing a string using std::stringstream is quite expensive. But the main issue is that you are throwing a plain std::string. The problem is that any error handling code now has no idea what kind of error happened, unless it parses that string. That will also be inefficient.

You should instead throw a type that inherits from std::exception. In this case, either throw a std::runtime_error, or if you wish some custom type that inherits from that.

Avoid using std::endl

Prefer using "\n" instead of std::endl; the latter is equivalent to the former, but also forces a flush of the output stream, which is usually not necessary and might be bad for performance.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer. Your feedbacks were very helpful to me, but about the first one, it doesn't seem to be that easy(or I couldn't do that). I get an error when want to read from the socket for the second time. I posted my problem here as well. \$\endgroup\$ – H.Ebr Sep 25 at 18:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.