Send multiple files per connection with boost iostream - c++

I'm trying to make a streaming application using boost with iostream, but the server is not separating the image frame
in the receive loop, is getting everything in one file(does not close the file, and continues to receive the other frames in the same file).
The only solution i could find was sent a frame for connection, but is leaving the streaming very slow.
currently sending 1 file per connection and everything works (slowly on remote networks)
i want to change it to send multiple files per connection (I think i will have a gain in performance), but I'm having the problem mentioned above.
the "/tmp/img.frame" must be overwritten
below the code I'm using (changed just to make one connection)
void send_()
{
boost::scoped_ptr<screenshot> ptr_screen(new screenshot);
handle_connection = true;
boost::asio::io_service svc;
boost::asio::ip::tcp::iostream stream_(boost::asio::ip::tcp::resolver::query{ "127.0.0.1", "6293" });
boost::iostreams::filtering_ostream out;
out.push(boost::iostreams::zlib_compressor());
out.push(stream_);
while (handle_connection) {
ptr_screen->Start(); // get screen.jpg
std::ifstream ifs("screen.jpg", std::ios::binary);
out << ifs.rdbuf();
out.flush();
ifs.close();
}
}
void receiver_()
{
connection_handle = true;
try
{
boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 6293);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
boost::asio::ip::tcp::iostream stream;
boost::system::error_code ec;
acceptor.accept(*stream.rdbuf(), ec);
if(!stream) { return; }
boost::iostreams::filtering_istream in;
in.push(boost::iostreams::zlib_decompressor());
in.push(stream);
while(connection_handle)
{
std::ofstream ofs("/tmp/img.frame", std::ios::binary); // must be overwritten
copy(in, ofs);
ofs.close();
}
}
catch (std::exception& e)
{
std::cerr << "\n[-] " << e.what() << std::endl;
}
}

Regardless of the underlying technology, you have to realize that TCP. Is a streaming, message-less protocol. If you want to send individual messages of any sort and have them picked apart correctly at the receding end, you have to implement an application protocol of some sort:
length word prefix
type-length-value
STX/ETX, with escaping in full detail
self-describing protocol such as XML
etc. etc. etc.

Related

boost::asio::read with completion condition boost::asio::transfer_at_least(1) won't read until EOF

I have a Python echo server made in asyncio and a C++ client that makes use of Boost's Asio. While the echo server works properly, the client does not. The client sends a message that is 3000 characters long, but only receives a response that is 512 characters long from the server even though the client is set to listen until EOF.
Server:
import asyncio
async def handle_client(reader, writer):
received = (await reader.read(3000)).decode("utf8")
print(received)
response = received
writer.write(response.encode("utf8"))
await writer.drain()
writer.close()
async def run_server():
server = await asyncio.start_server(handle_client, "localhost", 15555)
async with server:
await server.serve_forever()
asyncio.run(run_server())
Client:
#include <boost/asio.hpp>
#include <string>
#include <iostream>
int main() {
boost::asio::io_context io_context;
boost::asio::ip::tcp::socket socket(io_context);
boost::asio::ip::tcp::resolver resolver(io_context);
socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 15555));
// This message is 3000 characters long.
std::string message = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
boost::system::error_code error;
boost::asio::write(socket, boost::asio::buffer(message), error);
if (error) {
std::cerr << "error while sending the long message: " << error.message() << "\n";
}
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_at_least(1), error);
if (!error || error != boost::asio::error::eof) {
std::string received_data = boost::asio::buffer_cast<const char*>(receive_buffer.data());
std::cout << received_data << "\n";
}
}
The client output looks like this (according to Python, there is only 512 "a"s):
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa²²²²
What seems to be wrong here? If using boost::asio::read with the completion condition boost::asio::transfer_at_least(1) is not the right way to read until EOF, how can I achieve this?
even though the client is set to listen until EOF.
How so? The code
boost::asio::streambuf receive_buffer;
boost::asio::read(socket, receive_buffer, boost::asio::transfer_at_least(1), error);
Specifically tells the read operation may return as soon as the completion condition is met: transfer_at_least(1). So, as soon as a single byte is read, the operation will complete.
Now, since packets on the wirte usually don't carry a single byte, you will get whatever was already in the TCP buffers or is the first packet to arrive.
Simply use boost::asio::transfer_all() instead.
It also looks like the condition is flawed. Did you mean
if (!error || error == boost::asio::error::eof) {
...
}

Async accept an ssl socket using asio and c++

I am trying to write an async server using asio with SSL encrypted sockets. Currently I have code that does not use SSL, and after following this tutorial I have a basic idea of how to accept an SSL socket, however I do not know how to adapt this code to accept an SSL connection:
void waitForClients() {
acceptor.async_accept(
[this](std::error_code ec, asio::ip::tcp::socket socket) {
if (!ec) {
Conn newConn = std::make_shared<Connection>(ctx, std::move(socket));
connections.push_back(newConn);
} else {
std::cerr << "[SERVER] New connection error: " << ec.message() << "\n";
}
waitForClients();
}
);
}
//this is how the tutorial shows to accept a connection
ssl_socket socket(io_context, ssl_context);
acceptor.accept(socket.next_layer());
The issue is that the callback for acceptor.async_accept gives an ordinary asio::ip::tcp::socket rather than an asio::ssl::ssl_socket<asio::ip::tcp::socket>, and I cannot find any documentation that suggests there is a method of async_accepting an SSL socket in such a way. The only method I have seen is to construct a socket first then accept it afterwards, which cannot be done in this asynchronous manner.
Any help would be much appreciated.
I solved the problem by realising that the second argument to the constructor of asio::ssl::stream<asio::ip::tcp::socket> is any initialiser for the underlying type asio::ip::tcp::socket. Thus the problem can be solved:
void waitForClients() {
acceptor.async_accept(
[this](std::error_code ec, asio::ip::tcp::socket socket) {
if (!ec) {
//initialise an ssl stream from already created socket
asio::ssl::stream<asio::ip::tcp::socket> sslStream(sslCtx, std::move(socket);
//then pass it on to be used
Conn newConn = std::make_shared<Connection>(ctx, sslStream);
connections.push_back(newConn);
} else {
std::cerr << "[SERVER] New connection error: " << ec.message() << "\n";
}
waitForClients();
}
);
}

boost socket comms are not working past one exchange

I am converting an app which had a very simple heartbeat / status monitoring connection between two services. As that now needs to be made to run on linux in addition to windows, I thought I'd use boost (v1.51, and I cannot upgrade - linux compilers are too old and windows compiler is visual studio 2005) to accomplish the task of making it platform agnostic (considering, I really would prefer not to either have two code files, one for each OS, or a littering of #defines throughout the code, when boost offers the possibility of being pleasant to read (6mos after I've checked in and forgotten this code!)
My problem now, is the connection is timing out. Actually, it's not really working at all.
First time through, the 'status' message is sent, it's received by the server end which sends back an appropriate response. Server end then goes back to waiting on the socket for another message. Client end (this code), sends the 'status' message again... but this time, the server never receives it and the read_some() call blocks until the socket times out. I find it really strange that
The server end has not changed. The only thing that's changed, is my having altered the client code from basic winsock2 sockets, to this code. Previously, it connected and just looped through send / recv calls until the program was aborted or the 'lockdown' message was received.
Why would subsequent calls (to send) silently fail to send anything on the socket and, what do I need to adjust in order to restore the simple send / recv flow?
#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
using namespace std;
boost::system::error_code ServiceMonitorThread::ConnectToPeer(
tcp::socket &socket,
tcp::resolver::iterator endpoint_iterator)
{
boost::system::error_code error;
int tries = 0;
for (; tries < maxTriesBeforeAbort; tries++)
{
boost::asio::connect(socket, endpoint_iterator, error);
if (!error)
{
break;
}
else if (error != make_error_code(boost::system::errc::success))
{
// Error connecting to service... may not be running?
cerr << error.message() << endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
}
}
if (tries == maxTriesBeforeAbort)
{
error = make_error_code(boost::system::errc::host_unreachable);
}
return error;
}
// Main thread-loop routine.
void ServiceMonitorThread::run()
{
boost::system::error_code error;
tcp::resolver resolver(io_service);
tcp::resolver::query query(hostnameOrAddress, to_string(port));
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
error = ConnectToPeer(socket, endpoint_iterator);
if (error && error == boost::system::errc::host_unreachable)
{
TerminateProgram();
}
boost::asio::streambuf command;
std::ostream command_stream(&command);
command_stream << "status\n";
boost::array<char, 10> response;
int retry = 0;
while (retry < maxTriesBeforeAbort)
{
// A 1s request interval is more than sufficient for status checking.
boost::this_thread::sleep_for(boost::chrono::seconds(1));
// Send the command to the network monitor server service.
boost::asio::write(socket, command, error);
if (error)
{
// Error sending to socket
cerr << error.message() << endl;
retry++;
continue;
}
// Clear the response buffer, then read the network monitor status.
response.assign(0);
/* size_t bytes_read = */ socket.read_some(boost::asio::buffer(response), error);
if (error)
{
if (error == make_error_code(boost::asio::error::eof))
{
// Connection was dropped, re-connect to the service.
error = ConnectToPeer(socket, endpoint_iterator);
if (error && error == make_error_code(boost::system::errc::host_unreachable))
{
TerminateProgram();
}
continue;
}
else
{
cerr << error.message() << endl;
retry++;
continue;
}
}
// Examine the response message.
if (strncmp(response.data(), "normal", 6) != 0)
{
retry++;
// If we received the lockdown response, then terminate.
if (strncmp(response.data(), "lockdown", 8) == 0)
{
break;
}
// Not an expected response, potential error, retry to see if it was merely an aberration.
continue;
}
// If we arrived here, the exchange was successful; reset the retry count.
if (retry > 0)
{
retry = 0;
}
}
// If retry count was incremented, then we have likely encountered an issue; shut things down.
if (retry != 0)
{
TerminateProgram();
}
}
When a streambuf is provided directly to an I/O operation as the buffer, then the I/O operation will manage the input sequence appropriately by either commiting read data or consuming written data. Hence, in the following code, command is empty after the first iteration:
boost::asio::streambuf command;
std::ostream command_stream(&command);
command_stream << "status\n";
// `command`'s input sequence contains "status\n".
while (retry < maxTriesBeforeAbort)
{
...
// write all of `command`'s input sequence to the socket.
boost::asio::write(socket, command, error);
// `command.size()` is 0, as the write operation will consume the data.
// Subsequent write operations with `command` will be no-ops.
...
}
One solution would be to use std::string as the buffer:
std::string command("status\n");
while (retry < maxTriesBeforeAbort)
{
...
boost::asio::write(socket, boost::asio::buffer(command), error);
...
}
For more details on streambuf usage, consider reading this answer.

Write:Unitialized Error when performing boost::asio::async_write

I have assigned to create a HTTPS server using boost::asio, So i did spent some time in the internet and found one source that explains how we can combine boost HTTP and its SSL features together which wasn't explained in the boost official website.Everything has gone fine and now i am in execution phase, that's where a mind sicking problem rose,in my code after i constructed the request stream i am using boost::asio::async_write to deliver it,During runtime i was receiving an error like the below, I am very certain that it caused by boost::asio::async_write, But I am not certain about what caused it to do so, can anyone shed some light for me,I have been wandering in the darkness:( (please see my code below)
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): write: uninitialized
using boost::asio::ip::tcp;
string my_password_callback(size_t, boost::asio::ssl::context_base::password_purpose);
void handle_resolve(const boost::system::error_code& ,
tcp::resolver::iterator);
bool verify_certificate();
void handle_read();
void handle_write();
int i,j,rc;
sqlite3 *db;
string selectsql;
sqlite3_stmt *stmt;
char *zErrMsg = 0;
stringstream ss;
boost::asio::io_service io_service1;
boost::asio::io_service &io_service(io_service1);
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
boost::asio::ssl::context& context_=ctx;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_(io_service,context_);
int main()
{
boost::shared_ptr<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
context_.set_options(boost::asio::ssl::context::default_workarounds| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
context_.set_password_callback(my_password_callback);
context_.use_certificate_chain_file("SSL\\test.crt");
context_.use_private_key_file("SSL\\test.key", boost::asio::ssl::context::pem);
tcp::resolver resolver_(io_service);
tcp::resolver::query query("172.198.72.135:3000", "http");
resolver_.async_resolve(query,boost::bind(handle_resolve,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
boost::asio::streambuf request;
string path="https://172.198.72.135:3000/journals/enc_data?";
while(true)
{
char * EJTEXT;
int ID;
if(sqlite3_open("c:\\MinGW\\test.db", &db))
{
selectsql="select IEJ,EJ from EJ limit 1";
sqlite3_prepare_v2(db, selectsql.c_str(), -1, &stmt, NULL);
if(sqlite3_step(stmt)==SQLITE_ROW){
ID=sqlite3_column_int(stmt,0);
EJTEXT=(char *)sqlite3_column_text(stmt,1);
}
else{
}
sqlite3_finalize(stmt);
sqlite3_close(db);
}
string EJ=EJTEXT;
E.Encrypt(EJ);
string data=E.Url_safe(E.cipher);--my logic
string Iv=E.Url_safe(E.encoded_iv);--my logic
std::ostream request_stream(&request);
request_stream << "POST " <<path+"Data="+data+"&"+"iv="+Iv;
request_stream << "Host: " <<"172.198.72.135"<< "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
//try{
boost::asio::async_write(socket_, request,
boost::asio::transfer_at_least(1),
boost::bind(handle_write));
temp="";
data="";
Iv="";
boost::asio::streambuf response;
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
l.HTTP_SSLLOG("Invalid response");
}
if (status_code== 200)
{
string deletesql="delete * from EJ where IEJ="+ID;
if(sqlite3_open("c:\\MinGW\\test.db", &db))
{
rc=sqlite3_exec(db, deletesql.c_str(), 0, 0, &zErrMsg);
sqlite3_close(db);
if(rc)
{
ss<<ID;
l.EJ_Log("ERROR DELETING EJ FOR "+ss.str());
}
}
else{
l.DB_Log("ERROR OPENING DB");
}
}
else{
continue;
}
Sleep(6000);
}
return 0;
}
string my_password_callback(size_t t, boost::asio::ssl::context_base::password_purpose p)//std::size_t max_length,ssl::context::password_purpose purpose )
{
std::string password;
return "balaji";
}
void handle_resolve(const boost::system::error_code& err,
tcp::resolver::iterator endpoint_iterator)
{
if (!err)
{
socket_.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
socket_.set_verify_callback(boost::bind(verify_certificate));
boost::asio::connect(socket_.lowest_layer(), endpoint_iterator);
}
else
{
l.HTTP_SSLLOG("Error resolve: "+err.message());
}
}
bool verify_certificate()
{
bool preverified =true;
context_.set_default_verify_paths();
return preverified;
}
void handle_read()
{
}
void handle_write()
{
boost::asio::async_read_until(socket_, response, "\r\n",
boost::bind(handle_read));
}
The asynchronous operations are designed to not throw exceptions and instead pass errors to the completion handlers as their first parameter (boost::system::error_code). For example, the following program demonstrates async_write() failing with an uninitialized error:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
int main()
{
boost::asio::io_service io_service;
boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(io_service, ctx);
boost::asio::async_write(socket, boost::asio::buffer("demo"),
[](const boost::system::error_code& error, std::size_t bytes_transferred)
{
std::cout << error.message() << std::endl;
});
io_service.run();
}
The above program will output uninitialized. If an exception is being thrown from an asynchronous operation, then it strongly suggest that undefined behavior is being invoked.
Based on the posted code, the async_write() operation may violate the requirement where ownership of the underlying buffer memory is retained by the caller, who must guarantee that it remains valid until the handler is called. In this case, if the next iteration of the while loop may invalidate the buffer that had been provided to the prior iteration's async_write() operation.
However, even in the absence of undefined behavior, there will be additional problems, as the program neither attempts to establish the connection nor performs the SSL handshake, both of which must be completed before transmitting or receiving data over an encrypted connection.
When using asynchronous operations, a while-sleep loop that is part of the overall operation flow is often an indication of code smell. Consider removing the sqlite3 and encrypt code, and getting an SSL prototype up and running first. It may also help to compile with the highest warning-level/pedantic flags enabled. The Boost.Asio SSL overview shows a typical synchronous usage pattern:
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
typedef ssl::stream<tcp::socket> ssl_socket;
// Create a context that uses the default paths for
// finding CA certificates.
ssl::context ctx(ssl::context::sslv23);
ctx.set_default_verify_paths();
// Open a socket and connect it to the remote host.
boost::asio::io_service io_service;
ssl_socket sock(io_service, ctx);
tcp::resolver resolver(io_service);
tcp::resolver::query query("host.name", "https");
boost::asio::connect(sock.lowest_layer(), resolver.resolve(query));
sock.lowest_layer().set_option(tcp::no_delay(true));
// Perform SSL handshake and verify the remote host's
// certificate.
sock.set_verify_mode(ssl::verify_peer);
sock.set_verify_callback(ssl::rfc2818_verification("host.name"));
sock.handshake(ssl_socket::client);
// ... read and write as normal ...
The official SSL example can also serve as a great starting point or reference for using asynchronous operations. Once the SSL prototype is confirmed as working, then add the sqlite3 and encrypt logic back into the program.
Also, in the event multiple threads are being used, be aware that the SSL stream is not thread-safe. All asynchronous operations must be synchronized through an explicit strand. For composed operations, such as async_write(), the initiating function must be invoked within the context of a strand, and the completion handler must be wrapped by the same strand.

boost asio tcp async read/write

i have an understanding problem how boost asio handles this:
When I watch my request response on client side, I can use following boost example Example
But I don't understand what happens if the server send every X ms some status information to the client. Have I open a serperate socket for this or can my client difference which is the request, response and the cycleMessage ?
Can it happen, that the client send a Request and read is as cycleMessage? Because he is also waiting for async_read because of this Message?
class TcpConnectionServer : public boost::enable_shared_from_this<TcpConnectionServer>
{
public:
typedef boost::shared_ptr<TcpConnectionServer> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new TcpConnectionServer(io_service));
}
boost::asio::ip::tcp::socket& socket()
{
return m_socket;
}
void Start()
{
SendCycleMessage();
boost::asio::async_read(
m_socket, boost::asio::buffer(m_data, m_dataSize),
boost::bind(&TcpConnectionServer::handle_read_data, shared_from_this(), boost::asio::placeholders::error));
}
private:
TcpConnectionServer(boost::asio::io_service& io_service)
: m_socket(io_service),m_cycleUpdateRate(io_service,boost::posix_time::seconds(1))
{
}
void handle_read_data(const boost::system::error_code& error_code)
{
if (!error_code)
{
std::string answer=doSomeThingWithData(m_data);
writeImpl(answer);
boost::asio::async_read(
m_socket, boost::asio::buffer(m_data, m_dataSize),
boost::bind(&TcpConnectionServer::handle_read_data, shared_from_this(), boost::asio::placeholders::error));
}
else
{
std::cout << error_code.message() << "ERROR DELETE READ \n";
// delete this;
}
}
void SendCycleMessage()
{
std::string data = "some usefull data";
writeImpl(data);
m_cycleUpdateRate.expires_from_now(boost::posix_time::seconds(1));
m_cycleUpdateRate.async_wait(boost::bind(&TcpConnectionServer::SendTracedParameter,this));
}
void writeImpl(const std::string& message)
{
m_messageOutputQueue.push_back(message);
if (m_messageOutputQueue.size() > 1)
{
// outstanding async_write
return;
}
this->write();
}
void write()
{
m_message = m_messageOutputQueue[0];
boost::asio::async_write(
m_socket,
boost::asio::buffer(m_message),
boost::bind(&TcpConnectionServer::writeHandler, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void writeHandler(const boost::system::error_code& error, const size_t bytesTransferred)
{
m_messageOutputQueue.pop_front();
if (error)
{
std::cerr << "could not write: " << boost::system::system_error(error).what() << std::endl;
return;
}
if (!m_messageOutputQueue.empty())
{
// more messages to send
this->write();
}
}
boost::asio::ip::tcp::socket m_socket;
boost::asio::deadline_timer m_cycleUpdateRate;
std::string m_message;
const size_t m_sizeOfHeader = 5;
boost::array<char, 5> m_headerData;
std::vector<char> m_bodyData;
std::deque<std::string> m_messageOutputQueue;
};
With this implementation I will not need boost::asio::strand or? Because I will not modify the m_messageOutputQueue from an other thread.
But when I have on my client side an m_messageOutputQueue which i can access from an other thread on this point I will need strand? Because then i need the synchronization? Did I understand something wrong?
The differentiation of the message is part of your application protocol.
ASIO merely provides transport.
Now, indeed if you want to have a "keepalive" message you will have to design your protocol in such away that the client can distinguish the messages.
The trick is to think of it at a higher level. Don't deal with async_read on the client directly. Instead, make async_read put messages on a queue (or several queues; the status messages could not even go in a queue but supersede a previous non-handled status update, e.g.).
Then code your client against those queues.
A simple thing that is typically done is to introduce message framing and a message type id:
FRAME offset 0: message length(N)
FRAME offset 4: message data
FRAME offset 4+N: message checksum
FRAME offset 4+N+sizeof checksum: sentinel (e.g. 0x00, or a larger unique signature)
The structure there makes the protocol more extensible. It's easy to add encryption/compression without touch all other code. There's built-in error detection etc.