boost.asio transfer files - not completely transferd? - memory mapping performance? - c++

I am currently trying to implement a file-transfer app under linux using boost.asio. I am complete new to this topic (general learning cpp), the past days I was trying to figure out how this might work. I am already losing my mind.
I made some progress, but I can't transfer a file completely, instead I am just getting a part of the file. Does anyone knows why the buffer is not red or written completely?
I made It really simple, its just a series of commands, I will implement it object oriented later on.
The secondly I was wondering if there is another way to map the file in memory more efficiency? Say someone want to transfer a 2 tb file?
I am using this binary file for testing: blah.bin
to successfully build it u need:
g++ -std=c++17 -Wall -Wextra -g -Iinclude -Llib src/main.cpp -o bin/main -lboost_system -lpthread
server
//server
#include <boost/asio.hpp>
#include <iostream>
#include <fstream>
using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;
int main() {
boost::asio::io_service io_service;
//listen
tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 3333));
//socket
tcp::socket socket_(io_service);
//waiting
acceptor_.accept(socket_);
//read
boost::asio::streambuf buf;
boost::asio::read_until(socket_, buf, "\nend\n");
auto data = boost::asio::buffer_cast<const char*>(buf.data());
std::ofstream file("transferd.bin");
cout << data;
file << data;
file.close();
//response
boost::asio::write(socket_, boost::asio::buffer("data recived"));
return 0;
}
client
//client
#include <boost/asio.hpp>
#include <iostream>
#include <fstream>
#include <vector>
using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;
using std::vector;
const vector<char> fileVec(const std::string & fileName) {
std::ifstream file(fileName, std::ios::in | std::ios::binary);
vector<char> tempVec ((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
return tempVec;
};
int main() {
boost::asio::io_service io_service;
//socket
tcp::socket socket(io_service);
//connection
socket.connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 3333));
//write to server
auto vdata = fileVec("example.bin");
vdata.push_back('\n');
vdata.push_back('e');
vdata.push_back('n');
vdata.push_back('d');
vdata.push_back('\n');
boost::system::error_code error;
boost::asio::write(socket, boost::asio::buffer(vdata), error);
//response from server
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error);
const char* response = boost::asio::buffer_cast<const char*>(receive_buffer.data());
cout << response;
return 0;
}

The problem is not in the socket but how you are writing the file in the server.
std::ofstream file("transferd.bin");
cout << data; // you cannot print binary data like this on the standard output!
file << data;
file.close();
The above snippet is wrong because the << operator is used for ASCII not for binary data!
A simple fix would be to replace it with the following snippet:
std::ofstream file("transferd.bin");
file.write(data, buf.size());
The second part of the question is of course more hard and it requires a lot of code changing.
The point is that you cannot transfer all the content at once, But you should split the transfer in small chunks.
One solution can be to send a small header with some information like the total transfer bytes so the server can read chunk by chunk until the whole transfer is complete.
The message has a header file containing the total message size, the number of chunks. Each chunks have a little header indicating the chunk size or for instance the chunk index in case you wanna switch to UDP.
Following the server snippet
#include <array>
#include <boost/asio.hpp>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <vector>
using namespace boost::asio;
using ip::tcp;
using std::cout;
using std::endl;
using std::string;
struct MessageHeader {
int64_t totalSize;
int64_t chunkCount;
};
struct ChunkHeader {
int64_t index;
int64_t size;
};
MessageHeader parseHeader(const char* data) {
MessageHeader header;
memcpy(&header, data, sizeof(MessageHeader));
return header;
}
ChunkHeader parseChunkHeader(const char* data) {
ChunkHeader header;
memcpy(&header, data, sizeof(MessageHeader));
return header;
}
MessageHeader readHeader(tcp::socket& socket) {
std::array<char, sizeof(MessageHeader)> buffer;
boost::asio::read(socket, boost::asio::buffer(buffer));
return parseHeader(buffer.data());
}
ChunkHeader readChunkHeader(tcp::socket& socket) {
std::array<char, sizeof(ChunkHeader)> buffer;
boost::asio::read(socket, boost::asio::buffer(buffer));
return parseChunkHeader(buffer.data());
}
std::vector<char> readChunkMessage(tcp::socket& socket) {
auto chunkHeader = readChunkHeader(socket);
std::vector<char> chunk(chunkHeader.size);
boost::asio::read(socket, boost::asio::buffer(chunk));
return chunk;
}
int main() {
boost::asio::io_service io_service;
// listen
tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 3333));
// socket
tcp::socket socket_(io_service);
// waiting
acceptor_.accept(socket_);
auto messageHeader = readHeader(socket_);
for (auto chunkIndex = 0ll; chunkIndex != messageHeader.chunkCount; ++chunkIndex) {
auto chunk = readChunkMessage(socket_);
// open the file in append mode
std::ofstream file("transferd.bin", std::ofstream::app);
file.write(chunk.data(), chunk.size());
}
// response
boost::asio::write(socket_, boost::asio::buffer("data recived"));
return 0;
}
The above solution has drawbacks because everything is synchronous and if the client quit in the middle of transfer the server will be stuck :D
A better solution is to turn that in async code... but It's too much all at once for a beginner!

Related

vk api on boost c++ doesn't work correctly

I wrote a some code that should send GET request and get response.
It works for ip-api.com and returns me json file.
But for api.vk.com it returns html as that:
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>kittenx</center>
</body>
</html>
The most interesting thing is that the program returns the correct link, after opening which the desired GET request will be executed.
main.cpp:
#include <iostream>
#include "client.hpp"
#include "json.hpp"
std::string get_token(const std::string &);
int main()
{
std::string token = get_token("data/token1");
std::string query = "https://api.vk.com/method/groups.getMembers?access_token=" + token + "&v=5.13&group_id=klubauto";
std::cout << query << "\n\n\n";
Client client(url);
client.send_request(query);
std::string response = client.get_response();
std::cout << response << std::endl;
return 0;
}
client.hpp:
#pragma once
#include <string>
#include <boost/beast.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
namespace http = boost::beast::http;
class Client
{
public:
Client();
Client(const std::string &api);
~Client();
void send_request(const std::string &arguments);
std::string get_response();
private:
boost::asio::io_context io;
boost::asio::ip::tcp::resolver resolver;
boost::asio::ip::tcp::socket socket;
std::string url;
};
client.cpp
#include "client.hpp"
/*
* Constructors
*/
Client::Client() : url("google.com"), resolver(io), socket(io)
{
boost::asio::connect(socket, resolver.resolve(url, "80"));
}
Client::Client(const std::string &api) : url(api), resolver(io), socket(io)
{
boost::asio::connect(socket, resolver.resolve(url, "80"));
}
/*
* Destructor
*/
Client::~Client()
{
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
}
/*
* Send request
*/
void Client::send_request(const std::string &arguments)
{
http::request<http::string_body> req(http::verb::get, arguments, 11);
req.set(http::field::host, url);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(socket, req);
}
/*
* Get response
*/
std::string Client::get_response()
{
std::string response;
{
boost::beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(socket, buffer, res);
response = boost::beast::buffers_to_string(res.body().data());
}
return response;
}
I would like to receive a json file in the response variable, please tell me how to achieve this?
Like I commented, that's how HTTP works: Servers can redirect to a better/new location.
I assume the prime reason for this is because your connection is not HTTPS, and that's what the end-points require. So, fix that first.
Next, your query includes the base URL, which is another error.
Live Demo
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <string>
namespace http = boost::beast::http;
namespace ssl = boost::asio::ssl;
using boost::asio::ip::tcp;
class Client {
public:
Client(const std::string& host = "google.com") : _host(host) {
_ctx.set_default_verify_paths();
connect(_socket.lowest_layer(),
tcp::resolver{_io}.resolve(_host, "https"));
_socket.handshake(ssl::stream_base::client);
}
void send_request(const std::string& query)
{
http::request<http::string_body> req(http::verb::get, query, 11);
req.set(http::field::host, _host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(_socket, req);
}
std::string get_response() {
http::response<http::string_body> res;
boost::beast::flat_buffer buffer;
http::read(_socket, buffer, res);
return std::move(res.body());
}
private:
boost::asio::io_context _io;
ssl::context _ctx{ssl::context::sslv23_client};
ssl::stream<tcp::socket> _socket{_io, _ctx};
std::string _host;
};
#include <iostream>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for COLIRU header-only
namespace json = boost::json;
std::string get_token(const std::string&) { return ""; }
int main()
{
Client client("api.vk.com");
client.send_request("/method/groups.getMembers?access_token=" +
get_token("data/token1") + "&v=5.13&group_id=klubauto");
std::cout << json::parse(client.get_response()) << std::endl;
}
Coliru doesn't allow public network access:
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
what(): resolve: Service not found
But on my machine it correctly says:
{"error":{"error_code":5,"error_msg":"User authorization failed: no access_token passed.","request_params":[{"key":"v","value":"5.13"},{"key":"group_id","value":"klubauto"},{"key":"method","value":"groups.getMembers"},{"key":"oauth","value":"1"}]}}
Note I included quite a number of simplifications along the way.

Handling large http response using boost::beast

The following code use to get http response message:
boost::beast::tcp_stream stream_;
boost::beast::flat_buffer buffer;
boost::beast::http::response<boost::beast::http::dynamic_body> res;
boost::beast::http::read(stream_, buffer, res);
However, In some cases, based on the preceding request, I can expect that the response message body will include large binary file.
Therefore, I’d like to read it directly to the filesystem and not through buffer variable to avoid excessive use of process memory. How can it be done ?
in Objective-c framework NSUrlSession there's an easy way to do it using NSURLSessionDownloadTask instead of NSURLSessionDataTask, so I wonder if it's also exist in boost.
Thanks !
In general, you can use the http::buffer_body to handle arbitrarily large request/response messages.
If you specifically want to read/write from a filesystem file, you can have the http::file_body instead.
Full Demo buffer_body
The documentation sample for buffer_body is here https://www.boost.org/doc/libs/1_77_0/libs/beast/doc/html/beast/using_http/parser_stream_operations/incremental_read.html.
Using it to write to std::cout: Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = net::ip::tcp;
using socket_t = tcp::socket;
/* This function reads a message using a fixed size buffer to hold
portions of the body, and prints the body contents to a `std::ostream`.
*/
template<
bool isRequest,
class SyncReadStream,
class DynamicBuffer>
void
read_and_print_body(
std::ostream& os,
SyncReadStream& stream,
DynamicBuffer& buffer,
beast::error_code& ec)
{
http::parser<isRequest, http::buffer_body> p;
http::read_header(stream, buffer, p, ec);
if(ec)
return;
while(! p.is_done())
{
char buf[512];
p.get().body().data = buf;
p.get().body().size = sizeof(buf);
http::read(stream, buffer, p, ec);
if(ec == http::error::need_buffer)
ec = {};
if(ec)
return;
os.write(buf, sizeof(buf) - p.get().body().size);
}
}
int main() {
std::string host = "173.203.57.63"; // COLIRU 20210901
auto const port = "80";
net::io_context ioc;
tcp::resolver resolver{ioc};
socket_t s{ioc};
net::connect(s, resolver.resolve(host, port));
write(s, http::request<http::empty_body>{http::verb::get, "/", 11});
beast::error_code ec;
beast::flat_buffer buf;
read_and_print_body<false>(std::cout, s, buf, ec);
}
Full file_body example
This is much shorter, writing to body.html:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using tcp = net::ip::tcp;
using socket_t = tcp::socket;
int main() {
std::string host = "173.203.57.63"; // COLIRU 20210901
auto const port = "80";
net::io_context ioc;
tcp::resolver resolver{ioc};
socket_t s{ioc};
net::connect(s, resolver.resolve(host, port));
write(s, http::request<http::empty_body>{http::verb::get, "/", 11});
beast::error_code ec;
beast::flat_buffer buf;
http::response<http::file_body> res;
res.body().open("body.html", beast::file_mode::write_new, ec);
if (!ec.failed())
{
read(s, buf, res, ec);
}
std::cout << "Wrote 'body.html' (" << ec.message() << ")\n";
std::cout << "Headers " << res.base() << "\n";
}
Prints
Wrote 'body.html' (Success)
Headers HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
Content-Length: 8616
Server: WEBrick/1.4.2 (Ruby/2.5.1/2018-03-29) OpenSSL/1.0.2g
Date: Wed, 01 Sep 2021 19:52:20 GMT
Connection: Keep-Alive
With file body.html; wc body.html showing:
body.html: HTML document, ASCII text, with very long lines
185 644 8616 body.html
Beyond: streaming to child processes and streaming processing
I have an advanced example of that here: How to read data from Internet using muli-threading with connecting only once?.

How to send a file by a zmq multipart message?

I'm trying to send a video using a ZeroMQ infrastructure and I split the video into chunks to send it. When I do it and put the video into a vector to send it through zmq::send_multipart I get a very high usage of RAM memory and so times later I get a segmentation fault error.
My headache is that when I comment on the line that sends the multipart message and runs the program, the vector is made normally and I don't get the segmentation error, although the consumption of RAM memory is not so heavy.
Can someone give me a tip about how to send this file?
Server code:
#include <fstream>
#include <sstream>
#include <chrono>
#include <thread>
#include <iostream>
#include <future>
#include <zmq.hpp>
#include <zmq_addon.hpp>
using namespace std::chrono_literals;
const int size1MB = 1024 * 1024;
template <typename T>
void debug(T x)
{
std::cout << x << std::endl;
}
//Generate new chunks
std::unique_ptr<std::ofstream> createChunkFile(std::vector<std::string> &vecFilenames)
{
std::stringstream filename;
filename << "chunk" << vecFilenames.size() << ".mp4";
vecFilenames.push_back(filename.str());
return std::make_unique<std::ofstream>(filename.str(), std::ios::trunc);
}
//Split the file into chunks
void split(std::istream &inStream, int nMegaBytesPerChunk, std::vector<std::string> &vecFilenames)
{
std::unique_ptr<char[]> buffer(new char[size1MB]);
int nCurrentMegaBytes = 0;
std::unique_ptr<std::ostream> pOutStream = createChunkFile(vecFilenames);
while (!inStream.eof())
{
inStream.read(buffer.get(), size1MB);
pOutStream->write(buffer.get(), inStream.gcount());
++nCurrentMegaBytes;
if (nCurrentMegaBytes >= nMegaBytesPerChunk)
{
pOutStream = createChunkFile(vecFilenames);
nCurrentMegaBytes = 0;
}
}
}
int main()
{
zmq::context_t context(1);
zmq::socket_t socket(context, zmq::socket_type::rep);
socket.bind("tcp://*:5555");
std::ifstream img("video2.mp4", std::ifstream::in | std::ios::binary);
std::ifstream aux;
std::vector<std::string> vecFilenames;
std::vector<zmq::const_buffer> data;
std::ostringstream os;
std::async(std::launch::async, [&img, &vecFilenames]() {
split(img, 100, vecFilenames);
});
img.close();
zmq::message_t message, aux2;
socket.recv(message, zmq::recv_flags::none);
//Put the chunks into the vector
std::async([&data, &aux, &os, &vecFilenames]() {
for (int i = 0; i < vecFilenames.size(); i++)
{
std::async([&aux, &i]() {
aux.open("chunk" + std::to_string(i) + ".mp4", std::ifstream::in | std::ios::binary);
});
os << aux.rdbuf();
data.push_back(zmq::buffer(os.str()));
os.clear();
aux.close();
}
});
//Send the vector for the client
std::async([&socket, &data] {
zmq::send_multipart(socket, data);
});
}
Client-side:
#include <fstream>
#include <sstream>
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
#include <zmq.hpp>
#include <zmq_addon.hpp>
#include <queue>
#include <deque>
#include <future>
#include <vector>
using namespace std::chrono_literals;
template <typename T>
void debug(T x)
{
std::cout << x << std::endl;
}
int main()
{
zmq::context_t context(1);
zmq::socket_t socket(context, zmq::socket_type::req);
socket.connect("tcp://localhost:5555");
std::ofstream img("teste.mp4", std::ios::out | std::ios::binary);
socket.send(zmq::buffer("ok\n"), zmq::send_flags::none);
std::vector<zmq::message_t> send_msgs;
zmq::message_t size;
std::async([&send_msgs, &img, &socket] {
zmq::recv_multipart(socket, std::back_inserter(send_msgs));
while (send_msgs.size())
{
img << send_msgs[0].to_string();
send_msgs.erase(send_msgs.begin());
}
});
}
An attempt to move all data via multipart-message does collect all data into one immense BLOB, plus add duplicate O/S-level transport-class specific buffers and the most probable result is a crash.
Send individual blocks of the video-BLOB as individual simple-message payloads and reconstruct the BLOB ( best via indexed numbering, having an option to re-request any part, that did not arrive to the receiver-side ).
Using std::async mode with a REQ/REP seems to be a bit tricky for this Archetype must keep its dFSA interleaved sequence of .recv()-.send()-.recv()-.send()-...ad infinitum... as it falls into an unsalvagable mutual deadlock if failed to do so.
For streaming video (like for CV / scene-detection), there are more tricks to put in - one of which is to use ZMQ_CONFLATE option, so as to send but the very recent video-frame, not losing time on "archaic" scene-images, that are already part of the history, and thus delivering always but the very recent video-frame to the receiving-side processing.

How to reuse http::beast::flat_buffer and http::response?

I'm using boost::beast in my project. Following code is a modified version of example code. I tried to reuse flat_buffer and http::response in the following code, but the result is wrong. In the second query, the response body is concatenate of two query result. How can I solve this problem?
#include <iostream>
#include <string>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
int main(int argc, char** argv) {
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace http = boost::beast::http; // from <boost/beast/http.hpp>
auto const host = "www.example.com";
auto const port = "80";
auto const target = "/";
int version = 11;
boost::asio::io_context ioc;
tcp::resolver resolver{ioc};
tcp::socket socket{ioc};
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
boost::asio::connect(socket, results.begin(), results.end());
// Set up an HTTP GET request message
http::request<http::string_body> req{http::verb::get, target, version};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// This buffer is used for reading and must be persisted
boost::beast::flat_buffer buffer;
// Declare a container to hold the response
http::response<http::dynamic_body> res;
for (int n = 0; n < 2; n++) {
http::write(socket, req);
http::read(socket, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
std::cout << "+++++++++++++++++\n";
}
// Gracefully close the socket
boost::system::error_code ec;
socket.shutdown(tcp::socket::shutdown_both, ec);
return EXIT_SUCCESS;
}
The http::read is additive. If you want the buffer empty before the call to read you will need to do it manually.
One way is like this:
buffer.consume(buffer.size());
The beast team are always happy to help on slack if you have access to it:
http://slack.cpp.al
channel #beast

Cannot get simple boost networking example to work, merely initializing server causes errors immediately

I'm running Xubuntu 14.04 and Boost 1.54.0. I've modified the chapter 4 example from this book to be able to send arbitrary strings between client and server, but now the program just fails immediately.
This is the server:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <string>
using namespace boost::asio;
io_service service;
size_t read_complete(
char* buff, const boost::system::error_code& err, size_t bytes)
{
if (err) {
return 0;
}
bool found = std::find(buff, buff + bytes, '\n') < buff + bytes;
return found? 0 : 1;
}
void handle_connections() {
ip::tcp::acceptor acceptor(
service, ip::tcp::endpoint(ip::tcp::v4(), 8001));
std::string buff;
while (true) {
ip::tcp::socket sock(service);
acceptor.accept(sock);
int bytes = read(sock, buffer(buff.c_str()), bind(read_complete, buff, _1, _2));
std::string msg(buff, bytes);
sock.write_some(buffer(msg));
sock.close();
}
}
int main(int argc, char* argv[]) {
handle_connections();
}
It's copied directly from the book and has been verified to work correctly.
This is the minimal client necessary to reproduce the problem:
#include <memory>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
using namespace boost::asio;
class Client
{
public:
Client(const std::string& server, const std::string& port):
server(server), port(port)
{
service = std::unique_ptr<io_service>(new io_service);
endpoint = std::unique_ptr<ip::tcp::endpoint>(new ip::tcp::endpoint(
ip::address::from_string(server), atoi(port.c_str())));
boostSocket = std::unique_ptr<ip::tcp::socket>(
new ip::tcp::socket(*service));
std::cout << std::boolalpha << boostSocket.is_open(); // "false"
}
private:
std::string server;
std::string port;
std::unique_ptr<io_service> service;
std::unique_ptr<ip::tcp::socket> boostSocket;
std::unique_ptr<ip::tcp::endpoint> endpoint;
};
int main()
{
Client client("127.0.0.1", "8001");
return 0;
}
Running this program tells me that the socket is never opened. So I dived into GDB and found this. For convenience, I have omitted the (gdb) s debugging commands I used between every set of lines.
Client::Client (this=0x7fffffffd7f8, server="127.0.0.1", port="8001")
at DIRECTORY WHERE I STORED THE CPP FILE:14
14 service = std::unique_ptr<io_service>(new io_service);
boost::asio::io_service::io_service (this=0x622130) at /usr/include/boost/asio/impl/io_service.ipp:41
41 {
boost::asio::detail::noncopyable::noncopyable (this=0x622130) at /usr/include/boost/asio/detail/noncopyable.hpp:29
29 noncopyable() {}
boost::asio::io_service::io_service (this=0x622130) at /usr/include/boost/asio/impl/io_service.ipp:39
39 (std::numeric_limits<std::size_t>::max)())),
std::numeric_limits<unsigned long>::max () at /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/limits:1196
1196 max() _GLIBCXX_USE_NOEXCEPT { return __LONG_MAX__ * 2UL + 1; }
boost::asio::io_service::io_service (this=0x622130) at /usr/include/boost/asio/impl/io_service.ipp:41
41 {
boost::asio::detail::service_registry::service_registry<boost::asio::detail::task_io_service, unsigned long> (this=0x622150, o=..., arg=18446744073709551615)
at /usr/include/boost/asio/detail/impl/service_registry.hpp:29
29 {
boost::asio::detail::noncopyable::noncopyable (this=0x622150) at /usr/include/boost/asio/detail/noncopyable.hpp:29
29 noncopyable() {}
boost::asio::detail::posix_mutex::posix_mutex (this=0x622158) at /usr/include/boost/asio/detail/impl/posix_mutex.ipp:33
33 {
boost::asio::detail::noncopyable::noncopyable (this=0x622158) at /usr/include/boost/asio/detail/noncopyable.hpp:29
29 noncopyable() {}
boost::asio::detail::posix_mutex::posix_mutex (this=0x622158) at /usr/include/boost/asio/detail/impl/posix_mutex.ipp:34
34 int error = ::pthread_mutex_init(&mutex_, 0);
The relevant lines are the first (because it's part of my code) and the last (it goes straight to error handling). Subsequent GDB commands only show that it delves further into Boost's error handling systems. Why? All I'm doing is creating an io_service.
I have to fix errors in both the client and the server programs in order to even compile.
Here's the fixed server:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <string>
using namespace boost::asio;
io_service service;
size_t read_complete(std::vector<char>const& buff, const boost::system::error_code &err, size_t bytes) {
if (err) {
return 0;
}
auto last = buff.begin()+bytes;
bool found = (last != std::find(buff.begin(), last, '\n'));
return found;
}
void handle_connections() {
ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 8001));
std::vector<char> buff(1024);
while (true) {
ip::tcp::socket sock(service);
acceptor.accept(sock);
int bytes = read(sock, buffer(buff), bind(read_complete, boost::ref(buff), _1, _2));
sock.send(buffer(buff, bytes));
sock.close();
}
}
int main() { handle_connections(); }
There were quite a number of changes, most notably the one fixing the buffer.
Here's the fixed client. I junked all the superstitious use of new:
#include <memory>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
using namespace boost::asio;
class Client {
public:
Client(const std::string &server, const std::string &port)
: server(server),
port(port),
service(),
endpoint(ip::address::from_string(server), atoi(port.c_str())),
boostSocket(service)
{
std::cout << std::boolalpha << boostSocket.is_open() << "\n"; // of course, "false"
}
private:
std::string server;
std::string port;
io_service service;
ip::tcp::endpoint endpoint;
ip::tcp::socket boostSocket;
};
int main() {
Client client("127.0.0.1", "8001");
return 0;
}
Notes:
you should likely use the resolver to resolve address/port instead of just hardcoding the conversions (it might not be IP or port numbers)
you never connect, so of course the socket is not open
boostSocket.connect(endpoint);
std::cout << std::boolalpha << boostSocket.is_open() << "\n"; // of connected, "true"
/*
*std::string message("hellow world\n\n\n\n\n");
*boostSocket.send(buffer(message));
*streambuf sb;
*read(boostSocket, sb, boost::asio::transfer_all());
*std::cout << "RESPONSE: '" << &sb << "'\n";
*/
Happy debugging