How to start Boost echo server in a separate thread - boost

I was using boost version boost 1_64_0 to run a Tcp server in a seprate thread
This was the code for the server.
https://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp
in the Main loop i could spawn a new thread like this.
int main()
{
// Run the boost echo server as a different thread
boost::asio::io_service io_service;
server server1(io_service, 1980);
boost::thread t(boost::bind(&io_service::run, &io_service));
// when about to close the program
io_service.stop(); // stop the server
t.join();
}
Now i have changed the boost version to boost_1_73_0 and i am using this example to create a server
https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
how do to create a new thread ?
The existing code to create a new thread gives error.
io_service: left of :: must be a class/struct/union
run : undeclared identifier

In &io_service::run, io_service is not a type but the local variable by that name.
Starting from https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
io_context.run();
Needs to be like
std::thread t([&] { io_context.run(); });
// ...
t.join();
Or if you insist:
std::thread t(boost::bind(&boost::asio::io_context::run, &io_context));
I would use a thread_pool to shortcut all the complexities¹:
boost::asio::thread_pool io(1);
server s(io.get_executor(), std::atoi(argv[1]));
// ...
io.join();
A trivial edit to the class interface is required (see e.g. Boost 1.70 io_service deprecation)
Live On Coliru
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
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) {
do_write(length);
}
});
}
void do_write(std::size_t length) {
auto self(shared_from_this());
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
do_read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server {
public:
template <typename Executor>
server(Executor ex, short port)
: acceptor_(ex, tcp::endpoint(tcp::v4(), port)) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::thread_pool io(1);
server s(io.get_executor(), std::atoi(argv[1]));
// ...
io.join();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
¹ see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?

Related

Boost Server breaking the messages from the client

If i send messages locally from the same system than the boost server receives messages properly.
When the client is a remote application on other system and sending messages through TCP\IP than randomly some messages break(Line Enter).
Like if the client has sent "THIS IS A MESSAGE" the server will read it as
"THIS IS A ME
SSAGE"
This is the Server class.
#pragma once
#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>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <queue>
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()
{
auto self(shared_from_this());
// dispatch not strictly necessary for single-threaded contexts
dispatch(
socket_.get_executor(),
[this, self]
{
do_read();
});
}
private:
void handleCommand()
{
enqueueAnswer();
}
void enqueueAnswer()
{
if (stdqueAnswers.size() == 1)
{
do_write();
}
}
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) {
// In case the message has a leading 1 than we have to send a answer back to the client.
if (data_[0] == '1') {
std::string stdstrCmd(data_);
stdstrCmd.erase(0, 2);
wavefrontAccess->ReceiveCommandExternalGet(stdstrCmd);
handleCommand();
}
else
{
std::string strData(data_, length);
if(!strData.empty() || strData.find_first_not_of(' ') != std::string::npos)
{
// There's a non-space.
commandsQueue.push(strData); // this is std Queue
}
}
}
do_read();
}
});
}
void do_write()
{
if (stdqueAnswers.empty())
return;
auto self(shared_from_this());
async_write(
socket_,
boost::asio::buffer(stdqueAnswers.front()),
[this, self](boost::system::error_code ec, size_t)
{
if (!ec)
{
stdqueAnswers.pop();
do_write();
}
});
}
tcp::socket socket_;
enum { max_length = 12000 };
char data_[max_length];
};
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](boost::system::error_code ec, tcp::socket socket)
{
if (!ec)
{
std::make_shared<session>(std::move(socket))->start();
do_accept();
}
});
}
tcp::acceptor acceptor_;
};
Like Ruslan explains, you should not use read_some, but a higher level operation that reads a full "message", as defined by your application level wire protocol.
You clearly already have some protocol (the leading bytes), and we cannot guess what the rest could be. For simplicity, let's assume that a full message ends with a \n character. Here's my simplifying take:
async_read_until(
socket_, boost::asio::dynamic_buffer(data_, max_length), "\n",
[this, self](boost::system::error_code ec, size_t length) {
std::cerr << "async_read_until() " << ec.message() << std::endl;
if (!ec) {
std::string msg = data_.substr(0, length /* - 1*/);
data_.erase(0, length);
if (!msg.empty() && msg.front() == '1') {
// we have to send a answer back to the client
wavefrontAccess->ReceiveCommandExternalGet(msg.substr(2));
handleCommand();
} else {
if (msg.find_first_not_of(' ') != std::string::npos) {
// There's a non-space
commandsQueue.push(msg);
}
}
do_read();
}
});
I've simplified by making your buffer std::string directly. This immediately "solves" the complexity that read_until might obviously read more than a single message.
Uncomment the /* -1 */ to exclude the \n character from the message
With some missing bits mocked up:
Live On Coliru
#include <boost/asio.hpp>
#include <deque>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <queue>
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() {
auto self(shared_from_this());
std::cerr << "start() " << socket_.remote_endpoint() << std::endl;
dispatch(socket_.get_executor(), [this, self] { do_read(); });
}
private:
void handleCommand() { enqueueAnswer(); }
void enqueueAnswer() {
if (stdqueAnswers.size() == 1) {
do_write();
}
}
void do_read() {
auto self(shared_from_this());
async_read_until(
socket_, boost::asio::dynamic_buffer(data_, max_length), "\n",
[this, self](boost::system::error_code ec, size_t length) {
std::cerr << "async_read_until() " << ec.message() << std::endl;
if (!ec) {
std::string msg = data_.substr(0, length /* - 1*/);
data_.erase(0, length);
if (!msg.empty() && msg.front() == '1') {
// we have to send a answer back to the client
wavefrontAccess->ReceiveCommandExternalGet(msg.substr(2));
handleCommand();
} else {
if (msg.find_first_not_of(' ') != std::string::npos) {
// There's a non-space
commandsQueue.push(msg);
}
}
do_read();
}
});
}
void do_write() {
if (stdqueAnswers.empty())
return;
auto self(shared_from_this());
async_write( //
socket_, boost::asio::buffer(stdqueAnswers.front()),
[this, self](boost::system::error_code ec, size_t) {
std::cerr << "async_write() " << ec.message() << std::endl;
if (!ec) {
stdqueAnswers.pop_back();
do_write();
}
});
}
enum { max_length = 12000 };
tcp::socket socket_;
std::string data_;
std::deque<std::string> stdqueAnswers;
std::queue<std::string> commandsQueue;
struct WavefrontAccess {
session* _sess;
void ReceiveCommandExternalGet(std::string cmd) {
_sess->stdqueAnswers.push_back("reply for '" + std::move(cmd) + "'");
}
};
std::unique_ptr<WavefrontAccess> wavefrontAccess =
std::make_unique<WavefrontAccess>(WavefrontAccess{this});
};
class server {
public:
server(boost::asio::io_context& io_context, uint16_t port)
: acceptor_{io_context, {{}, 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) {
std::cerr << "accept: " << ec.message() << " "
<< (ec ? tcp::endpoint{} : socket.remote_endpoint())
<< std::endl;
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
do_accept();
}
});
}
tcp::acceptor acceptor_;
};
int main() {
boost::asio::io_context ioc(1);
server s(ioc, 7878);
ioc.run();
}
Even locally, messages may split into smaller TCP packets, for both client and server.
"Framing" protocol must be able to encode and decode sequence of variable sized messages unambiguously for any sequence of split parts (e.g. abc, ab c, a bc, a b c).
Your protocol can do "framing" by itself, or work on top of other "framing" protocol.
Example: TCP
Fixed length header (20 bytes) contains size(s) of variable size fields, including message content.
TCP reads 20 bytes.
TCP parses message size.
TCP reads that amount of bytes.
(repeat)
Example: HTTP
Header doesn't have fixed length.
However it's structure is unambiguous.
It's a text which ends with \r\n\r\n.
This text mustn't include \r\n\r\n, but it can represent it with escaping.
Somewhere in that text, there is size of message.
HTTP reads text (character by character) until it sees \r\n\r\n.
HTTP parses message size.
HTTP reads that amount of bytes.
(repeat)
Possible solution
If your message text doesn't have limitations (e.g. may include terminator/escape string), then you need to write message size before each message.
Otherwise if your messages have structure, you can write "terminator" (e.g. end-of-line or zero-terminator) after end each message, and read message until "terminator".

Boost TCP server not able to receive string messages with \0 in the end

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:

How to write content to file asynchronously with boost::beast or boost::asio?

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

Do i need boost strand

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

boost::asio::async_write, writing data larger than 65536 bytes

I'm attempting to write jpeg frames via a socket to a client using async_write(). I used the boost asynchronous TCP daytime server example as a starting point.
#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
std::string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time(0);
return ctime(&now);
}
class tcp_connection
: public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
message_ = make_daytime_string();
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
tcp_connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
tcp::socket socket_;
std::string message_;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))
{
start_accept();
}
private:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.io_service());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code& error)
{
if (!error)
{
new_connection->start();
start_accept();
}
}
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I have modified the method that performs the async_write() as follows:
void start()
{
// fileToVector method reads contents of file to vector;
std::vector<unsigned char> message_ = fileToVector("/tmp/test");
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
When reading a large file from the server using a client, the server will only write a maximum of 65536 bytes. If I replace the boost::asio::async_write() call with a synchronous call boost::asio::write() the correct amount of bytes are transferred to the client.
So I suppose my question is, how can I send more than 65536 bytes using boost::asio::async_write()? Any help would be greatly appreciated.
An issue is that using the async_write function data will be send not immediately by the function but in some time after the start method is finished and the local message_ variable will be destroyed and the boost::asio::buffer does not copy the content of message_. It stores only a reference to it. The result is unpredictable. May be transmission of 65536 bytes is the result of this behavior.

Resources