Discussion:
async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")
(too old to reply)
Sandra Schreiner
2016-02-04 18:01:33 UTC
Permalink
Dear asio-experts,

I need an advice on the following problem. After a fashion I managed to get this read and write in separate threads (almost) working. The first message is transmitted from client to server without problems. But if I try to send data again, the wrong data is received on server side. On client side everything is fine. Anyway in the session the header fetched by async_read contains random data, the length for the content is therefore wrong and the session get's stuck on the synchronous read afterwards.

It seems impossible that two writes are executed at the same time on client-side, because the send is synchronous, there are no other sending threads and the next send ist called when the result was received from the session.
I tried to figure out what is happening with wireshark, but because the connection is encrypted (SSL) I don't see anything usefull.
I can see that for every [PSH, ACK] and [ACK] there is a [TCP Retransmission] and [TCP Dup ACK]. But nothing is received twice on client/server side.

Any idea what might cause this problem? Any hints what I can do to figure it out by myself?

Maybe I'm doing something essentially wrong. So here is my source code:

Session
-----------------------------------------

void Session::read(){
mTmpBuffer = new char[HEADER_LENGTH];
asio::async_read(mSocket,
asio::buffer(mTmpBuffer, HEADER_LENGTH),
std::bind(&Session::readCallback, this,
std::placeholders::_1, std::placeholders::_2));
}

void Session::readCallback(const asio::error_code& headerError,
size_t bytes_transferred){
if(bytes_transferred != HEADER_LENGTH){
mLogger.log(AbstractLogger::Severity::Warning, "%s %s",
"Session::readCallback wrong size of data packet.", headerError.message().c_str());
closeSession();
}
else{
mReceiveHeader = std::string(mTmpBuffer, HEADER_LENGTH);
delete [] mTmpBuffer;
mTmpBuffer = nullptr;
if(!headerError && decodeHeader(mReceiveHeader, mReceiveMessageLength)){
mTmpBuffer = new char[mReceiveMessageLength];
asio::error_code contentError;
try{
size_t receivedContentLength = asio::read(mSocket, asio::buffer(mTmpBuffer, mReceiveMessageLength),
asio::transfer_all(), contentError);
if(contentError || mReceiveMessageLength != receivedContentLength){
closeSession();
}
else{
mReceiveMessage = std::string(mTmpBuffer, mReceiveMessageLength);
delete [] mTmpBuffer;
mTmpBuffer = nullptr;
std::string msg("thank you");
std::string response;
encodeHeader(msg, response);
write(response);
}
}catch(...){
mLogger.log(AbstractLogger::Severity::Debug, "%s %s",
"Session::readCallback","Something stange happened");
}

}
else{
closeSession();
}
}
}

Client (read thread ist startet after handshake)
----------------------------------
AbstractTLSConnector::ErrorCode Client::handshake(){
asio::error_code err;
mSocket->handshake(asio::ssl::stream_base::client, err);
if(!err){
mConnectionStatus = ConnectionStatus::OPEN;
mReadThread.reset(new std::thread{[this](){
mIoService.reset();
read();
mIoService.run(); }});
return ErrorCode::None;
}
else{
disconnect();
return ErrorCode::HandshakeFailed;
}
}

void Client::read(){
mTmpBuffer = new char[HEADER_LENGTH];
asio::async_read((*mSocket),
asio::buffer(mTmpBuffer, HEADER_LENGTH),
std::bind(&Client::readContent, this,
std::placeholders::_1, std::placeholders::_2));
}


void Client::readContent(const asio::error_code& error,
size_t bytes_transferred){
mReceiveHeader = std::string(mTmpBuffer, HEADER_LENGTH);
delete mTmpBuffer;
mTmpBuffer = nullptr;
if(!error && bytes_transferred == HEADER_LENGTH
&& decodeHeader(mReceiveHeader, mReceiveMessageLength)){
mTmpBuffer = new char[mReceiveMessageLength];
bytes_transferred = asio::read((*mSocket),
asio::buffer(mTmpBuffer, mReceiveMessageLength));
mReceiveMessage = std::string(mTmpBuffer, mReceiveMessageLength);
delete mTmpBuffer;
mTmpBuffer = nullptr;
if (!error && bytes_transferred == mReceiveMessageLength){
mListener.receivedResponse(mReceiveMessage, ErrorCode::None);
read();
}
else{
disconnect();
mListener.receivedResponse("", ErrorCode::ReadFailed);
}
}
else{
disconnect();
mListener.receivedResponse("", ErrorCode::ReadFailed);
}
}

AbstractTLSConnector::ErrorCode Client::send(const std::string& sendData){
if(mConnectionStatus == ConnectionStatus::OPEN){
encodeHeader(sendData, mSendMessage);
size_t dataLength = mSendMessage.size();
asio::error_code err;
size_t bytes_transferred = asio::write((*mSocket),
asio::buffer(mSendMessage, dataLength),
err);
if(!err && bytes_transferred == dataLength) {
return ErrorCode::None;
}
else {
disconnect();
return ErrorCode::WriteFailed;
}
}
else{
return ErrorCode::ConnectionAlreadyClosed;
}
}

Best regards,
Sandra
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Cliff Green
2016-02-05 19:46:37 UTC
Permalink
>> I need an advice on the following problem. After a fashion I managed to
>> get this read and write in separate threads (almost) working. The first
>> message is transmitted from client to server without problems. But if I
>> try to send data again, the wrong data is received on server side. On
>> client side everything is fine. Anyway in the session the header fetched
>> by async_read contains random data, the length for the content is
>> therefore wrong and the session get's stuck on the synchronous read
>> afterwards. >>

>From a fairly short read of your code, I didn't see anything obviously
wrong. So I can't provide any quick or simple fixes.

But some things to consider:

-- Does your "wire protocol" include binary lengths? E.g. does the header
include a "body length" that is a binary integer? If so, have you handled
the endian'ness of the binary data?

-- Consider simplifying your design. You mention a read and write thread,
and I notice that you've mixed async and sync calls. Since you've already
passed the hurdle (and syntax) of "thinking async", why not make all calls
async? At that point you can run everything in one thread (I'm simplifying a
bit, and you might need a wait queue or something similar for message
passing, but it really simplifies all of your networking code threading). I
never mix two threads on one socket (if I do thread pools or multiple
threads directly with Asio - which I typically _don't_ do, I never
multi-thread on the same socket) - while you might be able to get it working
fine on some operating systems, it might not on others; in any case, with
multiple threads _on one socket_ trying to keep track of which data is being
handled in which thread always hurts my head.

-- If I have an entity that is "always writing" and rarely (or never)
reading, I still _always_ have a read hanging. If there is never any data
coming in, the read handler is basically a "no-op". But there are many
advantages to having the "read hanging", one of which is that socket closes
and error conditions are handled quicker and cleaner (this is basic socket
programming - read up on any good socket programming books for more
details).

You can also simplify some of your buffer handling and reduce some buffer
copying, but those are minor items and likely won't make much, if any,
difference (other than simplifying your code and maybe slightly, barely,
improve a bit of performance). It's also unclear to me why you're creating a
new thread every time you perform a client handshake - why not have just one
thread running at all times, doing both the reads and writes? That saves the
relatively substantial overhead of constantly creating a new thread. You
would probably need some sort of threaded queue to pass data back and forth,
but there are many simple "wait queues" that will do that. (Maybe you have a
bigger framework constraint that requires you to create a new thread for
every client?)

Cliff




------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Sandra Schreiner
2016-02-06 08:08:12 UTC
Permalink
>>Does your "wire protocol" include binary lengths? E.g. does the header
>>include a "body length" that is a binary integer? If so, have you handled
>>the endian'ness of the binary data?

Yes it does. I use a method "encode header" which fetches the length of the data,
handles the endianness and adds a binary length 'string' in front of the data.

>> If I have an entity that is "always writing" and rarely (or never)
>> reading, I still _always_ have a read hanging. If there is never any data
>> coming in, the read handler is basically a "no-op".

The problem is i can't write with a 'hanging' read. See the test application I added below.
It only uses async operations and shows the mentioned behaviour. After the handshake
I start a async_read, but the write is not executed. It doesn't matter if I call io_service.reset()
and run() for the write or not. The last message shown is "Client Handshake success ". Why?

>> You would probably need some sort of threaded queue to pass data back and forth,
>> but there are many simple "wait queues" that will do that.

It was planned that this handling would be done by a 'Communicator' class. The threads
only purpose was to enable write with a read blocking as said above. I don't know why
it seems impossible to execute an async_write while async_read is hanging. I'm
really deseprate because of this problem. Would there be any difference if I use
boost::asio instead of asio standalone? After days of trying it feels like I should give up
on this matter. But it seems impossible that I'm the only or first one trying to implement
such a 'simple' task. Basically all I want is the following:

1) Client opens connection
2) As long as the application does not demand any send operation the client should wait
for shutdown request from server-side (e.g. if server application is closed)
3) Sometimes the client will send data and the session should respond. The connection
stays open.
4) At some point in time the client or server will send a shutdown and the connection
should be closed properly.

I don't know what I'm doing wrong all the time.

Best regards,
Sandra

Source Code:
--------------------------------------------------------------
#include <cstdlib>
#include <thread>
#include <iostream>
#include <chrono>
#include <memory>
#include "application/framework/impl/asio/asio.hpp"
#include "application/framework/impl/asio/asio/ssl.hpp"

typedef asio::ssl::stream<asio::ip::tcp::socket> ssl_socket;

class session{
public:

int connectionStatus; //0 = open, 1 = closing, 2 = closed

session(asio::io_service& io_service,
asio::ssl::context& context) : socket_(io_service, context) { }

ssl_socket::lowest_layer_type& socket(){
return socket_.lowest_layer();
}

void start(){
std::cout << " Session start "<< "\n";
connectionStatus = 1;
socket_.async_handshake(asio::ssl::stream_base::server,
std::bind(&session::handshakeCallback, this,
std::placeholders::_1));
}

void closeConnection(){
if(connectionStatus == 0){
std::cout << " Session shutdown "<< "\n";
try{
connectionStatus = 1;
socket_.shutdown();
}catch(...){
socket_.lowest_layer().close();
}
connectionStatus = 2;
delete this;
}
}

void handshakeCallback(const asio::error_code& error){
if (!error){
connectionStatus = 0;
std::cout << " Session handshake success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else {
std::cout << " Session handshake failed "<< error.message() << "\n";
closeConnection();
}
}

void readCallback(const asio::error_code& error,
size_t bytes_transferred) {
if (!error) {
std::cout << " Session Read success "<< "\n";
asio::async_write(socket_,
asio::buffer(data_, bytes_transferred),
std::bind(&session::writeCallback, this,
std::placeholders::_1));
}
else {
std::cout << " Session Read failed " << error.message() << "\n";
closeConnection();
}
}

void writeCallback(const asio::error_code& error){
if (!error){
std::cout << "Session Write success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else{
std::cout << "Session Write failed" << error.message() << std::endl;
closeConnection();
}
}

~session(){
std::cout << " Session Desctructor called\n";
closeConnection();
}

private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};

class server
{

private:
std::vector<std::shared_ptr<session>> sessions;
std::shared_ptr<std::thread> mAcceptThread;

public:
server(asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
context_(asio::ssl::context::sslv23) {
context_.set_options(
asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");

}

void start(){
mAcceptThread.reset(new std::thread {
[this](){
accept();
io_service_.run();
} });
}

std::string get_password() const{
return "test";
}

void accept(){
std::cout << " Server accept start "<< "\n";
std::shared_ptr<session> newSession(new session(io_service_, context_));
sessions.push_back(newSession);
acceptor_.async_accept(newSession->socket(),
std::bind(&server::acceptCallback, this, newSession,
std::placeholders::_1));
}

void acceptCallback(std::shared_ptr<session> newSession,
const asio::error_code& error){
if (!error){
std::cout << " Server Accept success "<< "\n";
newSession->start();
}
else
{
std::cout << " Server Accept failed "<< error.message() <<"\n";
newSession.reset();
}

accept();
}

void close(){
std::cout << "Server close enter\n";
for(auto &curSession: sessions){
curSession.reset();
}
sessions.clear();
std::cout << "Server close leave\n";
}

~server(){
if(mAcceptThread->joinable()){
mAcceptThread->join();
}
}

private:
asio::io_service& io_service_;
asio::ip::tcp::acceptor acceptor_;
asio::ssl::context context_;
};


enum { max_length = 1024 };

class client
{
public:
asio::io_service &mIoService;

client(asio::io_service& io_service,
asio::ssl::context& context,
asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context), mIoService(io_service), requestSize(4){
socket_.set_verify_mode(asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verifyCertificate, this, std::placeholders::_1, std::placeholders::_2));

asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
std::bind(&client::connectCallback, this,
std::placeholders::_1));
mIoService.run();
}

bool verifyCertificate(bool preverified,
asio::ssl::verify_context& ctx){
return true;
}

void connectCallback(const asio::error_code& error){

if (!error){
std::cout << "Client Connect success "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::client,
std::bind(&client::handshakeCallback, this,
std::placeholders::_1));
}
else {
std::cout << "Client Connect failed: " << error.message() << "\n";
closeConnection();
}
}

void handshakeCallback(const asio::error_code& error){
if (!error) {
std::cout << "Client Handshake success "<< "\n";
read();
}
else{
std::cout << "Client Handshake failed: " << error.message() << "\n";
closeConnection();
}
}


void send(std::string request_){
//mIoService.reset();
std::cout << " Client send data "<< "\n";
requestSize = request_.size();

asio::async_write(socket_,
asio::buffer(request_, requestSize),
std::bind(&client::writeCallback, this,
std::placeholders::_1,
std::placeholders::_2));
//mIoService.run();
}

void read(){
//length only hardcoded for test
asio::async_read(socket_,
asio::buffer(reply_, 4),
std::bind(&client::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}

void writeCallback(const asio::error_code& error, size_t bytes_transferred){

if (error){
std::cout << "Client Write failed: " << error.message() << "\n";
closeConnection();
}
else
{
std::cout << " Client Write success "<< "\n";
//read should be open
}
}

void readCallback(const asio::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Client Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
//start async_read again
read();
}
else {
std::cout << "Client Read failed: " << error.message() << "\n";
closeConnection();
}
}

void closeConnection(){
std::cout << "Client initiateDisconnect enter" << "\n";
try{
socket_.shutdown();
}catch(std::system_error &er){
std::cout << "Client close error" << er.what() << std::endl;
}
catch(...){
std::cout << "Client close undefined error" << std::endl;
}
std::cout << "Client close layer" << std::endl;
socket_.lowest_layer().close();
}

~client(){
closeConnection();
}

private:
asio::ssl::stream<asio::ip::tcp::socket> socket_;
size_t requestSize;
char request_[max_length];
char reply_[max_length];
};

using namespace std;

int main(){

asio::io_service server_service;
server s(server_service, 8877);
s.start();

std::this_thread::sleep_for(std::chrono::milliseconds(3000));
asio::io_service io_service;

asio::ip::tcp::resolver resolver(io_service);
asio::ip::tcp::resolver::query query("127.0.0.1", std::to_string(8877));
asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");

client c(io_service, ctx, iterator);

std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.send("test");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.closeConnection();
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
std::cout << "Main leave\n";
return 0;
}
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Roger Austin (Australia)
2016-02-07 21:30:08 UTC
Permalink
Both client::handshakeCallback and server::handshakeCallback issue async_read, but neither issues an async_write. So after the handshake both client and server are sitting there waiting for the other to say something.

Take a look at the asio chat and echo server examples (http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/examples/cpp11_examples.html) for an illustration of how this sort of thing is supposed to work.

-----Original Message-----
From: Sandra Schreiner [mailto:***@stud.hs-kl.de]
Sent: Saturday, 6 February 2016 7:08 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

>>Does your "wire protocol" include binary lengths? E.g. does the header
>>include a "body length" that is a binary integer? If so, have you
>>handled the endian'ness of the binary data?

Yes it does. I use a method "encode header" which fetches the length of the data, handles the endianness and adds a binary length 'string' in front of the data.

>> If I have an entity that is "always writing" and rarely (or never)
>> reading, I still _always_ have a read hanging. If there is never any
>> data coming in, the read handler is basically a "no-op".

The problem is i can't write with a 'hanging' read. See the test application I added below.
It only uses async operations and shows the mentioned behaviour. After the handshake I start a async_read, but the write is not executed. It doesn't matter if I call io_service.reset() and run() for the write or not. The last message shown is "Client Handshake success ". Why?

>> You would probably need some sort of threaded queue to pass data back
>> and forth, but there are many simple "wait queues" that will do that.

It was planned that this handling would be done by a 'Communicator' class. The threads only purpose was to enable write with a read blocking as said above. I don't know why it seems impossible to execute an async_write while async_read is hanging. I'm really deseprate because of this problem. Would there be any difference if I use boost::asio instead of asio standalone? After days of trying it feels like I should give up on this matter. But it seems impossible that I'm the only or first one trying to implement such a 'simple' task. Basically all I want is the following:

1) Client opens connection
2) As long as the application does not demand any send operation the client should wait for shutdown request from server-side (e.g. if server application is closed)
3) Sometimes the client will send data and the session should respond. The connection stays open.
4) At some point in time the client or server will send a shutdown and the connection
should be closed properly.

I don't know what I'm doing wrong all the time.

Best regards,
Sandra

Source Code:
--------------------------------------------------------------
#include <cstdlib>
#include <thread>
#include <iostream>
#include <chrono>
#include <memory>
#include "application/framework/impl/asio/asio.hpp"
#include "application/framework/impl/asio/asio/ssl.hpp"

typedef asio::ssl::stream<asio::ip::tcp::socket> ssl_socket;

class session{
public:

int connectionStatus; //0 = open, 1 = closing, 2 = closed

session(asio::io_service& io_service,
asio::ssl::context& context) : socket_(io_service, context) { }

ssl_socket::lowest_layer_type& socket(){
return socket_.lowest_layer();
}

void start(){
std::cout << " Session start "<< "\n";
connectionStatus = 1;
socket_.async_handshake(asio::ssl::stream_base::server,
std::bind(&session::handshakeCallback, this,
std::placeholders::_1));
}

void closeConnection(){
if(connectionStatus == 0){
std::cout << " Session shutdown "<< "\n";
try{
connectionStatus = 1;
socket_.shutdown();
}catch(...){
socket_.lowest_layer().close();
}
connectionStatus = 2;
delete this;
}
}

void handshakeCallback(const asio::error_code& error){
if (!error){
connectionStatus = 0;
std::cout << " Session handshake success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else {
std::cout << " Session handshake failed "<< error.message() << "\n";
closeConnection();
}
}

void readCallback(const asio::error_code& error,
size_t bytes_transferred) {
if (!error) {
std::cout << " Session Read success "<< "\n";
asio::async_write(socket_,
asio::buffer(data_, bytes_transferred),
std::bind(&session::writeCallback, this,
std::placeholders::_1));
}
else {
std::cout << " Session Read failed " << error.message() << "\n";
closeConnection();
}
}

void writeCallback(const asio::error_code& error){
if (!error){
std::cout << "Session Write success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else{
std::cout << "Session Write failed" << error.message() << std::endl;
closeConnection();
}
}

~session(){
std::cout << " Session Desctructor called\n";
closeConnection();
}

private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};

class server
{

private:
std::vector<std::shared_ptr<session>> sessions;
std::shared_ptr<std::thread> mAcceptThread;

public:
server(asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
context_(asio::ssl::context::sslv23) {
context_.set_options(
asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");

}

void start(){
mAcceptThread.reset(new std::thread {
[this](){
accept();
io_service_.run();
} });
}

std::string get_password() const{
return "test";
}

void accept(){
std::cout << " Server accept start "<< "\n";
std::shared_ptr<session> newSession(new session(io_service_, context_));
sessions.push_back(newSession);
acceptor_.async_accept(newSession->socket(),
std::bind(&server::acceptCallback, this, newSession,
std::placeholders::_1));
}

void acceptCallback(std::shared_ptr<session> newSession,
const asio::error_code& error){
if (!error){
std::cout << " Server Accept success "<< "\n";
newSession->start();
}
else
{
std::cout << " Server Accept failed "<< error.message() <<"\n";
newSession.reset();
}

accept();
}

void close(){
std::cout << "Server close enter\n";
for(auto &curSession: sessions){
curSession.reset();
}
sessions.clear();
std::cout << "Server close leave\n";
}

~server(){
if(mAcceptThread->joinable()){
mAcceptThread->join();
}
}

private:
asio::io_service& io_service_;
asio::ip::tcp::acceptor acceptor_;
asio::ssl::context context_;
};


enum { max_length = 1024 };

class client
{
public:
asio::io_service &mIoService;

client(asio::io_service& io_service,
asio::ssl::context& context,
asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context), mIoService(io_service), requestSize(4){
socket_.set_verify_mode(asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verifyCertificate, this, std::placeholders::_1, std::placeholders::_2));

asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
std::bind(&client::connectCallback, this,
std::placeholders::_1));
mIoService.run();
}

bool verifyCertificate(bool preverified,
asio::ssl::verify_context& ctx){
return true;
}

void connectCallback(const asio::error_code& error){

if (!error){
std::cout << "Client Connect success "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::client,
std::bind(&client::handshakeCallback, this,
std::placeholders::_1));
}
else {
std::cout << "Client Connect failed: " << error.message() << "\n";
closeConnection();
}
}

void handshakeCallback(const asio::error_code& error){
if (!error) {
std::cout << "Client Handshake success "<< "\n";
read();
}
else{
std::cout << "Client Handshake failed: " << error.message() << "\n";
closeConnection();
}
}


void send(std::string request_){
//mIoService.reset();
std::cout << " Client send data "<< "\n";
requestSize = request_.size();

asio::async_write(socket_,
asio::buffer(request_, requestSize),
std::bind(&client::writeCallback, this,
std::placeholders::_1,
std::placeholders::_2));
//mIoService.run();
}

void read(){
//length only hardcoded for test
asio::async_read(socket_,
asio::buffer(reply_, 4),
std::bind(&client::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}

void writeCallback(const asio::error_code& error, size_t bytes_transferred){

if (error){
std::cout << "Client Write failed: " << error.message() << "\n";
closeConnection();
}
else
{
std::cout << " Client Write success "<< "\n";
//read should be open
}
}

void readCallback(const asio::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Client Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
//start async_read again
read();
}
else {
std::cout << "Client Read failed: " << error.message() << "\n";
closeConnection();
}
}

void closeConnection(){
std::cout << "Client initiateDisconnect enter" << "\n";
try{
socket_.shutdown();
}catch(std::system_error &er){
std::cout << "Client close error" << er.what() << std::endl;
}
catch(...){
std::cout << "Client close undefined error" << std::endl;
}
std::cout << "Client close layer" << std::endl;
socket_.lowest_layer().close();
}

~client(){
closeConnection();
}

private:
asio::ssl::stream<asio::ip::tcp::socket> socket_;
size_t requestSize;
char request_[max_length];
char reply_[max_length];
};

using namespace std;

int main(){

asio::io_service server_service;
server s(server_service, 8877);
s.start();

std::this_thread::sleep_for(std::chrono::milliseconds(3000));
asio::io_service io_service;

asio::ip::tcp::resolver resolver(io_service);
asio::ip::tcp::resolver::query query("127.0.0.1", std::to_string(8877));
asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");

client c(io_service, ctx, iterator);

std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.send("test");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.closeConnection();
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
std::cout << "Main leave\n";
return 0;
}
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Sandra Schreiner
2016-02-08 08:00:06 UTC
Permalink
Hello Roger,

Many thanks for your answer. You wrote:

>>Both client::handshakeCallback and server::handshakeCallback issue async_read, but
>>neither issues an async_write.

But if you take a look at the main method you can see I'm calling

c.send()

that will initiate a aync_write from client side. The cout in this method is however never
reached. Why is it never called if it's not the async_read that is 'blocking' the code?

Best regards,
Sandra
________________________________________
Von: Roger Austin (Australia) [***@innovyze.com]
Gesendet: Sonntag, 7. Februar 2016 22:30
An: asio-***@lists.sourceforge.net
Betreff: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

Both client::handshakeCallback and server::handshakeCallback issue async_read, but neither issues an async_write. So after the handshake both client and server are sitting there waiting for the other to say something.

Take a look at the asio chat and echo server examples (http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/examples/cpp11_examples.html) for an illustration of how this sort of thing is supposed to work.

-----Original Message-----
From: Sandra Schreiner [mailto:***@stud.hs-kl.de]
Sent: Saturday, 6 February 2016 7:08 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

>>Does your "wire protocol" include binary lengths? E.g. does the header
>>include a "body length" that is a binary integer? If so, have you
>>handled the endian'ness of the binary data?

Yes it does. I use a method "encode header" which fetches the length of the data, handles the endianness and adds a binary length 'string' in front of the data.

>> If I have an entity that is "always writing" and rarely (or never)
>> reading, I still _always_ have a read hanging. If there is never any
>> data coming in, the read handler is basically a "no-op".

The problem is i can't write with a 'hanging' read. See the test application I added below.
It only uses async operations and shows the mentioned behaviour. After the handshake I start a async_read, but the write is not executed. It doesn't matter if I call io_service.reset() and run() for the write or not. The last message shown is "Client Handshake success ". Why?

>> You would probably need some sort of threaded queue to pass data back
>> and forth, but there are many simple "wait queues" that will do that.

It was planned that this handling would be done by a 'Communicator' class. The threads only purpose was to enable write with a read blocking as said above. I don't know why it seems impossible to execute an async_write while async_read is hanging. I'm really deseprate because of this problem. Would there be any difference if I use boost::asio instead of asio standalone? After days of trying it feels like I should give up on this matter. But it seems impossible that I'm the only or first one trying to implement such a 'simple' task. Basically all I want is the following:

1) Client opens connection
2) As long as the application does not demand any send operation the client should wait for shutdown request from server-side (e.g. if server application is closed)
3) Sometimes the client will send data and the session should respond. The connection stays open.
4) At some point in time the client or server will send a shutdown and the connection
should be closed properly.

I don't know what I'm doing wrong all the time.

Best regards,
Sandra

Source Code:
--------------------------------------------------------------
#include <cstdlib>
#include <thread>
#include <iostream>
#include <chrono>
#include <memory>
#include "application/framework/impl/asio/asio.hpp"
#include "application/framework/impl/asio/asio/ssl.hpp"

typedef asio::ssl::stream<asio::ip::tcp::socket> ssl_socket;

class session{
public:

int connectionStatus; //0 = open, 1 = closing, 2 = closed

session(asio::io_service& io_service,
asio::ssl::context& context) : socket_(io_service, context) { }

ssl_socket::lowest_layer_type& socket(){
return socket_.lowest_layer();
}

void start(){
std::cout << " Session start "<< "\n";
connectionStatus = 1;
socket_.async_handshake(asio::ssl::stream_base::server,
std::bind(&session::handshakeCallback, this,
std::placeholders::_1));
}

void closeConnection(){
if(connectionStatus == 0){
std::cout << " Session shutdown "<< "\n";
try{
connectionStatus = 1;
socket_.shutdown();
}catch(...){
socket_.lowest_layer().close();
}
connectionStatus = 2;
delete this;
}
}

void handshakeCallback(const asio::error_code& error){
if (!error){
connectionStatus = 0;
std::cout << " Session handshake success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else {
std::cout << " Session handshake failed "<< error.message() << "\n";
closeConnection();
}
}

void readCallback(const asio::error_code& error,
size_t bytes_transferred) {
if (!error) {
std::cout << " Session Read success "<< "\n";
asio::async_write(socket_,
asio::buffer(data_, bytes_transferred),
std::bind(&session::writeCallback, this,
std::placeholders::_1));
}
else {
std::cout << " Session Read failed " << error.message() << "\n";
closeConnection();
}
}

void writeCallback(const asio::error_code& error){
if (!error){
std::cout << "Session Write success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else{
std::cout << "Session Write failed" << error.message() << std::endl;
closeConnection();
}
}

~session(){
std::cout << " Session Desctructor called\n";
closeConnection();
}

private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};

class server
{

private:
std::vector<std::shared_ptr<session>> sessions;
std::shared_ptr<std::thread> mAcceptThread;

public:
server(asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
context_(asio::ssl::context::sslv23) {
context_.set_options(
asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");

}

void start(){
mAcceptThread.reset(new std::thread {
[this](){
accept();
io_service_.run();
} });
}

std::string get_password() const{
return "test";
}

void accept(){
std::cout << " Server accept start "<< "\n";
std::shared_ptr<session> newSession(new session(io_service_, context_));
sessions.push_back(newSession);
acceptor_.async_accept(newSession->socket(),
std::bind(&server::acceptCallback, this, newSession,
std::placeholders::_1));
}

void acceptCallback(std::shared_ptr<session> newSession,
const asio::error_code& error){
if (!error){
std::cout << " Server Accept success "<< "\n";
newSession->start();
}
else
{
std::cout << " Server Accept failed "<< error.message() <<"\n";
newSession.reset();
}

accept();
}

void close(){
std::cout << "Server close enter\n";
for(auto &curSession: sessions){
curSession.reset();
}
sessions.clear();
std::cout << "Server close leave\n";
}

~server(){
if(mAcceptThread->joinable()){
mAcceptThread->join();
}
}

private:
asio::io_service& io_service_;
asio::ip::tcp::acceptor acceptor_;
asio::ssl::context context_;
};


enum { max_length = 1024 };

class client
{
public:
asio::io_service &mIoService;

client(asio::io_service& io_service,
asio::ssl::context& context,
asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context), mIoService(io_service), requestSize(4){
socket_.set_verify_mode(asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verifyCertificate, this, std::placeholders::_1, std::placeholders::_2));

asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
std::bind(&client::connectCallback, this,
std::placeholders::_1));
mIoService.run();
}

bool verifyCertificate(bool preverified,
asio::ssl::verify_context& ctx){
return true;
}

void connectCallback(const asio::error_code& error){

if (!error){
std::cout << "Client Connect success "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::client,
std::bind(&client::handshakeCallback, this,
std::placeholders::_1));
}
else {
std::cout << "Client Connect failed: " << error.message() << "\n";
closeConnection();
}
}

void handshakeCallback(const asio::error_code& error){
if (!error) {
std::cout << "Client Handshake success "<< "\n";
read();
}
else{
std::cout << "Client Handshake failed: " << error.message() << "\n";
closeConnection();
}
}


void send(std::string request_){
//mIoService.reset();
std::cout << " Client send data "<< "\n";
requestSize = request_.size();

asio::async_write(socket_,
asio::buffer(request_, requestSize),
std::bind(&client::writeCallback, this,
std::placeholders::_1,
std::placeholders::_2));
//mIoService.run();
}

void read(){
//length only hardcoded for test
asio::async_read(socket_,
asio::buffer(reply_, 4),
std::bind(&client::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}

void writeCallback(const asio::error_code& error, size_t bytes_transferred){

if (error){
std::cout << "Client Write failed: " << error.message() << "\n";
closeConnection();
}
else
{
std::cout << " Client Write success "<< "\n";
//read should be open
}
}

void readCallback(const asio::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Client Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
//start async_read again
read();
}
else {
std::cout << "Client Read failed: " << error.message() << "\n";
closeConnection();
}
}

void closeConnection(){
std::cout << "Client initiateDisconnect enter" << "\n";
try{
socket_.shutdown();
}catch(std::system_error &er){
std::cout << "Client close error" << er.what() << std::endl;
}
catch(...){
std::cout << "Client close undefined error" << std::endl;
}
std::cout << "Client close layer" << std::endl;
socket_.lowest_layer().close();
}

~client(){
closeConnection();
}

private:
asio::ssl::stream<asio::ip::tcp::socket> socket_;
size_t requestSize;
char request_[max_length];
char reply_[max_length];
};

using namespace std;

int main(){

asio::io_service server_service;
server s(server_service, 8877);
s.start();

std::this_thread::sleep_for(std::chrono::milliseconds(3000));
asio::io_service io_service;

asio::ip::tcp::resolver resolver(io_service);
asio::ip::tcp::resolver::query query("127.0.0.1", std::to_string(8877));
asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");

client c(io_service, ctx, iterator);

std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.send("test");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.closeConnection();
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
std::cout << "Main leave\n";
return 0;
}
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Roger Austin (Australia)
2016-02-08 09:31:14 UTC
Permalink
Hi Sandra,

Sorry, I missed that.

The problem is that you call mIoService.run() in the client constructor. So the client service is running in the main thread. So your main thread blocks before it gets to c.send.

You could use a separate thread for the client, like you have with the server. But I would strongly recommend looking at the examples.

Cheers,
Roger

-----Original Message-----
From: Sandra Schreiner [mailto:***@stud.hs-kl.de]
Sent: Monday, 8 February 2016 7:00 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

Hello Roger,

Many thanks for your answer. You wrote:

>>Both client::handshakeCallback and server::handshakeCallback issue
>>async_read, but neither issues an async_write.

But if you take a look at the main method you can see I'm calling

c.send()

that will initiate a aync_write from client side. The cout in this method is however never reached. Why is it never called if it's not the async_read that is 'blocking' the code?

Best regards,
Sandra
________________________________________
Von: Roger Austin (Australia) [***@innovyze.com]
Gesendet: Sonntag, 7. Februar 2016 22:30
An: asio-***@lists.sourceforge.net
Betreff: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

Both client::handshakeCallback and server::handshakeCallback issue async_read, but neither issues an async_write. So after the handshake both client and server are sitting there waiting for the other to say something.

Take a look at the asio chat and echo server examples (http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/examples/cpp11_examples.html) for an illustration of how this sort of thing is supposed to work.

-----Original Message-----
From: Sandra Schreiner [mailto:***@stud.hs-kl.de]
Sent: Saturday, 6 February 2016 7:08 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

>>Does your "wire protocol" include binary lengths? E.g. does the header
>>include a "body length" that is a binary integer? If so, have you
>>handled the endian'ness of the binary data?

Yes it does. I use a method "encode header" which fetches the length of the data, handles the endianness and adds a binary length 'string' in front of the data.

>> If I have an entity that is "always writing" and rarely (or never)
>> reading, I still _always_ have a read hanging. If there is never any
>> data coming in, the read handler is basically a "no-op".

The problem is i can't write with a 'hanging' read. See the test application I added below.
It only uses async operations and shows the mentioned behaviour. After the handshake I start a async_read, but the write is not executed. It doesn't matter if I call io_service.reset() and run() for the write or not. The last message shown is "Client Handshake success ". Why?

>> You would probably need some sort of threaded queue to pass data back
>> and forth, but there are many simple "wait queues" that will do that.

It was planned that this handling would be done by a 'Communicator' class. The threads only purpose was to enable write with a read blocking as said above. I don't know why it seems impossible to execute an async_write while async_read is hanging. I'm really deseprate because of this problem. Would there be any difference if I use boost::asio instead of asio standalone? After days of trying it feels like I should give up on this matter. But it seems impossible that I'm the only or first one trying to implement such a 'simple' task. Basically all I want is the following:

1) Client opens connection
2) As long as the application does not demand any send operation the client should wait for shutdown request from server-side (e.g. if server application is closed)
3) Sometimes the client will send data and the session should respond. The connection stays open.
4) At some point in time the client or server will send a shutdown and the connection
should be closed properly.

I don't know what I'm doing wrong all the time.

Best regards,
Sandra

Source Code:
--------------------------------------------------------------
#include <cstdlib>
#include <thread>
#include <iostream>
#include <chrono>
#include <memory>
#include "application/framework/impl/asio/asio.hpp"
#include "application/framework/impl/asio/asio/ssl.hpp"

typedef asio::ssl::stream<asio::ip::tcp::socket> ssl_socket;

class session{
public:

int connectionStatus; //0 = open, 1 = closing, 2 = closed

session(asio::io_service& io_service,
asio::ssl::context& context) : socket_(io_service, context) { }

ssl_socket::lowest_layer_type& socket(){
return socket_.lowest_layer();
}

void start(){
std::cout << " Session start "<< "\n";
connectionStatus = 1;
socket_.async_handshake(asio::ssl::stream_base::server,
std::bind(&session::handshakeCallback, this,
std::placeholders::_1));
}

void closeConnection(){
if(connectionStatus == 0){
std::cout << " Session shutdown "<< "\n";
try{
connectionStatus = 1;
socket_.shutdown();
}catch(...){
socket_.lowest_layer().close();
}
connectionStatus = 2;
delete this;
}
}

void handshakeCallback(const asio::error_code& error){
if (!error){
connectionStatus = 0;
std::cout << " Session handshake success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else {
std::cout << " Session handshake failed "<< error.message() << "\n";
closeConnection();
}
}

void readCallback(const asio::error_code& error,
size_t bytes_transferred) {
if (!error) {
std::cout << " Session Read success "<< "\n";
asio::async_write(socket_,
asio::buffer(data_, bytes_transferred),
std::bind(&session::writeCallback, this,
std::placeholders::_1));
}
else {
std::cout << " Session Read failed " << error.message() << "\n";
closeConnection();
}
}

void writeCallback(const asio::error_code& error){
if (!error){
std::cout << "Session Write success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else{
std::cout << "Session Write failed" << error.message() << std::endl;
closeConnection();
}
}

~session(){
std::cout << " Session Desctructor called\n";
closeConnection();
}

private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};

class server
{

private:
std::vector<std::shared_ptr<session>> sessions;
std::shared_ptr<std::thread> mAcceptThread;

public:
server(asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
context_(asio::ssl::context::sslv23) {
context_.set_options(
asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");

}

void start(){
mAcceptThread.reset(new std::thread {
[this](){
accept();
io_service_.run();
} });
}

std::string get_password() const{
return "test";
}

void accept(){
std::cout << " Server accept start "<< "\n";
std::shared_ptr<session> newSession(new session(io_service_, context_));
sessions.push_back(newSession);
acceptor_.async_accept(newSession->socket(),
std::bind(&server::acceptCallback, this, newSession,
std::placeholders::_1));
}

void acceptCallback(std::shared_ptr<session> newSession,
const asio::error_code& error){
if (!error){
std::cout << " Server Accept success "<< "\n";
newSession->start();
}
else
{
std::cout << " Server Accept failed "<< error.message() <<"\n";
newSession.reset();
}

accept();
}

void close(){
std::cout << "Server close enter\n";
for(auto &curSession: sessions){
curSession.reset();
}
sessions.clear();
std::cout << "Server close leave\n";
}

~server(){
if(mAcceptThread->joinable()){
mAcceptThread->join();
}
}

private:
asio::io_service& io_service_;
asio::ip::tcp::acceptor acceptor_;
asio::ssl::context context_;
};


enum { max_length = 1024 };

class client
{
public:
asio::io_service &mIoService;

client(asio::io_service& io_service,
asio::ssl::context& context,
asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context), mIoService(io_service), requestSize(4){
socket_.set_verify_mode(asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verifyCertificate, this, std::placeholders::_1, std::placeholders::_2));

asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
std::bind(&client::connectCallback, this,
std::placeholders::_1));
mIoService.run();
}

bool verifyCertificate(bool preverified,
asio::ssl::verify_context& ctx){
return true;
}

void connectCallback(const asio::error_code& error){

if (!error){
std::cout << "Client Connect success "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::client,
std::bind(&client::handshakeCallback, this,
std::placeholders::_1));
}
else {
std::cout << "Client Connect failed: " << error.message() << "\n";
closeConnection();
}
}

void handshakeCallback(const asio::error_code& error){
if (!error) {
std::cout << "Client Handshake success "<< "\n";
read();
}
else{
std::cout << "Client Handshake failed: " << error.message() << "\n";
closeConnection();
}
}


void send(std::string request_){
//mIoService.reset();
std::cout << " Client send data "<< "\n";
requestSize = request_.size();

asio::async_write(socket_,
asio::buffer(request_, requestSize),
std::bind(&client::writeCallback, this,
std::placeholders::_1,
std::placeholders::_2));
//mIoService.run();
}

void read(){
//length only hardcoded for test
asio::async_read(socket_,
asio::buffer(reply_, 4),
std::bind(&client::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}

void writeCallback(const asio::error_code& error, size_t bytes_transferred){

if (error){
std::cout << "Client Write failed: " << error.message() << "\n";
closeConnection();
}
else
{
std::cout << " Client Write success "<< "\n";
//read should be open
}
}

void readCallback(const asio::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Client Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
//start async_read again
read();
}
else {
std::cout << "Client Read failed: " << error.message() << "\n";
closeConnection();
}
}

void closeConnection(){
std::cout << "Client initiateDisconnect enter" << "\n";
try{
socket_.shutdown();
}catch(std::system_error &er){
std::cout << "Client close error" << er.what() << std::endl;
}
catch(...){
std::cout << "Client close undefined error" << std::endl;
}
std::cout << "Client close layer" << std::endl;
socket_.lowest_layer().close();
}

~client(){
closeConnection();
}

private:
asio::ssl::stream<asio::ip::tcp::socket> socket_;
size_t requestSize;
char request_[max_length];
char reply_[max_length];
};

using namespace std;

int main(){

asio::io_service server_service;
server s(server_service, 8877);
s.start();

std::this_thread::sleep_for(std::chrono::milliseconds(3000));
asio::io_service io_service;

asio::ip::tcp::resolver resolver(io_service);
asio::ip::tcp::resolver::query query("127.0.0.1", std::to_string(8877));
asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");

client c(io_service, ctx, iterator);

std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.send("test");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.closeConnection();
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
std::cout << "Main leave\n";
return 0;
}
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Sandra Schreiner
2016-02-08 10:15:34 UTC
Permalink
Hello Roger,

>> But I would strongly recommend looking at the examples.

Already looking into them. Needed a bit time to wrap my mind around the deliver process of the chat example but I think I got the basic idea in the meanwhile. I will write again if any problems arise.
Many thanks again.

Best regards,

Sandra

________________________________________
Von: Roger Austin (Australia) [***@innovyze.com]
Gesendet: Montag, 8. Februar 2016 10:31
An: asio-***@lists.sourceforge.net
Betreff: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

Hi Sandra,

Sorry, I missed that.

The problem is that you call mIoService.run() in the client constructor. So the client service is running in the main thread. So your main thread blocks before it gets to c.send.

You could use a separate thread for the client, like you have with the server. But I would strongly recommend looking at the examples.

Cheers,
Roger

-----Original Message-----
From: Sandra Schreiner [mailto:***@stud.hs-kl.de]
Sent: Monday, 8 February 2016 7:00 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

Hello Roger,

Many thanks for your answer. You wrote:

>>Both client::handshakeCallback and server::handshakeCallback issue
>>async_read, but neither issues an async_write.

But if you take a look at the main method you can see I'm calling

c.send()

that will initiate a aync_write from client side. The cout in this method is however never reached. Why is it never called if it's not the async_read that is 'blocking' the code?

Best regards,
Sandra
________________________________________
Von: Roger Austin (Australia) [***@innovyze.com]
Gesendet: Sonntag, 7. Februar 2016 22:30
An: asio-***@lists.sourceforge.net
Betreff: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

Both client::handshakeCallback and server::handshakeCallback issue async_read, but neither issues an async_write. So after the handshake both client and server are sitting there waiting for the other to say something.

Take a look at the asio chat and echo server examples (http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/examples/cpp11_examples.html) for an illustration of how this sort of thing is supposed to work.

-----Original Message-----
From: Sandra Schreiner [mailto:***@stud.hs-kl.de]
Sent: Saturday, 6 February 2016 7:08 PM
To: asio-***@lists.sourceforge.net
Subject: Re: [asio-users] async_read in session fetches wrong data. (Following up to "Proper close of ASIO TLS connection and reuse afterwards")

>>Does your "wire protocol" include binary lengths? E.g. does the header
>>include a "body length" that is a binary integer? If so, have you
>>handled the endian'ness of the binary data?

Yes it does. I use a method "encode header" which fetches the length of the data, handles the endianness and adds a binary length 'string' in front of the data.

>> If I have an entity that is "always writing" and rarely (or never)
>> reading, I still _always_ have a read hanging. If there is never any
>> data coming in, the read handler is basically a "no-op".

The problem is i can't write with a 'hanging' read. See the test application I added below.
It only uses async operations and shows the mentioned behaviour. After the handshake I start a async_read, but the write is not executed. It doesn't matter if I call io_service.reset() and run() for the write or not. The last message shown is "Client Handshake success ". Why?

>> You would probably need some sort of threaded queue to pass data back
>> and forth, but there are many simple "wait queues" that will do that.

It was planned that this handling would be done by a 'Communicator' class. The threads only purpose was to enable write with a read blocking as said above. I don't know why it seems impossible to execute an async_write while async_read is hanging. I'm really deseprate because of this problem. Would there be any difference if I use boost::asio instead of asio standalone? After days of trying it feels like I should give up on this matter. But it seems impossible that I'm the only or first one trying to implement such a 'simple' task. Basically all I want is the following:

1) Client opens connection
2) As long as the application does not demand any send operation the client should wait for shutdown request from server-side (e.g. if server application is closed)
3) Sometimes the client will send data and the session should respond. The connection stays open.
4) At some point in time the client or server will send a shutdown and the connection
should be closed properly.

I don't know what I'm doing wrong all the time.

Best regards,
Sandra

Source Code:
--------------------------------------------------------------
#include <cstdlib>
#include <thread>
#include <iostream>
#include <chrono>
#include <memory>
#include "application/framework/impl/asio/asio.hpp"
#include "application/framework/impl/asio/asio/ssl.hpp"

typedef asio::ssl::stream<asio::ip::tcp::socket> ssl_socket;

class session{
public:

int connectionStatus; //0 = open, 1 = closing, 2 = closed

session(asio::io_service& io_service,
asio::ssl::context& context) : socket_(io_service, context) { }

ssl_socket::lowest_layer_type& socket(){
return socket_.lowest_layer();
}

void start(){
std::cout << " Session start "<< "\n";
connectionStatus = 1;
socket_.async_handshake(asio::ssl::stream_base::server,
std::bind(&session::handshakeCallback, this,
std::placeholders::_1));
}

void closeConnection(){
if(connectionStatus == 0){
std::cout << " Session shutdown "<< "\n";
try{
connectionStatus = 1;
socket_.shutdown();
}catch(...){
socket_.lowest_layer().close();
}
connectionStatus = 2;
delete this;
}
}

void handshakeCallback(const asio::error_code& error){
if (!error){
connectionStatus = 0;
std::cout << " Session handshake success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else {
std::cout << " Session handshake failed "<< error.message() << "\n";
closeConnection();
}
}

void readCallback(const asio::error_code& error,
size_t bytes_transferred) {
if (!error) {
std::cout << " Session Read success "<< "\n";
asio::async_write(socket_,
asio::buffer(data_, bytes_transferred),
std::bind(&session::writeCallback, this,
std::placeholders::_1));
}
else {
std::cout << " Session Read failed " << error.message() << "\n";
closeConnection();
}
}

void writeCallback(const asio::error_code& error){
if (!error){
std::cout << "Session Write success "<< "\n";
socket_.async_read_some(asio::buffer(data_, max_length),
std::bind(&session::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}
else{
std::cout << "Session Write failed" << error.message() << std::endl;
closeConnection();
}
}

~session(){
std::cout << " Session Desctructor called\n";
closeConnection();
}

private:
ssl_socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};

class server
{

private:
std::vector<std::shared_ptr<session>> sessions;
std::shared_ptr<std::thread> mAcceptThread;

public:
server(asio::io_service& io_service, unsigned short port)
: io_service_(io_service),
acceptor_(io_service,
asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
context_(asio::ssl::context::sslv23) {
context_.set_options(
asio::ssl::context::default_workarounds
| asio::ssl::context::no_sslv2
| asio::ssl::context::single_dh_use);
context_.set_password_callback(std::bind(&server::get_password, this));
context_.use_certificate_chain_file("server.pem");
context_.use_private_key_file("server.pem", asio::ssl::context::pem);
context_.use_tmp_dh_file("dh1024.pem");

}

void start(){
mAcceptThread.reset(new std::thread {
[this](){
accept();
io_service_.run();
} });
}

std::string get_password() const{
return "test";
}

void accept(){
std::cout << " Server accept start "<< "\n";
std::shared_ptr<session> newSession(new session(io_service_, context_));
sessions.push_back(newSession);
acceptor_.async_accept(newSession->socket(),
std::bind(&server::acceptCallback, this, newSession,
std::placeholders::_1));
}

void acceptCallback(std::shared_ptr<session> newSession,
const asio::error_code& error){
if (!error){
std::cout << " Server Accept success "<< "\n";
newSession->start();
}
else
{
std::cout << " Server Accept failed "<< error.message() <<"\n";
newSession.reset();
}

accept();
}

void close(){
std::cout << "Server close enter\n";
for(auto &curSession: sessions){
curSession.reset();
}
sessions.clear();
std::cout << "Server close leave\n";
}

~server(){
if(mAcceptThread->joinable()){
mAcceptThread->join();
}
}

private:
asio::io_service& io_service_;
asio::ip::tcp::acceptor acceptor_;
asio::ssl::context context_;
};


enum { max_length = 1024 };

class client
{
public:
asio::io_service &mIoService;

client(asio::io_service& io_service,
asio::ssl::context& context,
asio::ip::tcp::resolver::iterator endpoint_iterator)
: socket_(io_service, context), mIoService(io_service), requestSize(4){
socket_.set_verify_mode(asio::ssl::verify_peer);
socket_.set_verify_callback(
std::bind(&client::verifyCertificate, this, std::placeholders::_1, std::placeholders::_2));

asio::async_connect(socket_.lowest_layer(), endpoint_iterator,
std::bind(&client::connectCallback, this,
std::placeholders::_1));
mIoService.run();
}

bool verifyCertificate(bool preverified,
asio::ssl::verify_context& ctx){
return true;
}

void connectCallback(const asio::error_code& error){

if (!error){
std::cout << "Client Connect success "<< "\n";
socket_.async_handshake(asio::ssl::stream_base::client,
std::bind(&client::handshakeCallback, this,
std::placeholders::_1));
}
else {
std::cout << "Client Connect failed: " << error.message() << "\n";
closeConnection();
}
}

void handshakeCallback(const asio::error_code& error){
if (!error) {
std::cout << "Client Handshake success "<< "\n";
read();
}
else{
std::cout << "Client Handshake failed: " << error.message() << "\n";
closeConnection();
}
}


void send(std::string request_){
//mIoService.reset();
std::cout << " Client send data "<< "\n";
requestSize = request_.size();

asio::async_write(socket_,
asio::buffer(request_, requestSize),
std::bind(&client::writeCallback, this,
std::placeholders::_1,
std::placeholders::_2));
//mIoService.run();
}

void read(){
//length only hardcoded for test
asio::async_read(socket_,
asio::buffer(reply_, 4),
std::bind(&client::readCallback, this,
std::placeholders::_1,
std::placeholders::_2));
}

void writeCallback(const asio::error_code& error, size_t bytes_transferred){

if (error){
std::cout << "Client Write failed: " << error.message() << "\n";
closeConnection();
}
else
{
std::cout << " Client Write success "<< "\n";
//read should be open
}
}

void readCallback(const asio::error_code& error, size_t bytes_transferred) {
if (!error) {
std::cout << "Client Reply: ";
std::cout.write(reply_, bytes_transferred);
std::cout << "\n";
//start async_read again
read();
}
else {
std::cout << "Client Read failed: " << error.message() << "\n";
closeConnection();
}
}

void closeConnection(){
std::cout << "Client initiateDisconnect enter" << "\n";
try{
socket_.shutdown();
}catch(std::system_error &er){
std::cout << "Client close error" << er.what() << std::endl;
}
catch(...){
std::cout << "Client close undefined error" << std::endl;
}
std::cout << "Client close layer" << std::endl;
socket_.lowest_layer().close();
}

~client(){
closeConnection();
}

private:
asio::ssl::stream<asio::ip::tcp::socket> socket_;
size_t requestSize;
char request_[max_length];
char reply_[max_length];
};

using namespace std;

int main(){

asio::io_service server_service;
server s(server_service, 8877);
s.start();

std::this_thread::sleep_for(std::chrono::milliseconds(3000));
asio::io_service io_service;

asio::ip::tcp::resolver resolver(io_service);
asio::ip::tcp::resolver::query query("127.0.0.1", std::to_string(8877));
asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);

asio::ssl::context ctx(asio::ssl::context::sslv23);
ctx.load_verify_file("ca.pem");

client c(io_service, ctx, iterator);

std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.send("test");
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
c.closeConnection();
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
std::cout << "Main leave\n";
return 0;
}
------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio

------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Cliff Green
2016-02-08 22:50:40 UTC
Permalink
(I tried sending e-mail direct, but it was rejected: "This email was
rejected because it violates our security policy 550 5.7.1 Remotehost is
listed in the following RBL lists: NixSpam RBL".)

Other responses have identified your probably problem - you're not invoking
ioservice.run correctly. In my rejected e-mail I suggested simplifying by:

-- Remove "delete this" - use shared pointers instead for handler lifetime
management

-- Remove all thread creation; run everything in one thread (or
alternatively, do all networking in one thread, main thread simply calls
ioservice.run)

-- Take all shutdown and socket close logic out, get reads and writes
working, then add shutdown logic back in

-- Change server class to contain one session object and only do one accept;
create session object after accept; add multiple sessions later;
(C++ note - instead of a raw pointer, consider using std::unique_ptr or
std::shared_ptr for the session object - the shared_ptr will simplify
lifetime issues when closing / shutting down the socket, so use shared_ptr
unless you have a really good idea of the control flow for socket shutdown)

-- Change client class to contain one session object; create session object
after connect; (same C++ note about std::unique_ptr or shared_ptr)

-- Modify session class to always start an async read, either in session
ctor, or by "start_read" method that is called by the client and server
classes

Once this is going you should have a good idea of the async design - you
will have both async reads and writes going on a socket. Since all of the
reads and writes are in one thread you do not have to deal with concurrency
issues (in particular, whether the OS supports two threads reading and
writing from the same socket).

Cliff





------------------------------------------------------------------------------
Site24x7 APM Insight: Get Deep Visibility into Application Performance
APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month
Monitor end-to-end web transactions and take corrective actions now
Troubleshoot faster and improve end-user experience. Signup Now!
http://pubads.g.doubleclick.net/gampad/clk?id=272487151&iu=/4140
_______________________________________________
asio-users mailing list
asio-***@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Loading...