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
Related
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.
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?.
Below is a piece of code which I am trying hard to make it successful. I use the reference code available at git hub https://www.boost.org/doc/libs/1_70_0/libs/beast/doc/html/beast/quick_start/websocket_client.html.
The problem is when I call the connect method and do write operation in same function it works, however it wont work if I put that in different function.
I am new to boost and shared_pointer, please excuse if I am not making any sense.
// Sends a WebSocket message and prints the response
class CWebSocket_Sync : public std::enable_shared_from_this<CWebSocket_Sync>
{
tcp::resolver resolver_;
websocket::stream<tcp::socket> ws_;
boost::beast::multi_buffer buffer_;
std::string host_;
std::string text_;
public:
// Resolver and socket require an io_context
explicit
CWebSocket_Sync(boost::asio::io_context& ioc)
: resolver_(ioc)
, ws_(ioc)
{
}
void
connect(
char const* host,
char const* port,
char const* text)
{
// Save these for later
host_ = host;
text_ = text;
// Look up the domain name
auto const results = resolver_.resolve(host, port);
// Make the connection on the IP address we get from a lookup
auto ep = net::connect(ws_.next_layer(), results);
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// See https://tools.ietf.org/html/rfc7230#section-5.4
host_ += ':' + std::to_string(ep.port());
// Perform the websocket handshake
ws_.handshake(host_, "/");
//ws_.write(net::buffer(std::string(text)));
//// This buffer will hold the incoming message
//beast::flat_buffer buffer;
//// Read a message into our buffer
//ws_.read(buffer);
}
void ServerCommand(char const* text)
{
ws_.write(net::buffer(std::string(text))); // <-- this line throw memory error
// This buffer will hold the incoming message
beast::flat_buffer buffer;
// Read a message into our buffer
ws_.read(buffer);
// The make_printable() function helps print a ConstBufferSequence
std::cout << beast::make_printable(buffer.data()) << std::endl;
}
void CloseConnection()
{
// Close the WebSocket connection
ws_.close(websocket::close_code::normal);
}
};
int main(int argc, char** argv)
{
auto const host = "127.0.0.1";
auto const port = "7011";
auto const loginCmd = "login"
boost::asio::io_context ioc;
std::make_shared<CWebSocket_Sync>(ioc)->connect(host, port, loginCmd);
std::make_shared<CWebSocket_Sync>(ioc)->ServerCommand(loginCmd);
std::make_shared<CWebSocket_Sync>(ioc)->CloseConnection();
return EXIT_SUCCESS;
}
std::make_shared<CWebSocket_Sync>(ioc)->connect(host, port);
std::make_shared<CWebSocket_Sync>(ioc)->ServerCommand(loginCmd);
std::make_shared<CWebSocket_Sync>(ioc)->CloseConnection();
Each line creates a new client (make_shared<CWebSocket_Sync>) and runs a single step on it. What you probably wanted:
auto client = std::make_shared<CWebSocket_Sync>(ioc);
client->connect(host, port);
client->ServerCommand(loginCmd);
client->CloseConnection();
Indeed this works:
#include <boost/beast.hpp>
#include <boost/beast/websocket.hpp>
#include <memory>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace websocket = beast::websocket;
using net::ip::tcp;
// Sends a WebSocket message and prints the response
class CWebSocket_Sync : public std::enable_shared_from_this<CWebSocket_Sync> {
tcp::resolver resolver_;
websocket::stream<tcp::socket> ws_;
boost::beast::multi_buffer buffer_;
std::string host_;
public:
// Resolver and socket require an io_context
explicit CWebSocket_Sync(boost::asio::io_context& ioc)
: resolver_(ioc), ws_(ioc) { }
void connect(char const* host, char const* port) {
// Save these for later
host_ = host;
// Look up the domain name
auto const results = resolver_.resolve(host, port);
// Make the connection on the IP address we get from a lookup
auto ep = net::connect(ws_.next_layer(), results);
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// See https://tools.ietf.org/html/rfc7230#section-5.4
host_ += ':' + std::to_string(ep.port());
// Perform the websocket handshake
ws_.handshake(host_, "/");
}
void ServerCommand(char const* text) {
ws_.write(net::buffer(std::string(text))); // <-- this line throw memory error
beast::flat_buffer buffer;
ws_.read(buffer);
std::cout << beast::make_printable(buffer.data()) << std::endl;
}
void CloseConnection() {
ws_.close(websocket::close_code::normal);
}
};
int main() {
auto const host = "127.0.0.1";
auto const port = "7011";
auto const loginCmd = "login";
boost::asio::io_context ioc;
auto client = std::make_shared<CWebSocket_Sync>(ioc);
client->connect(host, port);
client->ServerCommand(loginCmd);
client->CloseConnection();
}
SIMPLIFY
However, since nothing uses the shared lifetime and you're not using asynchronous calls anywhere, why not simply:
Live On Coliru
class CWebSocket_Sync {
and later:
CWebSocket_Sync client(ioc);
client.connect(host, port);
client.ServerCommand(loginCmd);
client.CloseConnection();
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!
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