boost socket comms are not working past one exchange - c++

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.

Related

boost:asio::write writes to socket successfully, but server doesn't see the data

I've written a simple code sample that writes some data to the socket towards a simple TCP echo server. The data is written successfully to the socket (writtenBytes > 0), but the server doesn't respond that it has received the data.
The application is run in a Docker devcontainer, and from the development container, I'm communicating with the tcp-server-echo container on the same network.
io_service ioservice;
tcp::socket tcp_socket{ioservice};
void TestTcpConnection() {
boost::asio::ip::tcp::resolver nameResolver{ioservice};
boost::asio::ip::tcp::resolver::query query{"tcp-server-echo", "9000"};
boost::system::error_code ec{};
auto iterator = nameResolver.resolve(query, ec);
if (ec == 0) {
boost::asio::ip::tcp::resolver::iterator end{};
boost::asio::ip::tcp::endpoint endpoint = *iterator;
tcp_socket.connect(endpoint, ec);
if (ec == 0) {
std::string str{"Hello world test"};
while (tcp_socket.is_open()) {
auto writtenBytes =
boost::asio::write(tcp_socket, boost::asio::buffer(str));
if (writtenBytes > 0) {
// this line is executed successfully every time.
// writtenBytes == 13, which equals to str.length()
std::cout << "Bytes written successfully!\n";
}
using namespace std::chrono_literals;
std::this_thread::sleep_for(2000ms);
}
}
}
In this case writtenBytes > 0 is a sign of a successful write to the socket.
The echo server is based on istio/tcp-echo-server:1.2 image. I can ping it from my devcontainer by name or IP address with no issues. Also, when I write a similar code sample but using async functions (async_resolve, async_connect, except for the write operation, which is not async), and a separate thread to run ioservice, the server does see my data and responds appropriately.
Why the server doesn't see my data in case of no-async writes? Thanks in advance.
It turned out the issue was with the Docker container that received the message. The image istio/tcp-echo-server:1.2 doesn't write to logs unless you send the data with \n in the end.

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) {
...
}

Boost Socket goes belly-up on close()

We have a C++ application that talks to a server. It sends two messages to it, and the server responds to each message with another message. We're using Boost, but the Boost Socket--the entire application--barfs when we attempt to close the socket.
Here's the general idea of what we're doing:
encode the message (change it into a string)
open socket
send message
check the bytes sent
check the return message
shutdown & close the socket
Since we send two messages, we do it in a loop (just two iterations, obviously).
We know exactly where the error is since, if we remove that line, it works fine. It's on step 5. Unfortunately, that's kind of an important step. We can't find what we're doing wrong how to fix it.
Here's the code:
bool ReallyImportantService::sendMessages( int messageNum ) {
// ...some error-checking here...
bool successCode = false;
for( int i = 0; i < 2; ++i ) {
successCode = false;
unique_ptr<boost::asio::ip::tcp::socket> theSocket = connect();
if( theSocket == nullptr ) {
theLogger->error( "Could not create socket, could not send input messageNum to service" );
return successCode;
}
string message = encodeMessage( messageNum );
// send the message
boost::system::error_code error;
size_t bytesSent = boost::asio::write(*theSocket,
boost::asio::buffer(message),
boost::asio::transfer_all(), error);
// inspect the result
if( !messageNumSendSuccessful(message.length(), bytesSent) ) {
return successCode;
}
// Get the response message
string response;
boost::system::error_code e;
boost::asio::streambuf buffer;
// this is step #5 above, the line that kills it. But it responds with no errors
boost::asio::read_until(*theSocket, buffer, "\0", e);
if( e.value() == boost::system::errc::success ) {
istream str(&buffer);
getline(str, response);
// validate response
successCode = messageAckIsValid( response, messageNum );
}
else {
theLogger->error( "Got erroneous response from server when sending messageNum" );
}
// close it all up
boost::system::error_code eShut;
theSocket->shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, eShut);
// We never get an error code here, all clean
try {
boost::system::error_code ec;
// This is where it all goes belly-up. It doesn't throw an exception, doesn't return an
// error-code. Stepping through, we can see the call stack shows a Segmentation fault,
// but we don't know what could be causing this.
theSocket->close( ec );
}
catch(boost::system::system_error& se) {
theLogger->error( "sendMessages() barfed on close! " + string(se.what()) );
}
catch( ... ) {
theLogger->error( "sendMessages() barfed on close! " );
}
}
return successCode;
}
string ReallyImportantService::encodeMessage( int messageNum ) {
// Encode the message
stringstream ss;
ss << "^FINE=";
ss << to_string(messageNum) << "\n";
string message = ss.str();
theLogger->info( message );
return message;
}
unique_ptr<boost::asio::ip::tcp::socket> ReallyImportantService::connect() {
// Addresses from configuration
string address( server_ip );
string port( server_port );
// Resolve the IP address
boost::asio::io_service ioService;
boost::asio::ip::tcp::resolver resolver(ioService);
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::resolver::iterator ep_iterator = resolver.resolve(query);
// create the socket
unique_ptr<boost::asio::ip::tcp::socket> theSocket = make_unique<boost::asio::ip::tcp::socket>(ioService);
// not sure if this is necessary, but couldn't hurt; we do reuse the IP address the second time around
boost::system::error_code ec;
theSocket->set_option(boost::asio::socket_base::reuse_address(true), ec);
// Connect
try {
boost::asio::connect(*theSocket, ep_iterator);
} catch(const boost::system::system_error &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
} catch(const exception &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
}
return theSocket;
}
Here's the call stack we get when it errors-out:
(Suspended : Signal : SIGSEGV:Segmentation fault)
pthread_mutex_lock() at 0x7ffff7bc8c30
boost::asio::detail::posix_mutex::lock() at posix_mutex.hpp:52 0x969072
boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scoped_lock() at scoped_lock.hpp:36 0x980b66
boost::asio::detail::epoll_reactor::free_descriptor_state() at epoll_reactor.ipp:517 0x96c6fa
boost::asio::detail::epoll_reactor::deregister_descriptor() at epoll_reactor.ipp:338 0x96bccc
boost::asio::detail::reactive_socket_service_base::close() at reactive_socket_service_base.ipp:103 0xb920aa
boost::asio::stream_socket_service<boost::asio::ip::tcp>::close() at stream_socket_service.hpp:151 0xb975e0
boost::asio::basic_socket<boost::asio::ip::tcp, boost::asio::stream_socket_service<boost::asio::ip::tcp> >::close() at basic_socket.hpp:339 0xb94f0d
ReallyImportantService::sendMessages() at ReallyImportantService.cc:116 0xb8ce19
<...more frames...>
We created a minimal implementation that just:
Creates the socket
Shuts down the socket
Closes the socket
And it works perfectly. We put it in a loop and we can go for dozens of iterations without any problems.
We're using Eclipse CDT and gcc to compile.
Any idea what might be going on?
You've broken the cardinal rule.
An io_service must outlive all objects created on it.
Your connect() function creates an io_service, creates a socket on it and returns the socket (wrapped in a unique_ptr). Then the io_service is destroyed.
From that point forward, all bets are off because the socket will use a the socket service object associated with the io_service you just destroyed. This socket service is now just memory with undefined values in it. You're (un)lucky the program got this far before the segfault.
In general you will need one io_service per application. All objects that need it should carry a reference to it.
Your connect function then becomes:
bool connect(boost::asio::ip::tcp& theSocket) {
// Addresses from configuration
string address( server_ip );
string port( server_port );
// Resolve the IP address
boost::asio::ip::tcp::resolver resolver(theSocket.get_io_service());
boost::asio::ip::tcp::resolver::query query(address, port);
boost::asio::ip::tcp::resolver::iterator ep_iterator = resolver.resolve(query);
// not sure if this is necessary, but couldn't hurt; we do reuse the IP address the second time around
boost::system::error_code ec;
theSocket.set_option(boost::asio::socket_base::reuse_address(true), ec);
// Connect
try {
boost::asio::connect(theSocket, ep_iterator);
} catch(const boost::system::system_error &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
return false;
} catch(const exception &e){
theSocket = nullptr;
theLogger->error( "Exception while attempting to create socket: " + string(e.what()) );
return false;
}
return true;
}
bool sendMessages(boost::asio::io_service& ios, int messageNum)
{
boost::asio::ip::tcp::socket theSocket(ios);
auto ok = connect(theSocket);
// ... carry on ...
}
Prefer to hold references to sockets etc whenever possible. Wrapping them in a unique_ptr is a confusing extra layer of indirection.
As of c++11 and recent versions of boost, asio sockets are moveable. You can return them by value as opposed to passing in a reference as I have done.
I notice that you have a mixture of exception and non-exception error handling in the code. You probably want to stick to one or the other (in my view exception-based error handling is cleaner, but this is not a universal view).

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.

TCP IP Communication using C++

Im trying to make a TCP/IP client using boost::asio in C++. I have created function that creates a socket to connect to the server
void TCP_IP_Communication::Create_Socket()
{
...
_socket = new tcp::socket(_io); //create socket
_io.run();
boost::system::error_code error= boost::asio::error::host_not_found;
try
{
while (error && endpoint_iterator != end) //if error go to next endpoint
{
_socket->close();
_socket->connect(*endpoint_iterator++, error);
}
//if error throw error
if(error)
throw boost::system::system_error(error);
//else the router is connected
boost::asio::read_until(*_socket,buf,'\n');
}}
Then I use another function/thread to send a command and receive response.
try
{
if(p=='i')
_socket->send(boost::asio::buffer(" sspi l1\n\n")); //sending signal presence for input command
else
_socket->send(boost::asio::buffer(" sspo l1\n\n")); //sending signal presence for output command
for(; ;) //loop reading all values from router
{
//wait for reply??
boost::asio::read_until(*_socket,buf,'\n');
std::istream is(&buf);
std::getline(is,this->data);
std::cout<<std::endl<<this->data;
The problem is, each time I connect to the server it gives a response
? "login"
But I need to suppress it when I send a command. Actually it shouldn't show up as I'm not connecting to the server each time I send a command,but it does. What did I do wrong here? I just cant figure it out.
The main function is like this:
int main()
{
TCP_IP_Connection router;
router.Create_Socket();
boost::thread router_thread1,router_thread2;
router_thread1=boost::thread(&TCP_IP_Connection::get_status,&router,'i');
router_thread1.join();
std::string reply="\nend of main()";
std::cout<<reply;
return 0;
}