Can not find why this program is failing. It must be my boost usage. Problem is highlighted in comment and there is a small note about some of the function calls
/* Includes Hidden */
using boost::asio::ip::udp;
class UDP_Server {
public:
UDP_Server(boost::asio::io_service& IO, unsigned short PORT)
: sock(IO, udp::endpoint(udp::v4(),PORT)) {
Listen();
}
~UDP_Server() {
for(auto& endpoint : Clients) {
delete endpoint;
}
Clients.clear();
}
void Listen() {
//waits for msg to be sent. Captures end point and sends address
//so server can store connections
udp::endpoint* T = new udp::endpoint;
sock.async_receive_from(
boost::asio::buffer(rbuf),*T,
boost::bind(&UDP_Server::handle_rec, this, T,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_rec(udp::endpoint* EP, const boost::system::error_code& err, size_t len) {
//When the program enters here, err is 234 (windows error for more data available)
//len is 0 and rbuf is empty.
if(err && err != boost::asio::error::message_size) {
std::cerr << err.message() << std::endl;
}
std::cout.write(rbuf.data(),rbuf.size());
bool ThisClient = false;
std::string Msg = "";
for( auto& EPs : Clients) {
if(EPs == EP) {
ThisClient = true; break;
}
}
if(!ThisClient) {
if(len > 0 && rbuf[0]=='0') {
Clients.push_back(EP);
Msg = "Connected";
}else{
Msg = "Connection Refused";
}
}else{
if(rbuf[0]=='0') {
delete EP;
Clients.remove(EP);
Msg = "Disconnected";
}
}
//queue message to send back and call handle_snd function
sock.async_send_to(boost::asio::buffer(Msg),*EP,
boost::bind(&UDP_Server::handle_snd,this,EP,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
));
Listen(); //listen for some more messages!
} //debugging through the first time through this function eventually exits here
//and ends up going through a bunch of code I didn't write, and ultimately fail.
void handle_snd(udp::endpoint *Dest, const boost::system::error_code& err, size_t len) {
}
private:
udp::socket sock;
std::list<udp::endpoint*> Clients;
std::vector<char> rbuf;
};
void HostStart() {
try {
boost::asio::io_service io;
UDP_Server Host(io,13);
io.run();
}catch(std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
int main() {
std::thread thd(HostStart); //start server
try {
boost::asio::io_service io2;
udp::resolver res(io2);
udp::resolver::query queer(udp::v4(),"127.0.0.1","daytime");
udp::endpoint HostEP = *res.resolve(queer);
udp::socket sock(io2);
sock.open(udp::v4());
std::string Msg = "0";
std::vector<char> MsgArray(Msg.begin(),Msg.end());
sock.send_to(boost::asio::buffer(Msg),HostEP);
io2.run();
udp::endpoint RecEP;
std::array<char,128> rbuf;
sock.receive_from(boost::asio::buffer(rbuf),RecEP);
std::cout.write(rbuf.data(),rbuf.size());
sock.send_to(boost::asio::buffer(Msg),HostEP);
sock.receive_from(boost::asio::buffer(rbuf),RecEP);
std::cout.write(rbuf.data(),rbuf.size());
}catch(std::exception& e) {
std::cerr << e.what() << std::endl;
}
Sleep(10000);
return 0;
}
If I use debugging and walk through this code, I find that I ultimately end up in a file called
win_iocp_io_service.ipp
and I get this error:
In my main, I'm just trying to synch send a couple message to test the asynch server class. I have no idea why the buffer is empty after the async server call and why I am getting this error.
Possibly it is related to when I call run on my io service and the way I am trying to multithread it.
Thank you
This may be the result of the program invoking undefined behavior. Within UDP_Server::handle_rec(), the call to udp::socket::async_send_to() violates the requirement that the underlying memory provided to the buffer must remain valid until the handler is called.
Although the buffers object may be copied as necessary, ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.
To meet this criteria, consider making Msg a data member of UDP_Server, rather than an automatic variable.
Also, two other points to consider:
UDP_Server::rbuf will always maintain a size of zero, causing udp::socket::async_receive_from() in UDP_Server::Listen() to read nothing, as there is no memory into which data can be read. udp::socket::async_receive_from() only modifies the contents of the memory block provided to it as a buffer; it is the caller's responsibility to have already allocated the memory block. Either resize rbuf to a size large enough to handle all incoming datagrams, or lazily allocate the buffer.
In main(), rbuf.size() will always return 128, regardless of how much data was actually received. udp::socket::receive_from()'s return value indicates the number of bytes received, and this value should be used when creating a boost::asio::buffer and when writing to std::cout.
Related
I want my TCP client to connect to multiple servers(each server has a separate IP and port).
I am using async_connect. I can successfully connect to different servers but the read/write fails since the server's corresponding tcp::socket object is not available.
Can you please suggest how I could store each server's socket in some data structure? I tried saving the IP, socket to a std::map, but the first server's socket object is not available in memory and the app crashes. I tried making the socket static, but it does not help either.
Please help me!!
Also, I hope I am logically correct in making a single TCP client connect to 2 different servers.
I am sharing below the simplified header & cpp file.
class TCPClient: public Socket
{
public:
TCPClient(boost::asio::io_service& io_service,
boost::asio::ip::tcp::endpoint ep);
virtual ~TCPClient();
void Connect(boost::asio::ip::tcp::endpoint ep, boost::asio::io_service &ioService, void (Comm::*SaveClientDetails)(std::string,void*),
void *pClassInstance);
void TransmitData(const INT8 *pi8Buffer);
void HandleWrite(const boost::system::error_code& err,
size_t szBytesTransferred);
void HandleConnect(const boost::system::error_code &err,
void (Comm::*SaveClientDetails)(std::string,void*),
void *pClassInstance, std::string sIPAddr);
static tcp::socket* CreateSocket(boost::asio::io_service &ioService)
{ return new tcp::socket(ioService); }
static tcp::socket *mSocket;
private:
std::string sMsgRead;
INT8 i8Data[MAX_BUFFER_LENGTH];
std::string sMsg;
boost::asio::deadline_timer mTimer;
};
tcp::socket* TCPClient::mSocket = NULL;
TCPClient::TCPClient(boost::asio::io_service &ioService,
boost::asio::ip::tcp::endpoint ep) :
mTimer(ioService)
{
}
void TCPClient::Connect(boost::asio::ip::tcp::endpoint ep,
boost::asio::io_service &ioService,
void (Comm::*SaveServerDetails)(std::string,void*),
void *pClassInstance)
{
mSocket = CreateSocket(ioService);
std::string sIPAddr = ep.address().to_string();
/* To send connection request to server*/
mSocket->async_connect(ep,boost::bind(&TCPClient::HandleConnect, this,
boost::asio::placeholders::error, SaveServerDetails,
pClassInstance, sIPAddr));
}
void TCPClient::HandleConnect(const boost::system::error_code &err,
void (Comm::*SaveServerDetails)(std::string,void*),
void *pClassInstance, std::string sIPAddr)
{
if (!err)
{
Comm* pInstance = (Comm*) pClassInstance;
if (NULL == pInstance)
{
break;
}
(pInstance->*SaveServerDetails)(sIPAddr,(void*)(mSocket));
}
else
{
break;
}
}
void TCPClient::TransmitData(const INT8 *pi8Buffer)
{
sMsg = pi8Buffer;
if (sMsg.empty())
{
break;
}
mSocket->async_write_some(boost::asio::buffer(sMsg, MAX_BUFFER_LENGTH),
boost::bind(&TCPClient::HandleWrite, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void TCPClient::HandleWrite(const boost::system::error_code &err,
size_t szBytesTransferred)
{
if (!err)
{
std::cout<< "Data written to TCP Client port! ";
}
else
{
break;
}
}
You seem to know your problem: the socket object is unavailable. That's 100% by choice. You chose to make it static, of course there will be only one instance.
Also, I hope I am logically correct in making a single TCP client connect to 2 different servers.
It sounds wrong to me. You can redefine "client" to mean something having multiple TCP connections. In that case at the very minimum you expect a container of tcp::socket objects to hold those (or, you know, a Connection object that contains the tcp::socket.
BONUS: Demo
For fun and glory, here's what I think you should be looking for.
Notes:
no more new, delete
no more void*, reinterpret casts (!!!)
less manual buffer sizing/handling
no more bind
buffer lifetimes are guaranteed for the corresponding async operations
message queues per connection
connections are on a strand for proper synchronized access to shared state in multi-threading environments
I added in a connection max idle time timeout; it also limits the time taken for any async operation (connect/write). I assumed you wanted something like this because (a) it's common (b) there was an unused deadline_timer in your question code
Note the technique of using shared pointers to have Comm manage its own lifetime. Note also that _socket and _outbox are owned by the individual Comm instance.
Live On Coliru
#include <boost/asio.hpp>
#include <deque>
#include <iostream>
using INT8 = char;
using boost::asio::ip::tcp;
using boost::system::error_code;
//using SaveFunc = std::function<void(std::string, void*)>; // TODO abolish void*
using namespace std::chrono_literals;
using duration = std::chrono::high_resolution_clock::duration;
static inline constexpr size_t MAX_BUFFER_LENGTH = 1024;
using Handle = std::weak_ptr<class Comm>;
class Comm : public std::enable_shared_from_this<Comm> {
public:
template <typename Executor>
explicit Comm(Executor ex, tcp::endpoint ep, // ex assumed to be strand
duration max_idle)
: _ep(ep)
, _max_idle(max_idle)
, _socket{ex}
, _timer{_socket.get_executor()}
{
}
~Comm() { std::cerr << "Comm closed (" << _ep << ")\n"; }
void Start() {
post(_socket.get_executor(), [this, self = shared_from_this()] {
_socket.async_connect(
_ep, [this, self = shared_from_this()](error_code ec) {
std::cerr << "Connect: " << ec.message() << std::endl;
if (!ec)
DoIdle();
else
_timer.cancel();
});
DoIdle();
});
}
void Stop() {
post(_socket.get_executor(), [this, self = shared_from_this()] {
if (not _outbox.empty())
std::cerr << "Warning: some messages may be undelivered ("
<< _ep << ")" << std::endl;
_socket.cancel();
_timer.cancel();
});
}
void TransmitData(std::string_view msg) {
post(_socket.get_executor(),
[this, self = shared_from_this(), msg = std::string(msg.substr(0, MAX_BUFFER_LENGTH))] {
_outbox.emplace_back(std::move(msg));
if (_outbox.size() == 1) { // no send loop already active?
DoSendLoop();
}
});
}
private:
// The DoXXXX functions are assumed to be on the strand
void DoSendLoop() {
DoIdle(); // restart max_idle even after last successful send
if (_outbox.empty())
return;
boost::asio::async_write(
_socket, boost::asio::buffer(_outbox.front()),
[this, self = shared_from_this()](error_code ec, size_t xfr) {
std::cerr << "Write " << xfr << " bytes to " << _ep << " " << ec.message() << std::endl;
if (!ec) {
_outbox.pop_front();
DoSendLoop();
} else
_timer.cancel(); // causes Comm shutdown
});
}
void DoIdle() {
_timer.expires_from_now(_max_idle); // cancels any pending wait
_timer.async_wait([this, self = shared_from_this()](error_code ec) {
if (!ec) {
std::cerr << "Timeout" << std::endl;
_socket.cancel();
}
});
}
tcp::endpoint _ep;
duration _max_idle;
tcp::socket _socket;
boost::asio::high_resolution_timer _timer;
std::deque<std::string> _outbox;
};
class TCPClient {
boost::asio::any_io_executor _ex;
std::deque<Handle> _comms;
public:
TCPClient(boost::asio::any_io_executor ex) : _ex(ex) {}
void Add(tcp::endpoint ep, duration max_idle = 3s)
{
auto pcomm = std::make_shared<Comm>(make_strand(_ex), ep, max_idle);
pcomm->Start();
_comms.push_back(pcomm);
// optionally garbage collect expired handles:
std::erase_if(_comms, std::mem_fn(&Handle::expired));
}
void TransmitData(std::string_view msg) {
for (auto& handle : _comms)
if (auto pcomm = handle.lock())
pcomm->TransmitData(msg);
}
void Stop() {
for (auto& handle : _comms)
if (auto pcomm = handle.lock())
pcomm->Stop();
}
};
int main() {
using std::this_thread::sleep_for;
boost::asio::thread_pool ctx(1);
TCPClient c(ctx.get_executor());
c.Add({{}, 8989});
c.Add({{}, 8990}, 1s); // shorter timeout for demo
c.TransmitData("Hello world\n");
c.Add({{}, 8991});
sleep_for(2s); // times out second connection
c.TransmitData("Three is a crowd\n"); // only delivered to 8989 and 8991
sleep_for(1s); // allow for delivery
c.Stop();
ctx.join();
}
Prints (on Coliru):
for p in {8989..8991}; do netcat -t -l -p $p& done
sleep .5; ./a.out
Hello world
Connect: Success
Connect: Success
Hello world
Connect: Success
Write 12 bytes to 0.0.0.0:8989 Success
Write 12 bytes to 0.0.0.0:8990 Success
Timeout
Comm closed (0.0.0.0:8990)
Write Three is a crowd
17Three is a crowd
bytes to 0.0.0.0:8989 Success
Write 17 bytes to 0.0.0.0:8991 Success
Comm closed (0.0.0.0:8989)
Comm closed (0.0.0.0:8991)
The output is a little out of sequence there. Live local demo:
I am implementing a class that uses boost::asio to implement a library for TLS connections.
I am implementing only synchronous operations and some of them accept a timeout. I implement the timeout methods using a deadline_timer and io_service.run_one, as explained in this example: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/timeouts/async_tcp_client.cpp
My problem is with a method that reads exactly 'n' bytes from the socket and accepts a timeout as a parameter. The problem is that the io_service.run_one() is raising a SIGSEV and I do no know why. Below is the code (it is so long, but I do not know any other better way to explain this):
The code
Below are the methods involved in the test I am executing:
void CMDRboostConnection::check_deadline()
{
// Check whether the deadline has passed. We compare the deadline against
// the current time since a new asynchronous operation may have moved the
// deadline before this actor had a chance to run.
if (m_timeoutOpsTimer->expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
// TODO do I need to cancel async operations?
m_timeoutOpsErrorCode = boost::asio::error::timed_out;
// There is no longer an active deadline. The expiry is set to positive
// infinity so that the actor takes no action until a new deadline is set.
m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin);
}
// Put the actor back to sleep.
m_timeoutOpsTimer->async_wait(
boost::bind(&CMDRboostConnection::check_deadline, this));
}
bool CMDRboostConnection::connect()
{
// TODO: This method already throws an exception, it should be void.
DEBUG("Connecting to " + m_url + " : " + m_port);
try
{
// If the socket is already connected, disconnect it before
// opening a new conneciont.
if (isConnected())
{
disconnect();
}
m_socket = new SSLSocket(m_ioService, m_context);
tcp::resolver resolver(m_ioService);
tcp::resolver::query query(m_url, m_port);
tcp::resolver::iterator end;
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::connect(m_socket->lowest_layer(), resolver.resolve(query));
if (endpoint_iterator == end)
{
DEBUG("Endpoint cannot be resolved, disconnecting...");
disconnect();
}
else
{
m_timeoutOpsTimer = new boost::asio::deadline_timer(m_ioService);
m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin);
// Start the persistent actor that checks for deadline expiry.
check_deadline();
DEBUG("Endpoint resolved, performing handshake");
m_socket->set_verify_mode(boost::asio::ssl::verify_none);
m_socket->handshake(SSLSocket::client);
DEBUG("Handshake done, connected to " + m_url + " : " + m_port);
m_isConnected = true;
}
}
catch (boost::system::system_error &err)
{
disconnect();
throw;
}
return m_isConnected;
}
std::streambuf& CMDRboostConnection::readNBytes(int n, unsigned int timeout)
{
try
{
if(!isConnected())
{
std::string err = "Cannot read, not connected";
ERROR(err);
throw std::logic_error(err);
}
if(n == 0)
{
return m_buffer;
}
m_timeoutOpsTimer->expires_from_now(
boost::posix_time::milliseconds(timeout));
m_timeoutOpsErrorCode = boost::asio::error::would_block;
boost::asio::async_read(
*m_socket,
m_buffer,
boost::asio::transfer_exactly(n),
boost::bind(
&CMDRboostConnection::timoutOpsCallback,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)
);
do
{
m_ioService.run_one();
} while (m_timeoutOpsErrorCode == boost::asio::error::would_block);
if(m_timeoutOpsErrorCode)
{
throw boost::system::system_error(m_timeoutOpsErrorCode);
}
return m_buffer;
}
catch(boost::system::system_error &err)
{
ERROR("Timeout reached trying to read a message");
disconnect();
throw;
}
}
void CMDRboostConnection::disconnect()
{
try
{
DEBUG("Disconnecting...");
if(isConnected())
{
m_socket->shutdown();
DEBUG("Closing socket...");
m_socket->lowest_layer().close();
if(m_socket != NULL)
{
delete m_socket;
m_socket = NULL;
}
}
if(m_timeoutOpsTimer != NULL)
{
delete m_timeoutOpsTimer;
m_timeoutOpsTimer = NULL;
}
DEBUG("Disconnection performed properly");
m_isConnected = false;
}
catch (boost::system::system_error &err)
{
ERROR("Exception thrown, error = " << err.code() <<
", category: " << err.code().category().name() << std::endl);
m_isConnected = false;
throw;
}
}
The test
Below it is the test I am running to test the method:
TEST(CMDRboostConnection, readNbytesTimeoutDoesNotMakeTheProgramCrashWhenTmeout)
{
std::auto_ptr<CMDR::SSL::ICMDRsslConnection> m_connection =
std::auto_ptr<CMDR::SSL::ICMDRsslConnection>(
new CMDR::SSL::CMDRboostConnection("localhost", "9999"));
unsigned int sleepInterval = 0; // seconds
unsigned int timeout = 10; // milliseconds
unsigned int numIterations = 10;
std::string msg("delay 500000"); // microseconds
if(!m_connection->isConnected())
{
m_connection->connect();
}
for(unsigned int i = 0; i < numIterations; i++)
{
if(!m_connection->isConnected())
{
m_connection->connect();
}
ASSERT_NO_THROW( m_connection->write(msg) );
ASSERT_THROW (
m_connection->readNBytes(msg.size(), timeout),
boost::system::system_error);
ASSERT_FALSE(m_connection->isConnected());
ASSERT_NO_THROW( m_connection->connect() );
sleep(sleepInterval);
}
}
The problem
In the above test, the first loop iteration goes ok, that is, the first time the method readNBytes is called, it works (throws an exception as expected). The second time it is executed, it raises the SIGSEV.
EDIT
I am executing the above test among others that test other functionalities. I have realized that if I execute the above test only, it works. But, If I execute it in addition other, then the program crashes with the mentioned SIGSEV.
This is one of the tests that causes the problem:
TEST(CMDRboostConnection, canConnectDisconnect)
{
std::auto_ptr<CMDR::SSL::ICMDRsslConnection> m_connection =
std::auto_ptr<CMDR::SSL::ICMDRsslConnection>(
new CMDR::SSL::CMDRboostConnection("localhost", "9999"));
unsigned int sleepInterval = 0; // seconds
unsigned int timeout = 1000; // milliseconds
unsigned int numIterations = 10;
std::string msg("normally");
if(!m_connection->isConnected())
{
ASSERT_NO_THROW (m_connection->connect() );
}
for(unsigned int i = 0; i < numIterations; i++)
{
ASSERT_NO_THROW( m_connection->disconnect() );
sleep(sleepInterval);
ASSERT_NO_THROW( m_connection->connect() );
}
}
In conclusion, If I execute both the above tests, the first one crashes. But if I execute only the first one, it works.
EDIT 2
Fixed the bug mentioned in the comments.
You've messed up pointers and object lifetime management. If connect method is called when already connected you overwrite old socket with new and only then check whether it was already connected or used somewhere. Also auto_ptr is deprecated. You should use unique_ptr to manage owning pointers instead.
I have replaced all the member attributes by pointers, and now it works (that is, I can pass all tests I have write). The methods disconnect / connect are now as following:
bool CMDRboostConnection::connect()
{
// TODO: This method already throws an exception, it should be void.
DEBUG("Connecting to " + m_url + " : " + m_port);
try
{
// If the socket is already connected, disconnect it before
// opening a new conneciont.
if (isConnected())
{
disconnect();
}
m_ioService = new boost::asio::io_service();
m_timeoutOpsTimer = new boost::asio::deadline_timer(*m_ioService);
m_context = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
m_socket = new SSLSocket(*m_ioService, *m_context);
tcp::resolver resolver(*m_ioService);
tcp::resolver::query query(m_url, m_port);
tcp::resolver::iterator end;
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::connect(m_socket->lowest_layer(), resolver.resolve(query));
if (endpoint_iterator == end)
{
DEBUG("Endpoint cannot be resolved, disconnecting...");
disconnect();
}
else
{
m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin);
// Start the persistent actor that checks for deadline expiry.
check_deadline();
DEBUG("Endpoint resolved, performing handshake");
m_socket->set_verify_mode(boost::asio::ssl::verify_none);
m_socket->handshake(SSLSocket::client);
DEBUG("Handshake done, connected to " + m_url + " : " + m_port);
m_isConnected = true;
}
}
catch (boost::system::system_error &err)
{
disconnect();
throw;
}
return m_isConnected;
}
void CMDRboostConnection::disconnect()
{
try
{
DEBUG("Disconnecting...");
if(isConnected())
{
m_socket->shutdown();
DEBUG("Closing socket...");
m_socket->lowest_layer().close();
if(m_socket != NULL)
{
delete m_socket;
m_socket = NULL;
}
}
if(m_timeoutOpsTimer != NULL)
{
delete m_timeoutOpsTimer;
m_timeoutOpsTimer = NULL;
}
if(m_context != NULL)
{
delete m_context;
m_context = NULL;
}
if(m_ioService != NULL)
{
delete m_ioService;
m_ioService = NULL;
}
DEBUG("Disconnection performed properly");
m_isConnected = false;
}
catch (boost::system::system_error &err)
{
ERROR("Exception thrown, error = " << err.code() <<
", category: " << err.code().category().name() << std::endl);
if(m_timeoutOpsTimer != NULL)
{
delete m_timeoutOpsTimer;
m_timeoutOpsTimer = NULL;
}
if(m_context != NULL)
{
delete m_context;
m_context = NULL;
}
if(m_ioService != NULL)
{
delete m_ioService;
m_ioService = NULL;
}
m_isConnected = false;
throw;
}
}
As you can see, now the socket, the io_service, the deadline_timer and the context are created on connecting and released on disconnecting. I still do not understand what is going on, let me explain:
I have tried to reimplement the above variables one next one, that is, first the socket, then the timer, then the context and finally the io_service.
The tests have passed only when the io_service is a ptr, but I can't understand why. If io_service is a class-scoped variable, it should be deleted every time the class instance goes out of scope, that is, every time one of my TESTs finishes.
It seems that, before implementing it as a ptr, that was not happening. I suspect that maybe, when the readNBytes throws an exception due to a timeout, the read_async call remains in the io_service action queue, and maybe that caused the problem.
I am trying to write IO_Service based async TCP client where Async_write works fine but async_read runs in infinite loop . During my trial to rectify the issue i found that in all the other cases async_read just stops receiving data , it receives nothing until i stop the server. Below is the code and the links which i tried before posting my query.
The suggestions i tried are exactly as mine , 2 , 3 and but in all the cases my async_read handler does not read anything . In one and only one case it start infinite loop when i set buffer as boost::asio::mutable_buffer bytes; in rest cases i have tried boost::array<char, 512> bytes; , boost::asio::streambuf bytes; and char bytes[512]; where the async_read handler is not raised.
After going through all of these solution I am now confused : can it be the problem of buffer ? Do I need to initialize it before passing to read
?
Please guide.
ScalableSocket::ScalableSocket()
{
//ctor
using namespace boost::asio;
service = boost::make_shared<io_service>();
work = boost::make_shared<io_service::work>(*service );
strand = boost::make_shared<io_service::strand>( *service );
worker_threads = boost::make_shared<boost::thread_group>();
worker_threads->create_thread(boost::bind(&ScalableSocket::WorkerThread,this));
resolver = boost::make_shared<boost::asio::ip::tcp::resolver> (*service);
tcp_socket= boost::make_shared<boost::asio::ip::tcp::socket> (*service);
boost::asio::ip::tcp::resolver::query q(boost::asio::ip::tcp::v4(),"192.168.100.96","9602");
boost::asio::ip::tcp::resolver::iterator it = resolver->resolve(q);
boost::asio::async_connect(*tcp_socket,it,boost::bind(&ScalableSocket::connect_handler,this,boost::asio::placeholders::error));
tcp_socket->set_option(boost::asio::ip::tcp::no_delay(true) );
}
ScalableSocket::~ScalableSocket()
{
//dtor
}
void ScalableSocket::PublishPost()
{
strand->post(boost::bind(&ScalableSocket::OnSend,this));
}
void ScalableSocket::OnSend()
{
boost::array<char, 6> a = { 'a', 'b', 'c', 'd', 'e' };
boost::asio::async_write(*tcp_socket,boost::asio::buffer(a),
boost::bind(&ScalableSocket::write_handler, this, boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
void ScalableSocket::WorkerThread()
{
while( true )
{
try
{
boost::system::error_code ec;
service->run( ec );
if( ec )
{
///LOGE(ec);
}
break;
}
catch( std::exception & ex )
{
///LOGE(ex.what());
}
}
}
void ScalableSocket::connect_handler(const boost::system::error_code &ec)
{
if (!ec)
{
PublishPost();
/* boost::asio::async_read(*tcp_socket,
boost::asio::buffer(bytes),
boost::bind(&ScalableSocket::read_handler, this,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
*/
///https://stackoverflow.com/questions/4527443/problems-using-boostasioasync-read
boost::shared_ptr<boost::array<char, 512>> buf(new boost::array<char, 512>);
boost::asio::async_read(*tcp_socket,boost::asio::buffer(*buf),
boost::bind(&ScalableSocket::read_handler, this,buf,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
else
{
cout<<" Some error connecting to Exchange "<< ec.message()<<endl;
}
}
void ScalableSocket::OnTimer(const boost::system::error_code &ec)
{
if(!ec)
{
printf("\n\n Heartbeat event raised sending KeepAlive to exchange \n\n");
PublishPost();
HeartBeatTimer->async_wait(boost::bind(&ScalableSocket::OnTimer,this, boost::asio::placeholders::error));
}
}
void ScalableSocket::recvData()
{
boost::system::error_code error;
boost::array<char, 1024> buf;
//for(;;)
{
size_t len = tcp_socket->read_some(boost::asio::buffer(buf), error);
cout<<"\n Recv data size is "<<len;
}
}
void ScalableSocket::read_handler(boost::shared_ptr<boost::array<char, 512>> buf,const boost::system::error_code &ec,std::size_t bytes_transferred)
{
if (!ec )//&& bytes_transferred > 0)
{
///recvData(); /// If i enable this code during infinite loop it start getting data that means socket has no issue
cout << " Data size recieved "<< bytes_transferred<<endl;
boost::asio::async_read(*tcp_socket,boost::asio::buffer(*buf),
boost::bind(&ScalableSocket::read_handler, this,buf,
boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
else
{
/// Some issue with socket publish error , inform user and reconnect
cout<<" Some error reading data from Exchange "<< ec.message()<<endl;
}
}
void ScalableSocket::write_handler(const boost::system::error_code& error,std::size_t bytes_transferred)
{
if(!error)
{
/// data Sent successfully
cout<< " Data sent size "<< bytes_transferred<<endl;
}
else
{
cout<<" Some error sending data to Exchange "<< error.message()<<endl;
}
}
asnyc_readwill NOT "return" / call the handler until the given buffer is completely full.
asnyc_read_some will return after some bytes have been read. This might be the function you're looking for.
Remember to handle received data correctly with asnyc_read_some. If you send 512 bytes, it may arrive in a couple of reads, depending on the machine.
I am making an asynchronous UDP client using boost::asio
the send data is OK when receive data that async_receive_from is error
error message: Expression: string iterator not able to de-reference.
What's wrong with my code ?
Can anyone explain. Thanks for a lot.
UDPClient::UDPClient()
: socket_(io_service, udp::endpoint (udp::v4(), 0))
{
receiver_endpoint = boost::asio::ip::udp::endpoint(
boost::asio::ip::address::from_string("127.0.0.1"),
8080);
do_receive();
boost::function0< void> f = boost::bind(&UDPClient::Sendtest,this);
boost::thread t(f);
io_service.run();
}
void UDPClient::do_receive()
{
socket_.async_receive_from(boost::asio::buffer(recv_buffer), receiver_endpoint,
boost::bind(&UDPClient::handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void UDPClient::handle_receive(const boost::system::error_code& error, size_t bytes_transferred)
{
std::cout << "recve" << std::endl;
if (!error || error == boost::asio::error::message_size)
do_receive();
}
void UDPClient::Sendtest()
{
while(true)
{
boost::thread::sleep(boost::get_system_time()+boost::posix_time::seconds(10));
str = "23434";
size_t leng = str.length();
socket_.async_send_to( boost::asio::buffer(str,leng) ,
receiver_endpoint,
boost::bind(&UDPClient::handle_write,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
}
void UDPClient::handle_write(const boost::system::error_code &error,size_t bytes_transferred)
{
cout << "handle_write_over! " << endl;
}
int main()
{
UDPClient updclient;
}
These three lines look like the problem to me:
str = "23434";
size_t leng = str.length();
socket_.async_send_to( boost::asio::buffer(str,leng) ,
Assuming that str is declared as a std::string object, you need to know that std::string is not implicitly convertable to an ASIO buffer.
You might try this:
stocket_.async_send_to(boost::asio::buffer(&str.front(), leng),
Or this ought to compile correctly:
stocket_.async_send_to(boost::asio::buffer(str),
I would note additionally that you've got a different problem in this program, in that you're reusing the buffer before you are certain that the data has been sent. That's beyond the scope of this problem though, and represents a design issue you'll have to address on your own or in a different question.
I've got a problem where boost::asio::async_read fails on the second call, in a strange way:
std::atomic<error_code> ec(asio::error::would_block);
size_t len = 0;
// 1st call
asio::async_read(socket,
asio::buffer(buffer+X),
asio::transfer_exactly(512-X),
[&] (error_code const& err, size_t bytesTransferred)
{
len = bytesTransferred;
ec.store(err, std::memory_order_release);
}
);
/////// ... wait for read to complete ...
// 2nd call
asio::async_read(socket,
asio::buffer(buffer),
asio::transfer_exactly(512),
[&] (error_code const& err, size_t bytesTransferred)
{
len = bytesTransferred;
ec.store(err, std::memory_order_release);
}
);
The constant X is because I have some data already that I got in another way,
so the first read is smaller. Say X=364, then bytesTransferred will be 148 the first time around. My problem however is that the second read again returns 148 bytes, even though that read was for 512 bytes exactly.
I'm baffled. The second call doesn't have an error condition (I checked err). bytesTransferred is an argument passed by aync_read to me, and it's 148 bytes twice. The first time, it matches the asio::transfer_exactly(148) higher up on the stack .The second time the callstack clearly has a asio::transfer_exactly(512). What is going on here?
It's particular to that second call, though, The third call again reads 512 bytes, but also gets 512 bytes.
[MCVE]
#include <iostream>
#include <atomic>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/read.hpp>
// Minimal example, code that works has error checking removed. Class members turned itno globals etc.
namespace {
boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::socket sock(io_service);
std::vector<char> data(512);
boost::asio::mutable_buffers_1 buffer(&data[0], data.size());
unsigned read_counter = 1;
std::atomic<unsigned> read_timeout;
}
boost::system::error_code openSocket(const std::string &server,
const std::string &port)
{
boost::system::error_code error = boost::asio::error::host_not_found;
using boost::asio::ip::tcp;
tcp::resolver::query query(server, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
while (error && endpoint_iterator != end)
{
sock.close();
sock.connect(*endpoint_iterator++, error);
}
if (error)
{
std::cerr << "No route\n";
sock.close(); // Would be wrong to leave it open.
}
return error;
}
int read(size_t bytesNeeded)
{
size_t buffer_len = boost::asio::buffer_size(buffer);
size_t byteShift = buffer_len - bytesNeeded; // Read into back of buffer.
const int timeoutSeconds = 10;
boost::asio::deadline_timer deadline(io_service);
deadline.expires_from_now(boost::posix_time::seconds(timeoutSeconds)); // This will reset any outstanding timer
read_counter += 2; // If we'd use +1, after 4 billion cycles it would reset to 0
read_timeout.store(0, std::memory_order_release); // 0 = no timeout.
unsigned read_counter_copy = read_counter; // Can't capture global.
deadline.async_wait([read_counter_copy](boost::system::error_code const&) {
// read_timeout is very intentionally captured by value - timeout events are numbered
read_timeout.store(read_counter_copy, std::memory_order_release); }
);
// Start reading "asynchronously", wait for completion or timeout:
std::atomic<boost::system::error_code> ec(boost::asio::error::would_block);
size_t len = 0;
boost::asio::async_read(sock, boost::asio::buffer(buffer + byteShift), boost::asio::transfer_exactly(bytesNeeded),
[&, bytesNeeded](boost::system::error_code const& err, size_t bytesTransferred)
{
if (bytesTransferred != bytesNeeded) {
std::cout << bytesTransferred << " , " << err.message() << std::endl;
}
len = bytesTransferred;
ec.store(err, std::memory_order_release);
}
);
do {
io_service.run_one();
} while (read_timeout.load(std::memory_order_acquire) != read_counter && // Continue if the **last** read didn't time out
(ec.load(std::memory_order_acquire) == boost::asio::error::would_block) && // ec.store() not called,
!io_service.stopped()); // and program still running.
deadline.cancel(); // This will set read_timeout, if it wasn't set yet. But we ignore it from now on.
if (ec.load(std::memory_order_acquire))
{
std::cerr << "oops\n"; // Real error handling omitted.
throw std::runtime_error("");
}
else if (read_timeout == read_counter)
{
std::cerr << "timeout\n";
}
else if (len != bytesNeeded)
{
// This is the real problem.
std::cerr << "Asked " << bytesNeeded << " got " << len;
}
return (int)len;
}
int main(int argc, char* argv[])
{
do try {
::openSocket("192.168.6.30", "80");
read(148); // Assume that data[] already has 364 bytes on the first call.
for (;;)
{
read(512); // Full buffers on every subsequent call.
// Do something with data[] here.
}
}
catch (std::runtime_error) { } while (true);
}
The do try catch while is necessary because the error only happens after I unplug the other side. After the second call to read(148), the next read(512)` fails.
[update]
It's not just transfer_exactly. With transfer_at_least(512) I also get the same problem, one superfluous 148 byte read. (The two should behave the same, as reading at least 512 bytes into a buffer that's only 512 bytes cannot read more or less bytes)
"Solved" it for now by ignoring the incorrect read operation. I was lucky in that I could deal with an unknown amount of missing data, and resync with the stream later on. But it looks like I will have to drop Boost::Asio in the future when I can no longer tolerate missed data.