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;
}