1

A custom tcp client-server implementation with asio, problem with reading the data the client already exchanged to the server side, async_read simply closes on itself, i've managed to pass both the header and the body, but server is stuck on reading the header.

I've made sure that: header is the correct size header is packed and encoded? header has data

my client output:

enter the file you'd like to send:
C:\Users\marcopolo\source\repos\test\edwynne.jpg
connection is being initialized..
on_connect invoked no errors The operation completed successfully.
do_write async_write failed An existing connection was forcibly closed by the remote host.

my server output:

launching server..
awaiting for upcoming connection..
new connection: 127.0.0.1
do_read_header: failed The network connection was aborted by the local system.

Client code:


#include <iostream>
#include <asio.hpp>
#include <memory>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <thread>

std::vector<char> header;
std::vector<char> payload;
std::vector<char> response;

size_t header_size = 128;
size_t message_size = 0;
size_t header_response_size = 128;

class connection : public std::enable_shared_from_this<connection>
{
public:
    connection(asio::io_context& ioc) : socket(ioc), resolver(ioc), io(ioc)
    {
    }

    void connect() {

        asio::error_code ec;

        auto resolve = resolver.resolve(asio::ip::tcp::resolver::query("127.0.0.1", "47778"), ec);

        if (ec) {
            std::cout << "connect failed" << std::endl;
            return;
        }

        auto self = shared_from_this();

        asio::async_connect(socket, resolve, [this, self](asio::error_code ec, asio::ip::tcp::endpoint ep) {
            if (!ec && socket.is_open()) {
                using namespace std::chrono_literals;

                std::cout << "on_connect invoked no errors" << ec.message() << std::endl;
                do_write_header(self);

             //   std::this_thread::sleep_for(2000ms);
            }
            else {
                std::cout << "on_connect async_connect failed " << ec.message() << std::endl;
            }
        });
    }

private:

    void do_read_header(std::shared_ptr<connection> self) {
        asio::async_read(socket, asio::buffer(response.data(), header_response_size), asio::transfer_at_least(header_response_size), [this, self](const asio::error_code& ec, size_t length)
            {
                if (!ec && length >= header_response_size)
                {
                    std::cout << "response: " << std::string(response.data(), header_response_size) << std::endl;
                }
                else {
                    std::cout << "do_read_header async_read_some failed " << ec.message() << std::endl;
                    asio::post(io, [&]() {socket.close(); });
                }
            });
    }

    void do_write_header(std::shared_ptr<connection> self) {
        asio::async_write(socket, asio::buffer(header.data(), header_size), [this, self](const asio::error_code& ec, size_t length)
            {
                if (!ec && length >= header_size) {
                    std::cout << "size of bytes written.. " << length << ec.message() << std::endl;

                    do_write_body(self);    
                }
                else
                {
                    std::cout << "do_write async_write failed " << ec.message() << std::endl;
                    asio::post(io, [&]() {socket.close(); });
                }
            });
    }

    void do_write_body(std::shared_ptr<connection> self) {
        asio::async_write(socket, asio::buffer(payload.data(), message_size), [this, self](const asio::error_code& ec, size_t length)
            {
                if (!ec && length > 0) {
                    std::cout << "size of bytes written.. " << length << std::endl;

                  //  do_read_header(self);
                }
                else
                {
                    std::cout << "do_write async_write failed " << ec.message() << std::endl;
                    asio::post(io, [&]() {socket.close(); });
                }
            });
    }

private:
    asio::ip::tcp::socket socket;
    asio::ip::tcp::resolver resolver;
    asio::io_context& io;
};

int main()
{
    asio::io_context io;

    std::string actualpath;

    while (actualpath.empty()) {
        std::cout << "enter the file you'd like to send: " << std::endl;
        std::getline(std::cin, actualpath);
    }

    std::filesystem::path path( actualpath );

    if (!std::filesystem::exists(path)) {
        std::cout << "file does not exist" << std::endl;
        return -1;
    }

    std::string file_name(path.filename().string()); 
    size_t file_size(std::filesystem::file_size(path));

    // initalize buffer
    std::string buf;
    std::ifstream ifs(path, std::ifstream::in | std::ifstream::binary);

    std::ostringstream header_, payload_;

    header_ << file_name << " " << file_size << std::endl;

    if (header_.str().length() > header_size || header_.str().length() <= 0) {
        std::cout << "header_size is incorrect" << std::endl;
        return -1;
    }

    payload.reserve(file_size);

    payload_ << ifs.rdbuf();
    
    std::string header_str = header_.str();
    std::string payload_str = payload_.str();

    header.reserve(header_str.length());
    payload.reserve(payload_str.length());

    std::copy(header_str.begin(), header_str.end(), std::back_inserter(header));
    std::copy(payload_str.begin(), payload_str.end(), std::back_inserter(payload));

    message_size = file_size;

    auto ptr = std::make_shared<connection>(io);

    std::cout << "connection is being initialized.." << std::endl;

    ptr->connect();

    std::thread thread([&io]() { io.run(); });

    // join the thread
    if (thread.joinable())
        thread.join();
}

server code:


#include <iostream>
#include <asio.hpp>
#include <memory>
#include <filesystem>
#include <fstream>
#include <mutex>
#include <condition_variable>

bool ready = false;
std::mutex cvmutex;
std::condition_variable cv;


std::vector<char> header;
std::vector<char> payload;
std::vector<char> response;

constexpr size_t header_size = 128;
constexpr size_t message_size = 0;
constexpr size_t header_response_size = 128;

class connection : public std::enable_shared_from_this<connection>
{
public:
    connection(asio::ip::tcp::socket&& p_socket) : socket(p_socket) 
    {

    }

    void init() {
        auto rel = shared_from_this();

        do_read_header(rel);
    }

private:
    void do_read_header(std::shared_ptr<connection> self) {
        asio::async_read(socket, asio::buffer(&header, header_size), asio::transfer_at_least(1), [this, self](const asio::error_code& ec, size_t length)
        {
            if (!ec)
            {
                std::cout << "do_read_header: invoked " <<  ec.message() << std::endl;

                if (length <= 0) {
                    std::cout << "do_read_header: connection closed size is below 0 " << std::endl;
                    asio::post([&]() {socket.close(); });
                    return;
                }         

                std::istringstream iss(header.data());
                std::string file_name, file_size;

                iss >> file_name >> file_size;

                std::cout << "header path: " << file_name
                    << " file_size: " << file_size
                    << " header size: " << header_size << std::endl;

                length = std::atoi(file_size.c_str());

                do_read_body(self);
            }
            else {
                std::cout << "do_read_header: failed " << ec.message() << std::endl;
            }
        });
    }

    void do_read_body(std::shared_ptr<connection> self) {
        asio::async_read(socket, asio::buffer(payload, length), asio::transfer_at_least(1), [this, self](const asio::error_code& ec, size_t p_length)
            {
                if (!ec)
                {
                    std::cout << "do_ready_body succeed " << ec.message() << std::endl;

                }
                else
                {
                    asio::post([&]() {socket.close(); });
                }

                do_read_handler(ec, self);
            });
    }

    void do_write(std::shared_ptr<connection> self) {
        asio::async_write(socket, asio::buffer(response, header_response_size),
            [this, self](const asio::error_code& ec, size_t length)
            {
                if (!ec && length >= 0) {
                    std::cout << "do_write invoked " << ec.message() << std::endl;

                }
                else {
                    std::cout << "do_write failed " << ec.message() << std::endl;
                }
            });
    }

    void do_read_handler(const asio::error_code& ec, std::shared_ptr<connection> self) {
        if (ec != asio::error::eof) {
            do_read_body(self);
        }
        else {
            std::cout << "download is finished" << std::endl;

            std::string fullPath = "D:\akon\test.jpg";
            std::filesystem::path path(fullPath);

            std::ofstream file(fullPath);

            file << payload << std::endl;

            ready = true;

            std::string buf = { ready ? "OK" : "BAD" };
           // std::copy(buf.begin(), buf.end(), response);

            cv.notify_one();

            do_write(self);

            delete payload;
        }
    }

private:
    asio::ip::tcp::socket& socket;
    asio::ip::tcp::endpoint endpoint;

    size_t length;
    char* payload;
};

class server
{
public:
    server(asio::io_context& p_io, const asio::ip::tcp::endpoint& endpoint, int max_connection = 0) : acceptor(p_io, endpoint) 
    {
    }

    void do_accept() {
        std::cout << "awaiting for upcoming connection.." << std::endl;

        acceptor.async_accept([this](const asio::error_code& ec, asio::ip::tcp::socket p_socket) {
            if (!ec) {
                connections = std::make_shared<connection>(std::move(p_socket));

                connections->init();
                //connections.push_back(new_connection);

                std::cout << "new connection: " << p_socket.local_endpoint().address() << std::endl;

            }
            else
            {
                std::cout << "do_accept error" << ec.message() << std::endl;
            }

        });
    }

    bool init() {
        do_accept();

        return true;
    }

private:
    asio::ip::tcp::acceptor acceptor;
    std::shared_ptr<connection>  connections;
};

int main()
{
    asio::io_context io;
    asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), 47778);

    std::cout << "launching server.. " << std::endl;

    server server(io, endpoint, 100);

    server.init();

    // exec io
    std::thread thread([&]() {io.run(); });

    std::unique_lock<std::mutex> lk(cvmutex);
    cv.wait(lk, []() { return (ready); });
    return 0;
}
  • The server closes the socket as soon as the async_accept lambda returns, because the socket is a local variable in this lambda, and doesn't get moved anywhere else. – user253751 Mar 21 '23 at 02:16
  • In the server code, try having the `connection` class have the socket as `asio::ip::tcp::socket socket` instead of `asio::ip::tcp::socket& socket` (i.e. not a reference) You should also be careful about using `p_socket` after it has been moved in `do_accept` – ioums Mar 21 '23 at 18:58

0 Answers0