?
VS2010, C++03, Boost 1.61.0
basiert auf: https://github.com/chriskohlhoff/asio/blob/master/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp
die Routinen
1. write_n_timeout
2. read_available
3. read_n_with_timeout
Verhalten sich mal so wie ich das gerne hätte - aber:
-die Fehlerfälle werde noch nicht "sauber" mit return-Code (weil die alten C/C++-Schnittstellen so sind) behandelt (wie komme ich da richtig drann?)
-ich bin mir mit den boost::lambdas oder handlern unsicher - ist das richtig so oder geht das mit weniger Code/sauberer
-ich bin bei den async_write/read/receive(mit den cancel) nicht 100% sicher ob das die richtigen Funktionen fuer mich sind
Fragen sind mit "Frage:" markiert
zum testen benutzte ich Hercules: http://www.hw-group.com/products/hercules/index_de.html
#include <boost/asio/connect.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/read.hpp>
#include <boost/bind/bind.hpp>
#include <boost/asio/placeholders.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>
using boost::asio::deadline_timer;
using boost::asio::ip::tcp;
using boost::lambda::bind;
using boost::lambda::var;
using boost::lambda::_1;
class client
{
public:
client(): socket_(io_service_), deadline_(io_service_)
{
deadline_.expires_at(boost::posix_time::pos_infin);
check_deadline();
}
void connect(const std::string& host, const std::string& service, boost::posix_time::time_duration timeout)
{
tcp::resolver::query query(host, service);
tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
deadline_.expires_from_now(timeout);
boost::system::error_code ec = boost::asio::error::would_block;
boost::asio::async_connect(socket_, iter, var(ec) = _1);
// Block until the asynchronous operation has completed.
do
{
io_service_.run_one();
}
while (ec == boost::asio::error::would_block);
if (ec || !socket_.is_open())
{
throw boost::system::system_error(ec ? ec : boost::asio::error::operation_aborted);
}
}
//warte auf n bytes bis zum timeout
int read_n_with_timeout(void* p_data, size_t p_size, boost::posix_time::time_duration timeout)
{
deadline_.expires_from_now(timeout);
boost::system::error_code ec = boost::asio::error::would_block;
boost::asio::async_read(socket_, boost::asio::buffer(p_data, p_size), var(ec) = _1); // async - fuer den timeout
// Block until the asynchronous operation has completed.
do
{
io_service_.run_one();
}
while (ec == boost::asio::error::would_block);
if (ec)
{
//throw boost::system::system_error(ec);
return -1;
}
//FRAGE: im Timeoutfall kommt "Die Netzwerkverbindung wurde durch das lokale System getrennt" als Exception meldung - kann ich damit sauber einen Timeout erkennen?
return 0; // 0 = OK, <0 = Fehler, -1 = TIMEOUT
}
void read_available_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if(!error)
{
printf("read_available_handler: %u\n", bytes_transferred);
int brk = 1;
}
else if(error == boost::asio::error::operation_aborted)
{
//wenn keine Daten + cancel()
printf("read_handler error: boost::asio::error::operation_aborted\n");
assert(bytes_transferred == 0);
}
else
{
printf("read_available_handler error: %s\n", error.message().c_str());
assert(false);
}
}
//lese was da ist - 0-n bytes ohne timeout da nicht blockierend
int read_available(void* p_data, size_t p_size)
{
boost::system::error_code ec;// = boost::asio::error::would_block;
//FRAGE: wie kann man das mit boost::lambda - oder eben C++03 konform schreiben ohne den read_available_handler?
socket_.async_receive(boost::asio::buffer(p_data, p_size), boost::bind(&client::read_available_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); // async - fuer den timeout
//hier will auch jemand 0-n bytes lesen - eben was da ist
//http://stackoverflow.com/questions/31939843/boostasioasync-receive-and-0-bytes-in-socket
socket_.cancel(); // abbrechen scheint hier die loesung zu sein um die gleiche semantic zu erreichen - es funktioniert auch, sieht aber komisch aus
//FRAGE: ist das so richtig?
//do
//{
io_service_.run_one();
//}
//while (ec == boost::asio::error::would_block);
//FRAGE: wie komme ich hier an die transfered bytes?
if (ec)
{
//throw boost::system::system_error(ec);
return -1;
}
return 0; // 0-n byte gelesen, <0 = FEHLER
}
//schreibe n bytes (mit timeout)
int write_n_timeout(void* p_data, size_t p_size, boost::posix_time::time_duration timeout)
{
deadline_.expires_from_now(timeout);
boost::system::error_code ec = boost::asio::error::would_block;
boost::asio::async_write(socket_, boost::asio::buffer(p_data, p_size), var(ec) = _1);
// Block until the asynchronous operation has completed.
do
{
io_service_.run_one();
}
while (ec == boost::asio::error::would_block);
if (ec)
{
//throw boost::system::system_error(ec);
return -1;
}
//Frage: wie den Timeout sauber erkennen?
return 0; // 0 = OK, <0 = Fehler, -1 = TIMEOUT
}
private:
void check_deadline()
{
if (deadline_.expires_at() <= deadline_timer::traits_type::now())
{
boost::system::error_code ignored_ec;
socket_.close(ignored_ec);
deadline_.expires_at(boost::posix_time::pos_infin);
}
// Put the actor back to sleep.
deadline_.async_wait(bind(&client::check_deadline, this));
}
boost::asio::io_service io_service_;
tcp::socket socket_;
deadline_timer deadline_;
boost::asio::streambuf input_buffer_;
};
//----------------------------------------------------------------------
int main(int argc, char* argv[])
{
try
{
std::string host = "localhost";
std::string service = "544";
client c;
c.connect(host, service, boost::posix_time::seconds(10));
char buffer[10]={0};
printf("write_n_timeout (schreibe n bytes (mit timeout))\n");
c.write_n_timeout(buffer, sizeof(buffer), boost::posix_time::seconds(10));
printf("read_available (lese was da ist - 0-n bytes ohne timeout da nicht blockierend)\n"); // bei dieser implementierung bin ich mir total unsicher
c.read_available(buffer, sizeof(buffer)); // hol was da ist - nicht blockierend und ohne timeout
printf("read_n_with_timeout (warte auf n bytes bis zum timeout)\n");
boost::posix_time::ptime time_sent = boost::posix_time::microsec_clock::universal_time();
c.read_n_with_timeout(buffer, sizeof(buffer), boost::posix_time::seconds(10));
boost::posix_time::ptime time_received = boost::posix_time::microsec_clock::universal_time();
std::cout << "Round trip time: ";
std::cout << (time_received - time_sent).total_microseconds();
std::cout << " microseconds\n";
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}