Rahul Mahadev
Rahul Mahadev

Reputation: 67

boost::asio write hangs for large sizes

I am using boost::asio to implement a tcp server and client. I am using the write and read functions when the size of the file is large the write hangs and does not complete.

This is the function which reads

int send_request(std::string &ep_ip, int ep_port, std::string &message, std::string &response) {
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(ep_ip), ep_port);
    try {
        boost::asio::ip::tcp::socket socket(io_service);
        boost::system::error_code error;
        socket.connect(ep);
        std::vector<char> buf(BUF_SIZE);
        // buf = new std::vector<char>(BUF_SIZE);
        std::copy(message.begin(), message.end(), buf.begin());
        boost::asio::write(socket, boost::asio::buffer(buf), error);
        // int bytes_read = socket.read_some(boost::asio::buffer(buf) , error );
        int bytes_read = boost::asio::read(socket, boost::asio::buffer(buf), error);
        std::copy(buf.begin(), buf.begin() + bytes_read, std::back_inserter(response));
        socket.close();
    } catch (std::exception &e) {
        // std::cerr << e.what() << std::endl;
        return 0;
    }
    return 1;
}

This is the function which writes

void listen(int port_no, std::string &filename) {
    boost::asio::io_service io_service;
    std::string result = "";
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port_no));
    while (1) {
        tcp::socket socket(io_service);
        std::vector<char> buf(BUF_SIZE);
        boost::system::error_code ignored_error, error;
        acceptor.accept(socket);
        int req_bytes_read = socket.read_some(boost::asio::buffer(buf), error);
        std::string request;
        std::copy(buf.begin(), buf.begin() + req_bytes_read, std::back_inserter(request));
        request = clean_string(request);
        if (error) {
            std::cout << "[ERROR] Unable to process the request. CODE  " << error << std::endl;
        } else {
            std::cout << "Received the message: " << request << std::endl;
            // This where we do some processing of the request, perhaps call grep.
            std::string PATH = "$HOME/" + filename;
            result = "";
            grep_impl(request, PATH, result);
            // result = grep(request , PATH);
        }
        std::cout << "Size of result is " << result.size() << std::endl;
        boost::asio::write(socket, boost::asio::buffer(result), ignored_error);
        socket.close();
    }
}

Upvotes: 1

Views: 1357

Answers (1)

sehe
sehe

Reputation: 393114

so grep_impl puts a large string in result . 38mb to be precise – Rahul Mahadev 18 hours ago

So, in response to that comment: you only ever read BUF_SIZE characters.

(Note that this still would not lead to hanging writes, because the write operation will simply stop as the connection gets reset by the peer, you have an explicit socket.close() in the send_request client).

To read a larger response (I'm assuming 38mb is larger) you need a loop.

while (!error) {
    int bytes_read = ba::read(socket, ba::buffer(buf), error);
    std::copy(buf.begin(), buf.begin() + bytes_read, std::back_inserter(response));
    if (bytes_read == 0)
        break;
}

You can expect the error to usually become eof when the server stops transmission.

  1. You send all the bytes in buf, even those not filled. I'm not sure this is on purpose, but at least it's corrupting the output.

    Oh. Maybe it's clean_string that does this, but why not simply send only the request?

Here's a self-contained demonstration transferring 38mb of data using a 1k buffer:

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>

static constexpr size_t BUF_SIZE = 1024u;

namespace ba = boost::asio;
using ba::ip::tcp;

int send_request(std::string const& ep_ip, int ep_port, std::string const& message, std::string &response) {
    ba::io_service io_service;
    tcp::endpoint ep(ba::ip::address::from_string(ep_ip), ep_port);
    try {
        tcp::socket socket(io_service);
        boost::system::error_code error;
        socket.connect(ep);
        std::vector<char> buf(BUF_SIZE);
        std::copy(message.begin(), message.end(), buf.begin());

        ba::write(socket, ba::buffer(buf), error);

        // int bytes_read = socket.read_some(ba::buffer(buf) , error );

        while (!error) {
            int bytes_read = ba::read(socket, ba::buffer(buf), error);
            std::copy(buf.begin(), buf.begin() + bytes_read, std::back_inserter(response));
            if (bytes_read == 0)
                break;
        }

        socket.close();
    } catch (std::exception &e) {
        // std::cerr << e.what() << std::endl;
        return 0;
    }
    return 1;
}

std::string clean_string(std::string const& s) { 
    return s.c_str(); // Cut from NUL
}

void grep_impl(std::string const& /*request*/, std::string const& /*PATH*/, std::string& result) {
    result = std::string(38ul << 20, '*');
}

void listen(int port_no, std::string const& filename) {
    ba::io_service io_service;
    std::string result = "";
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port_no));
    while (1) {
        tcp::socket socket(io_service);
        std::vector<char> buf(BUF_SIZE);
        boost::system::error_code ignored_error, error;
        acceptor.accept(socket);
        int req_bytes_read = socket.read_some(ba::buffer(buf), error);
        std::string request;
        std::copy(buf.begin(), buf.begin() + req_bytes_read, std::back_inserter(request));

        request = clean_string(request);
        if (error) {
            std::cout << "[ERROR] Unable to process the request. CODE  " << error << std::endl;
        } else {
            std::cout << "Received the message: " << request << std::endl;
            // This where we do some processing of the request, perhaps call grep.
            std::string PATH = "$HOME/" + filename;
            result = "";
            grep_impl(request, PATH, result);
            // result = grep(request , PATH);
        }
        std::cout << "Size of result is " << result.size() << std::endl;
        ba::write(socket, ba::buffer(result), ignored_error);
        socket.close();
        break;
    }
}

#include <boost/thread.hpp>

int main() {
    boost::thread_group tg;
    tg.create_thread([]{ listen(6767, "test.cpp"); });
    tg.create_thread([]{ 
            boost::this_thread::sleep_for(boost::chrono::seconds(1));
            std::string response;
            send_request("127.0.0.1", 6767, "TEST", response);

            std::cout << "send_request returned " << response.size() << " bytes";
        });

    tg.join_all();
}

Prints

Received the message: TEST
Size of result is 39845888
send_request returned 39845888 bytes

Upvotes: 1

Related Questions