I am trying to make simple server that remembers and operates some variables with receive short instructions.
I didn't complete this server, and I am trying to test connecting to the server.
But when I try to connect the server, it occurs segmentation fault.
It seems that be occured at io_context.run() function.
I don't know exact cause of this error in spite of reading asio's reference page.
Please help me..
I think that you don't have to read code of data(data.hpp).
This is server code.
//server.cpp
#include <iostream>
#include "network/sc_network.hpp"
int main(int argc, char *argv[])
{
try
{
if(argc != 2)
{
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
tcp::endpoint endpoint(tcp::v4(), std::atoi(argv[1]));
server server(io_context, endpoint);
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
This is client code.
//client.cpp
#include <iostream>
#include <thread>
#include <cstdlib>
#include <boost/asio.hpp>
#include "network/data/data.hpp"
using boost::asio::ip::tcp;
class client{
private:
boost::asio::io_context& io_context_;
tcp::socket socket_;
oper_data *data_;
void do_connect(const tcp::resolver::results_type& endpoints)
{
boost::asio::async_connect(socket_, endpoints,
[this](boost::system::error_code ec, tcp::endpoint)
{
if(!ec)
{
boost::asio::async_read(socket_,
boost::asio::buffer(data_, sizeof(oper_data)),
[this](boost::system::error_code ec, std::size_t)
{
if(!ec)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_,sizeof(oper_data)),
[this](boost::system::error_code ec, std::size_t)
{
});
}
else
{
socket_.close();
}
});
}
else
{
socket_.close();
}
});
}
public:
client(boost::asio::io_context& io_context,
const tcp::resolver::results_type& endpoints)
: io_context_(io_context),
socket_(io_context)
{
do_connect(endpoints);
}
void write(const oper_data& data)
{
boost::asio::post(io_context_,
[this, data]()
{
});
}
};
int main(int argc, char *argv[])
{
try
{
if(argc != 3)
{
std::cerr << "Usage: client <host> <port>\n";
return 1;
}
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
auto endpoints = resolver.resolve(argv[1], argv[2]);
client c(io_context, endpoints);
std::thread t([&io_context](){ io_context.run(); });
char line[128];
while (std::cin.getline(line, 128))
{
oper_data data;
//processing the line with deviding in 3 words.
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
this is sc_network.hpp
//sc_network.hpp
#include <boost/asio.hpp>
#include <memory>
#include <utility>
#include "data/data.hpp"
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
private:
tcp::socket socket_;
data_proc data_proc_;
public:
session(tcp::socket socket)
: socket_(std::move(socket)){}
void start()
{
oper_data *input_data;
boost::asio::async_read(socket_,
boost::asio::buffer(input_data, sizeof(oper_data)),
[this, input_data](boost::system::error_code ec, std::size_t)
{
if(!ec)
{
data_proc_.set_data(*input_data);
data_proc_.oper_process();
start();
}
else
{
return;
}
});
}
};
class server
{
private:
tcp::acceptor acceptor_;
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket)
{
if(!ec)
{
session ex_session(std::move(socket));
}
do_accept();
});
}
public:
server(boost::asio::io_context& io_context,
const tcp::endpoint& endpoint)
: acceptor_(io_context, endpoint)
{
do_accept();
}
};
this is data.hpp.
//data.hpp
#include <deque>
#include <cstring>
#include "favdew_utility.hpp"
#define max_oper_size 5
#define max_oper_buf max_oper_size + 1
struct oper_data {
char oper_[max_oper_buf] = "\0";
char *operand_;
char *oper_num_;
};
typedef struct oper_data oper_data;
class data_store {
private:
char *var_name_;
char *var_value_;
public:
data_store()
: var_name_(NULL), var_value_(NULL) {}
data_store(const char *var_name, const char *var_value)
{
std::size_t var_name_size = strlen(var_name) + 1;
var_name_ = new char[var_name_size];
strncpy(var_name_, var_name, strlen(var_name));
std::size_t var_value_size = strlen(var_value) + 1;
var_value_ = new char[var_value_size];
strncpy(var_value_, var_value, strlen(var_value));
}
char *var_name() { return var_name_; }
char *var_value() { return var_value_; }
void set_value(const char *var_value) {
var_value_ = new char[strlen(var_value) + 1];
strncpy(var_value_, var_value, strlen(var_value));
}
};
typedef std::deque<data_store> data_queue;
class data_proc {
private:
oper_data data_;
data_queue proc_queue;
void var()
{
if (data_store *var = this->get_var(data_.operand_)) {
var->set_value(data_.oper_num_);
}
else {
data_store input_data(data_.operand_, data_.oper_num_);
this->proc_queue.push_back(input_data);
}
}
bool sum()
{
data_store *var = this->get_var(data_.operand_);
if ( (var) && isNumber(var->var_value()))
{
const int input_data = std::atoi(var->var_value()) +
std::atoi(this->data_.oper_num_);
var->set_value(std::to_string(input_data).c_str());
return true;
}
else
return false;
}
bool dif()
{
data_store *var = this->get_var(data_.operand_);
if ((var) && isNumber(var->var_value()))
{
const int input_data = std::atoi(var->var_value()) -
std::atoi(this->data_.oper_num_);
var->set_value(std::to_string(input_data).c_str());
return true;
}
else
return false;
}
public:
data_proc()
{
oper_data input_data;
//<input_data.oper_> is already initialized with "\0"
std::memset(input_data.operand_, 0, sizeof(char *));
std::memset(input_data.oper_num_, 0, sizeof(char *));
}
data_proc(const char *oper, const char *operand, const char *oper_num)
{
strncpy(data_.oper_, oper, max_oper_size);
std::size_t operand_size = strlen(operand) + 1;
data_.operand_ = new char[operand_size];
strncpy(data_.operand_, operand, strlen(operand));
std::size_t oper_num_size = strlen(oper_num) + 1;
data_.oper_num_ = new char[oper_num_size];
strncpy(data_.oper_num_, oper_num, strlen(oper_num));
}
inline void set_data(oper_data data)
{
this->data_ = data;
}
void set_data(const char *oper, const char *operand, const char *oper_num)
{
strncpy(data_.oper_, oper, max_oper_size);
std::size_t operand_size = strlen(operand) + 1;
data_.operand_ = new char[operand_size];
strncpy(data_.operand_, operand, strlen(operand));
std::size_t oper_num_size = strlen(oper_num) + 1;
data_.oper_num_ = new char[oper_num_size];
strncpy(data_.oper_num_, oper_num, strlen(oper_num));
}
data_store *get_var(const char *var_name)
{
const std::size_t queue_size = this->proc_queue.size();
for (std::size_t i=0; i < queue_size; i++) {
if (!strcmp(this->proc_queue[i].var_name(), var_name)) {
return &proc_queue[i];
}
}
return NULL;
}
bool oper_process()
{
const char *oper = this->data_.oper_;
if (!strcmp(oper, "var")) {
var();
return true;
}
else if (!strcmp(oper, "sum")) {
sum();
return true;
}
else if (!strcmp(oper, "dif")) {
dif();
return true;
}
else {
return false;
}
}
};
this is favdew_utility.hpp
#include <string>
#include <cstdlib>
bool isNumber(const char *str)
{
std::size_t length = strlen(str);
for (std::size_t i = 0; i < length; i++)
{
if (!('0' < str[i] && str[i] < '9'))
return false;
continue;
}
return true;
}
bool isEmpty(void *buffer)
{
if (!buffer || *(char *)buffer == '\0')
return true;
else
return false;
}
There are many issues, just pointing out a few:
The declaration
session ex_session(std::move(socket));
This creates a local (stack) variable that inherits from enable_shared_from_this. Using shared_from_this will be Undefined Behaviour
Session gets immediately destructed and start() appears to be never called
If session::start() were called, it would fail because it starts an async operation without guarding the lifetime of the session instance:
boost::asio::async_read(socket_,
boost::asio::buffer(input_data, sizeof(oper_data)),
[this, input_data](boost::system::error_code ec, std::size_t) { ....
At the very least you need to capture the shared pointer to the session:
auto self = shared_from_this();
boost::asio::async_read(socket_,
boost::asio::buffer(input_data, sizeof(oper_data)),
[this, self, input_data](boost::system::error_code ec, std::size_t)
Even worse, input_data is never initialized. Again: Undefined Behaviour. Even if you did initialize it, you'd have to manage lifetime; why not make it a member of the session, instead of dynamically allocating (or forgetting to, as you have now)?
Caution: No, you cannot stack-allocate inside start() not even if you capture it in the lambda, because the async operations will not complete before start() exits.
Same in client: data_ is never initialized. Boom.
Even if you had it correctly allocated, using it as an asio::buffer() treats it as a POD.
Since, however, data_proc happily aggregates a data_queue which is std::deque<> it obviously IS NOT POD. More Undefined Behaviour.
What you probably need is to serialize your datastructures, instead of hoping that copying some bytes of memory is going to magically "work". It won't!
See e.g. sending/receiving a struct in boost::asio
Note While you're at is, use C++ instead of C? All the raw pointers and char* are complexity that you don't need, and it is handing your dozens of footguns or ends or rope that you're gonna hurt yourself more with.
In client.cpp you have:
std::thread t([&io_context](){ io_context.run(); });
char line[128];
while (std::cin.getline(line, 128))
{
oper_data data;
//processing the line with deviding in 3 words.
}
Soooo many things...
use std::getline, not std::istream::getline
the thread needs to be joined (https://en.cppreference.com/w/cpp/thread/thread/~thread)
if all you do is block for input, why have the thread?
io_context.run(); // replaces all of the above
data_store is also not POD, but it is also a living memory-leak. All the new-ed memory is never freed.
Note that, the way it's written, the struct might APPEAR to be POD, but logically it isn't (Rule Of Three). Basically, you wrote it in C, not C++. This foregoes all abstractions that C++ has, and now the compiler cannot tell that the struct refers to non-owned resources.
Mind you, this gives me the impression that oper_data might have similar issues (though at first I assumed that operand_ and _oper_num are supposed to point inside the fixed-size buffer oper_[])
Summarizing:
You're way ahead of yourself. Start much simpler. Use C++ (std::string, never use new/delete, actually use std::make_shared if you want to enable_shared_from_this).
You'll be much happier. Feel free to come back with simpler questions when you get stuck, ideally the SSCCE would be a (few) dozen or so lines.
Related
The server works fine and receives all the messages when there are not prepended with \0.
Suppose if i send a message like
"Message" , and send it 100 times to the server all the messages are received by the server but if i add \0 in the end like "Message\0" , than the server only receives couple messages.
This is the code for the TCP server.
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <iostream>
#include <global.h>
#include <memory>
#include <fstream>
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self ](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
if (length > 0) {
std::string strData(data_, length);
std::cout << strData;
if (strData.find_first_not_of(' ') != std::string::npos)
{
// There's a non-space.
commandsQueue.push(strData);
}
}
do_write(length);
}
});
}
void do_write(std::size_t length)
{
if (stdqueAnswers.size() > 0) {
auto self(shared_from_this());
std::string stdstrAnswer = stdqueAnswers.front();
boost::asio::async_write(socket_, boost::asio::buffer(stdstrAnswer.c_str(), stdstrAnswer.length() ),
[this, self](boost::system::error_code ec, std::size_t /*length*/)
{
if (!ec)
{
do_read();
}
});
stdqueAnswers.pop();
}
else {
auto self(shared_from_this());
do_read();
}
}
tcp::socket socket_;
enum { max_length = 8096 };
char data_[max_length];
};
class server {
public:
server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
{
acceptor_.listen();
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
make_strand(acceptor_.get_executor()),
[this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
do_accept();
}
});
}
tcp::acceptor acceptor_;
};
At first I thought what's likely happening is that you are breaking the output stream by writing \0 to it.
However, reading on, I spotted Undefined Behavior:
std::string stdstrAnswer = stdqueAnswers.front();
async_write(socket_, boost::asio::buffer(stdstrAnswer),
[this, self](error_code ec, size_t /*length*/) {
if (!ec) {
do_read();
}
});
stdqueAnswers.pop();
You're passing a reference to stdstrAnswer, a local variable, which goes out of scope before async_write completes. That's a problem regardless of whether you included stdqueAnswers.pop(). Instead consider:
auto self(shared_from_this());
async_write(socket_, boost::asio::buffer(stdqueAnswers.front()),
[this, self](error_code ec, size_t /*length*/) {
if (!ec) {
stdqueAnswers.pop();
do_read();
}
});
Of course, there's
the code smell of unused length parameter to do_write (what was it intended for?)
the fact that stdqueAnswers is used, but never pushed to
the fact that stdqueAnswers queues answers, but the queue is never drained: instead, after a single async_write there is always just do_read, so the queue won't function as a queue.
It feels like you're trying to have full-duplex IO. In that case you need to change from the half-duplex model that you currently still use:
To something conceptually like
There's some catches how to start the write loop when the first message is pushed to the out queue. See enqueueAnswer in the code below, which shows what I expect you were after:
Live On Coliru
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <queue>
using boost::asio::ip::tcp;
using boost::system::error_code;
class session : public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
auto self(shared_from_this());
// dispatch not strictly necessary for single-threaded contexts
dispatch(
socket_.get_executor(),
[this, self]
{
do_read();
});
}
private:
// all private member functions assumed to be on-strand
std::queue<std::string> stdqueAnswers;
void handleCommand(std::string&& cmd)
{
std::cout << std::quoted(cmd);
if(cmd.find_first_not_of(' ') != std::string::npos)
{
// There's a non-space.
enqueueAnswer("Handled command: '" + cmd + "'\n");
}
}
void enqueueAnswer(std::string&& answer)
{
stdqueAnswers.push(std::move(answer));
if(stdqueAnswers.size() == 1)
{
do_write();
}
}
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some( //
boost::asio::buffer(data_),
[this, self](error_code ec, size_t length)
{
if(! ec)
{
handleCommand({data_, length});
do_read();
}
});
}
void do_write()
{
if(stdqueAnswers.empty())
return;
auto self(shared_from_this());
async_write(
socket_,
boost::asio::buffer(stdqueAnswers.front()),
[this, self](error_code ec, size_t)
{
if(! ec)
{
stdqueAnswers.pop();
do_write();
}
});
}
tcp::socket socket_;
char data_[8096];
};
class server
{
public:
server(boost::asio::io_context& io_context, std::uint16_t port)
: acceptor_{io_context, tcp::endpoint(tcp::v4(), port)}
{
acceptor_.listen();
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
make_strand(acceptor_.get_executor()),
[this](error_code ec, tcp::socket socket)
{
if(! ec)
{
std::make_shared<session>(std::move(socket))->start();
do_accept();
}
});
}
tcp::acceptor acceptor_;
};
int main()
{
boost::asio::io_context ioc;
server s(ioc, 8989);
ioc.run();
}
With a demo client:
sleep 1; printf 'hello\nworld\nnull chars \0 are the best\nbye' | netcat 127.0.0.1 8989 | xxd
Seems to handle \0 characters just fine:
Based on websocket_client_async_ssl.cpp, I modify the function of on_read so that I can save the content into a local file.
class session : public std::enable_shared_from_this<session>
{
std::ofstream outfile_text; // outfile_text.open("test.txt", std::ofstream::out);
const int MAX_LINE_COUNT; // 10
int current_line_;
...
}
void on_read_version2( beast::error_code ec, std::size_t)
{
if(ec)
return fail(ec, "read");
else
{
++current_line_;
const std::string buf_string = beast::buffers_to_string(buffer_.data());
buffer_.consume(buffer_.size());
outfile_text.write((char*)buf_string.data(), buf_string.size());
outfile_text.write("\n", 1);
if (current_line_ > MAX_LINE_COUNT)
{
outfile_text.close();
return;
}
// Re-read a message into our buffer
ws_.async_read( buffer_, beast::bind_front_handler( &session::on_read, shared_from_this()));
}
}
void on_read_version3( beast::error_code ec, std::size_t)
{
if(ec)
return fail(ec, "read");
else
{
++current_line_;
buffer_.consume(buffer_.size());
queue_.push_back(beast::buffers_to_string(buffer_.data()));
// Are we already writing?
if (queue_.size() > 1)
return;
else
// async_write to file from queue_
if (current_line_ > MAX_LINE_COUNT)
{
outfile_text.close();
return;
}
// Re-read a message into our buffer
ws_.async_read( buffer_, beast::bind_front_handler( &session::on_read, shared_from_this()));
}
}
In version2, I used a blocking method to write the content to file. While in version 3, I list the psedo-code where I like to write this part of logic with async-method.
Question>
Does boost::asio or boost::beast support async_write file?
If not, what is the best way to write content to file within the on_read function?
Thank you
Assuming POSIX, you could use stream_descriptor:
net::posix::stream_descriptor stream_{ex_, ::creat("test.txt", 0755)};
Which models the ASIO AsyncStream concept.
On Windows, you have similar types (including stream_handle).
Demo:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/beast.hpp>
#include <chrono>
#include <deque>
#include <fstream>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace websocket = beast::websocket;
using net::ip::tcp;
using namespace std::chrono_literals;
static inline void fail(beast::error_code ec, std::string_view msg) {
if (ec) {
std::cerr << msg << ": " << ec.message() << std::endl;
}
}
class session : public std::enable_shared_from_this<session> {
public:
session(net::any_io_executor ex) : ex_(ex) {}
void start()
{
// assumed on logical strand
ws_.next_layer().connect({{}, 8989});
ws_.handshake("localhost", "/");
do_read();
}
private:
const int MAX_LINE_COUNT = 10;
int current_line_ = 0;
net::streambuf buffer_;
net::any_io_executor ex_;
net::posix::stream_descriptor stream_{ex_, ::creat("test.txt", 0755)};
websocket::stream<tcp::socket> ws_{ex_};
std::deque<std::string> queue_;
void do_read() {
// assumed on strand
ws_.async_read(
buffer_,
beast::bind_front_handler(&session::on_read, shared_from_this()));
}
void on_read(beast::error_code ec, std::size_t)
{
if (ec)
return fail(ec, "read");
++current_line_; // TODO fixme count `\n` in buffer?
enqueue_output(beast::buffers_to_string(buffer_.data()) + '\n');
do_read();
}
bool file_full() const {
return current_line_ > MAX_LINE_COUNT;
}
void enqueue_output(std::string msg) {
if (file_full())
return;
queue_.push_back(std::move(msg));
buffer_.consume(buffer_.size());
// Are we already writing?
if (queue_.size() == 1)
do_write_loop();
}
void do_write_loop()
{
if (queue_.empty()){
if (file_full())
stream_.close();
return;
}
// async_write to file from queue_
net::async_write(
stream_, net::buffer(queue_.front()),
[this, self = shared_from_this()](beast::error_code ec, size_t) {
if (!ec) {
queue_.pop_front();
do_write_loop();
} // TODO error handling
});
}
};
int main()
{
net::io_context io;
std::make_shared<session>(make_strand(io.get_executor())) //
->start();
io.run_for(5s);
}
And a live demo using websocat: https://imgur.com/0TyHmBj
I modified echo example but I have issues understanding boost strand.
Do I need or not?
I yes please be kind and give code for.
I searched examples but I can not understand (Im just stupid..)
Thank you
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include "mingw.thread.h"
#include <boost/bind.hpp>
class session
: public std::enable_shared_from_this<session>
{
public:
session(boost::asio::ip::tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
input_buffer_str = "[2J[H[A\r\n*ServerV8*\r\nSCAN_BADGE:\r\n";
out_msg =input_buffer_str;
do_write(out_msg.length());
}
private:
void do_read()
{
// std::cout << "do_read" <<"" <<std::endl;
auto self(shared_from_this());
//async_read_until for Telnet client
async_read_until(socket_,input_buffer_,'\r',
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
else
{
// std::cout<<ec <<"Disconection?"<<std::endl;
input_buffer_str.clear();
out_msg.clear();
input_buffer_.consume(length);
// Some Cleanup
}
});
}
// async_write back to Telnet client based on received
// will be result on SQL Query
void do_write(std::size_t length)
{
auto self(shared_from_this());
std::istream(&input_buffer_) >> input_buffer_str;
input_buffer_.consume(length);
std::cout << input_buffer_str <<std::endl;
if (input_buffer_str == "c") close();
if (input_buffer_str == "a") out_msg = "test_a\r\n";
if (input_buffer_str == "t")
{
Sleep(30000);
out_msg = "test_t\r\n";
}
if(input_buffer_str.length()>0 && out_msg.length()>0)
{
input_buffer_str.clear();
boost::asio::async_write(socket_, boost::asio::buffer(out_msg, out_msg.length()),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec) do_read();
else
{
out_msg.clear();
input_buffer_.consume(length);
// Some Cleanup
}
out_msg.clear();
});
}else do_read();
}
// close() Telnet client will be when user pushes F2 (Handheld scanner)
void close()
{
std::cout <<"Closing..."<<std::endl;
boost::system::error_code ec;
input_buffer_str.clear();
out_msg.clear();
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
Sleep(250);
socket_.close(ec);
// delete data_;
if (ec)
{
std::cout<<ec << std::endl;
// An error occurred.
}
std::cout <<"Closed..."<<std::endl;
}
boost::asio::streambuf input_buffer_;
std::string input_buffer_str;
std::string out_msg ;
boost::asio::ip::tcp::socket socket_;
//enum { max_length = 1024 };
//char data_[max_length];
//std::string ClearScreen= "[2J[H[A"; "\r\n*ServerV8*\r\nSCAN_BADGE:\r\n";
};
Copy paste from example
class server
{
public:
server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket)
{
if (!ec)
{
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
boost::asio::ip::tcp::acceptor acceptor_;
};
added Threads Some queries to sql might take depends on Network
int main(int argc, char* argv[])
{
auto thread_count = std::thread::hardware_concurrency(); // for multi core
boost::thread_group m_Threads;
boost::asio::io_context m_io_context;
server srv(m_io_context, 23);
for(unsigned int i = 0; i < thread_count; ++i)
m_Threads.create_thread( [&](){m_io_context.run();});
m_Threads.join_all();
return 0;
}
I was implementing the ring buffer and have encountered an error. What does it mean to store a reference of outer class(class ring) object(m_ring) in inner class(class iterator) and when I remove the reference(&) the program compiles correctly but crashes. Please explain what is happening.(See the comment in Ring.h) Sorry for bad English.
// Ring.h
#ifndef RING.H
#define RING.H
#include <iostream>
using namespace std;
template<class T>
class ring {
unsigned int m_size;
int m_pos;
T *m_values;
public:
class iterator;
public:
ring(unsigned int size) : m_size(size), m_pos(0)
{
m_values = new T[m_size];
}
~ring()
{
delete[] m_values;
}
void add(const T &val)
{
m_values[m_pos] = val;
m_pos++;
m_pos %= m_size;
}
T& get(int pos)
{
return m_values[pos];
}
iterator begin()
{
return iterator(0, *this);
}
iterator end()
{
return iterator(m_size, *this);
}
};
template<class T>
class ring<T>::iterator {
int m_pos;
ring &m_ring; // Removing & gives garbage output.
public:
iterator(int pos, ring& aRing) : m_pos(pos), m_ring(aRing){}
bool operator!=(const iterator &other) const
{
return other.m_pos != m_pos;
}
iterator &operator++(int)
{
m_pos++;
return *this;
}
iterator &operator++()
{
m_pos++;
return *this;
}
T &operator*()
{
// return m_ring.m_values[m_pos];
return m_ring.get(m_pos);
}
};
#endif // RING
Driver program :
// Ring_Buffer_Class.cpp
#include <iostream>
#include "ring.h"
using namespace std;
int main()
{
ring<string> textring(3);
textring.add("one");
textring.add("two");
textring.add("three");
textring.add("four");
// C++ 98
for(ring<string>::iterator it = textring.begin(); it != textring.end(); it++)
{
cout << *it << endl;
}
cout << endl;
// C++11
for(string value : textring)
{
cout << value << endl;
}
return 0;
}
I also observed that removing ~ring() (Destructor) results into correct output.
Expected output :
four
two
three
four
two
three
I thought I had found the answer in the following example, but not quite.
boost::asio::ip::udp::socket socket(io_service);
...
boost::asio::ip::address_v4 local_interface =
boost::asio::ip::address_v4::from_string("1.2.3.4");
boost::asio::ip::multicast::outbound_interface option(local_interface);
socket.set_option(option);
How do I map eth0 to the appropriate outbound_interface option?
The following code works fine on Windows and Mac OS X:
const ip::udp::resolver::query queryIF( ip::udp::v4(),
_description->getInterface(), "0" );
const ip::udp::resolver::iterator interfaceIP =
resolver.resolve( queryIF );
if( interfaceIP == end )
return false;
const ip::address ifAddr( ip::udp::endpoint( *interfaceIP ).address( ));
_read->set_option( ip::multicast::join_group( mcAddr.to_v4(),
ifAddr.to_v4( )));
_write->set_option( ip::multicast::outbound_interface( ifAddr.to_v4()));
EDIT: I had some issues on Linux, but did not look into it yet. My guess is the socket option is ignored in favor of the routing table.
I think the reason why your example and eile's example don't work is because you didn't set the SO_BINDTODEVICE socket option.
See this to know why it doesn't work: http://codingrelic.geekhold.com/2009/10/code-snippet-sobindtodevice.html
See this to know how to do it with boost::asio: http://permalink.gmane.org/gmane.comp.lib.boost.asio.user/2724
/**************************************************************************//**
\brief
\details
*******************************************************************************/
class UDPClient : public BoostSocketClient
{
public:
UDPClient ();
virtual ~UDPClient();
virtual ARLErrorCode_e open(int port_num, const char* network_type="ipv4", const char* ip_address="", uint32_t listen_interface=0);
virtual ARLErrorCode_e send(u8* message, u32 size);
virtual ARLErrorCode_e close();
virtual bool isOpen();
//virtual void onReceived(u8*, u32);
private:
void startReceive();
void handleReceive(const boost::system::error_code&, std::size_t);
void handleSend(const boost::system::error_code& error, std::size_t bytes_transferred);
private:
boost::asio::io_service send_ios_;
std::unique_ptr<boost::asio::io_service::work> send_worker_;
boost::asio::io_service receive_ios_;
std::unique_ptr<boost::asio::io_service::work> receive_worker_;
boost::thread send_thread_;
boost::thread receive_thread_;
boost::array<u8, 1024> _buffer;
boost::asio::ip::udp::endpoint send_endpoint_;
boost::asio::ip::udp::endpoint sender_endpoint_;
boost::asio::ip::udp::endpoint listen_endpoint_;
std::unique_ptr<boost::asio::ip::udp::socket> send_udp_socket_;
std::unique_ptr<boost::asio::ip::udp::socket> receive_udp_socket_;
};
#include <ACCompLib/Include/Typedefs.h>
#include <ACCompLib/Include/ARLErrorCodes.h>
#include <NetLib/Platform/Boost/cpp/UDPClient.h>
#include "Ws2tcpip.h"
#include "Iphlpapi.h"
using namespace std;
using namespace boost;
using namespace asio;
using namespace ip;
using namespace NetLib;
/***************************************************************************//**
\brief Constructor
\details
*******************************************************************************/
UDPClient::UDPClient()
{
receive_worker_.reset(new boost::asio::io_service::work(receive_ios_));
}
/***************************************************************************//**
\brief ctor
\details
*******************************************************************************/
UDPClient::~UDPClient()
{
try
{
receive_worker_.reset();
//send_worker_.reset();
if (send_thread_.joinable()) {
send_thread_.join();
}
if (receive_thread_.joinable()) {
receive_thread_.join();
}
}
catch (std::exception& e)
{
std::string str = e.what();
}
}
/***************************************************************************//**
\brief
\details
\note
\param[in]
*******************************************************************************/
ARLErrorCode_e UDPClient::open(int port_num, const char* network_type, const char* multicastAddress, uint32_t listen_interface)
{
try
{
struct in_addr in;
in.S_un.S_addr = listen_interface;
char* address_listen = inet_ntoa(in);
//const char* address_listen = "0.0.0.0";
std::string address_mcast = multicastAddress;
unsigned short address_port = port_num;
boost::system::error_code ec;
boost::asio::ip::address listen_addr = boost::asio::ip::address::from_string(address_listen, ec);
boost::asio::ip::address mcast_addr = boost::asio::ip::address::from_string(address_mcast, ec);
if (strcmp(network_type, "ipv4") == 0)
{
listen_endpoint_ = udp::endpoint(listen_addr, port_num);
send_endpoint_ = udp::endpoint(mcast_addr, port_num);
}
else if (strcmp(network_type, "ipv6") == 0)
{
}
else
return ES35_INVALID_SOCKET_CONNECTION;
send_udp_socket_.reset(new boost::asio::ip::udp::socket(send_ios_));
receive_udp_socket_.reset(new boost::asio::ip::udp::socket(receive_ios_));
send_udp_socket_->open(boost::asio::ip::udp::v4());
receive_udp_socket_->open(listen_endpoint_.protocol());
send_udp_socket_->set_option(boost::asio::ip::udp::socket::reuse_address(true));
receive_udp_socket_->set_option(boost::asio::ip::udp::socket::reuse_address(true));
boost::asio::ip::address_v4 local_interface =
boost::asio::ip::address_v4::from_string(address_listen);
boost::asio::ip::multicast::outbound_interface option(local_interface);
// Join the multicast group.
receive_udp_socket_->set_option(
boost::asio::ip::multicast::join_group(mcast_addr));
send_udp_socket_->set_option(option);
receive_udp_socket_->set_option(option);
boost::asio::ip::multicast::hops hops_option(3);
//send_udp_socket_->set_option(hops_option);
receive_udp_socket_->set_option(hops_option);
receive_udp_socket_->bind(listen_endpoint_);
startReceive();
receive_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &receive_ios_));
send_thread_ = boost::thread(boost::bind(&boost::asio::io_service::run, &send_ios_));
return ES_NoError;
}
catch (std::exception& exp)
{
std::string str = exp.what();
return ES35_INVALID_SOCKET_CONNECTION;
}
}
/***************************************************************************//**
\brief
\details
*******************************************************************************/
ARLErrorCode_e UDPClient::close(void)
{
try {
boost::system::error_code ec;
//udp_socket_->cancel();
//udp_socket_.shutdown(socket_base::shutdown_both, ec);
if (ec)
{
return ES35_INVALID_SOCKET_CONNECTION;
}
if (send_udp_socket_->is_open())
{
send_udp_socket_->close();
}
if (receive_udp_socket_->is_open())
{
receive_udp_socket_->close();
}
receive_udp_socket_.reset();
send_udp_socket_.reset();
}
catch (std::exception& e)
{
std::string str = e.what();
return ES35_INVALID_SOCKET_CONNECTION;
}
return ES_NoError;
}
/***************************************************************************//**
\brief
\details
*******************************************************************************/
bool UDPClient::isOpen()
{
return send_udp_socket_->is_open() && receive_udp_socket_->is_open();
}
/***************************************************************************//**
\brief Send a message.
\details The message is sent asynchronously.
\param message
\param message size
*******************************************************************************/
ARLErrorCode_e UDPClient::send(u8* message, u32 size)
{
if (!isOpen()) {
return ES35_INVALID_SOCKET_CONNECTION;
}
std::string send_to_address = send_endpoint_.address().to_string();
send_udp_socket_->set_option(asio::ip::multicast::enable_loopback(false));
send_udp_socket_->async_send_to(
buffer(message, size),
send_endpoint_,
bind(
&UDPClient::handleSend,
this,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
return ES_NoError;
}
/***************************************************************************//**
\brief Do nothing.
\details This function has the required signature to be used as an
asynchronous send completion handler.
\param not used
\param not used
*******************************************************************************/
void UDPClient::handleSend(const system::error_code& error, size_t)
{
if (error)
{
BoostSocketClient::onError(ES35_UDP_SEND_ERROR, (u8*)error.message().c_str(), error.message().size());
}
else
{
send_udp_socket_->set_option(asio::ip::multicast::enable_loopback(true));
}
}
/***************************************************************************//**
\brief Start an asynchronous receiver.
*******************************************************************************/
void NetLib::UDPClient::startReceive()
{
receive_udp_socket_->async_receive_from(
buffer(_buffer),
sender_endpoint_,
bind(
&UDPClient::handleReceive,
this,
asio::placeholders::error,
asio::placeholders::bytes_transferred));
std::string sender_address = sender_endpoint_.address().to_string();
}
/***************************************************************************//**
\brief Pass received data to the base class.
\details A new receiver is started.
\param error code
\param data size
*******************************************************************************/
void NetLib::UDPClient::handleReceive(const system::error_code& error, size_t size)
{
if (!error || error == error::message_size)
{
BoostSocketClient::onReceived(_buffer.data(), size);
startReceive();
}
else
{
BoostSocketClient::onError(ES35_UDP_RECEIVE_ERROR, (u8*)error.message().c_str(), error.message().size());
}
}
This code is not receiving response.please check
http://permalink.gmane.org/gmane.comp.lib.boost.asio.user/2724 is invalid.
The following code seems to be invalid:
boost::asio::ip::address_v4 local_interface =
boost::asio::ip::address_v4::from_string(ip);
boost::asio::ip::multicast::outbound_interface option(local_interface);
sock.set_option(option);