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.
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'm currently trying to get a chatroom type program working with boost::asio. In the current state, the server is able to accept connections from clients, and then the clients are able to send messages to the server (at which point, the server formats the message a little bit and then sends it to every client currently connected).
The problem I am having is as follows:
server starts
client 0 connects
client 0 sends a message
(the message is received by the server and then sent back to client 0 who receives it correctly)
client 1 connects
client 1 sends a message
(the message is received by the server and then sent back to client 0 and client 1 who both receive it correctly)
client 0 tries to send a message again
(the message is received by the server and the server processes the header then attempts to call async_read again to read the body of the message, however the socket member variable for client 0 no longer exists and I get a segfault)
I find this really strange because the server still has a valid socket object for client 0 (otherwise it wouldn't be able to send client 1's messages to client 0).
Here is the relevant code:
tcp_connection class (where the segfault occurs)
#include <deque>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
using boost::asio::ip::tcp;
class tcp_connection {
public:
tcp_connection(tcp::socket socket_, int id, std::function<void (std::size_t, char*, std::size_t)> read_handler)
: socket_(std::move(socket)), id_(id), read_handler_(read_handler) {
}
void start() {
char first_message[] = "server: connected";
net_message msg(first_message, strlen(first_message));
send(msg);
read_header();
}
void send(net_message msg) {
bool write_in_progress = !write_messages_.empty();
write_messages_.push_back(msg);
if (!write_in_progress) {
do_write();
}
}
int get_id() { return id_; }
private:
void read_header() {
boost::asio::async_read(socket_, boost::asio::buffer(read_message_.get_data(), net_message::header_length),
boost::bind(&tcp_connection::handle_read_header, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read_header(const boost::system::error_code e, std::size_t bytes_transferred) {
read_message_.decode_header();
read_body();
}
void read_body() {
/*
######################
THIS IS WHERE THE SEGFAULT OCCURS.
socket_ is no longer valid for some reason
despite socket_ still being valid for any async_write
operations that need to be handled by the do_write() function
######################
*/
boost::asio::async_read(socket_, boost::asio::buffer(read_message_.get_data() + net_message::header_length, read_message_.get_body_length()),
boost::bind(&tcp_connection::handle_read_body, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read_body(const boost::system::error_code e, std::size_t bytes_transferred) {
char body[read_message_.get_body_length()];
memcpy(body, read_message_.get_body(), read_message_.get_body_length());
// call the read_handler from the net_server object
read_handler_(id_, body, read_message_.get_body_length());
read_header();
}
void handle_write(const boost::system::error_code e, std::size_t bytes_transferred) {
}
void do_write() {
boost::asio::async_write(socket_, boost::asio::buffer(write_messages_.front().get_data(),
write_messages_.front().get_body_length() + net_message::header_length),
[this] (boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
write_messages_.pop_front();
if (!write_messages_.empty()) {
do_write();
}
} else {
std::cerr << "error with writing to client " << id_ << " with error code: " << ec << std::endl;
}
});
}
tcp::socket socket_;
std::function<void (std::size_t, char*, std::size_t)> read_handler_;
net_message read_message_;
std::deque<net_message> write_messages_;
int id_;
};
net_server class
class net_server {
public:
net_server(boost::asio::io_context& io_context, std::size_t port,
std::function<void (std::size_t)> accept_handler,
std::function<void (std::size_t, char*, std::size_t)> read_handler)
: io_context_(io_context), acceptor_(io_context, tcp::endpoint(tcp::v4(), 1234)),
accept_handler_(accept_handler), read_handler_(read_handler) {
start_accept();
}
void send_to(std::size_t id, const char* body, std::size_t length) {
net_message msg(body, length);
connections_[id].send(msg);
}
void send_to_all(const char* body, std::size_t length) {
net_message msg(body, length);
for (int i = 0; i < connections_.size(); i++) {
connections_[i].send(msg);
}
}
void send_to_all_except(std::size_t id, const char* body, std::size_t length) {
net_message msg(body, length);
for (int i = 0; i < connections_.size(); i++) {
if (i == id) continue;
connections_[i].send(msg);
}
}
private:
void start_accept() {
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
std::unique_lock lock(connections_mutex_);
std::size_t index = connections_.size();
connections_.emplace_back(std::move(socket), connections_.size(), read_handler_);
lock.unlock();
connections_[index].start();
accept_handler_(index);
}
start_accept();
});
}
boost::asio::io_context& io_context_;
tcp::acceptor acceptor_;
std::vector<tcp_connection> connections_;
std::mutex connections_mutex_;
std::function<void (std::size_t)> accept_handler_;
std::function<void (std::size_t, char*, std::size_t)> read_handler_;
};
main cpp program that sets up the server
#include <iostream>
class client {
public:
client()
: valid_(false)
{}
client(int id)
: id_(id), valid_(true)
{}
const char * get_name() const {
std::string str("Client ");
str += std::to_string(id_);
return str.c_str();
}
private:
int id_;
bool valid_;
};
class chat_server {
public:
chat_server(boost::asio::io_context& io_context, std::size_t port)
: server_(io_context, port, std::bind(&chat_server::handle_accept, this, std::placeholders::_1),
std::bind(&chat_server::handle_read, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3))
{}
void handle_accept(std::size_t client_index) {
std::scoped_lock lock(clients_mutex_);
if (clients_.size() != client_index) {
std::cerr << "New client connecting at index " << client_index <<
" however, clients_ vector currently has size " << clients_.size() << std::endl;
if (clients_.size() < client_index) {
clients_.resize(client_index);
clients_.emplace_back(client_index);
} else {
clients_[client_index] = client(client_index);
}
} else {
clients_.emplace_back(client_index);
}
std::cout << "New client with id: " << client_index << std::endl;
}
void handle_read(std::size_t sender, char* body, std::size_t length) {
// whenever the server receives a message, this function will be called
// where clients[sender] will be the connection that sent the message
// body will be a pointer to the start of the body of the message
// and length will be the length of the body
// we will process the message here and decide if / what to send in response
// (for example, in a chat server, we'd want to forward the message to every client
// with the name of the sender attached to it so that clients can update the chat dialogue)
std::size_t sender_name_len = strlen(clients_[sender].get_name());
std::size_t new_message_length = sender_name_len + length + 3;
char new_message[new_message_length];
sprintf(new_message, "%s: ", clients_[sender].get_name());
memcpy(new_message + sender_name_len + 2, body, length);
new_message[new_message_length - 1] = '\0';
std::cout << new_message << std::endl;
server_.send_to_all(new_message, new_message_length-1);
}
private:
net_server server_;
std::vector<client> clients_;
std::mutex clients_mutex_;
};
int main() {
try {
boost::asio::io_context io_context;
chat_server serv(io_context, 1234);
io_context.run();
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
What I want is for my server class to maintain a list of tcp_connections which each represent a client that has connected to the server. When the server accepts a connection, a tcp_connection object is created for that connection and then that tcp_connection object starts an infinite asynchronous "read_header -> read_body -> repeat" loop. Whenever the server receives a message from any of the clients, it should format the message and then send it to every tcp_connection in the list.
Your connections_ member variable is being reallocated when you add new elements to it. In your various handlers in tcp_connection you are capturing this, when the vector is reallocated the value of this will change and your handlers will then try to operate on the old copy of the object causing undefined behaviour.
The simple solution is to make your connections_ vector a vector of std::shared_ptr.
It is also best practice to capture your object's shared_ptr in your handlers so that the object can't go out of scope before your callbacks execute. e.g.:
void do_write() {
auto self = shared_from_this();
boost::asio::async_write(socket_, boost::asio::buffer(write_messages_.front().get_data(),
write_messages_.front().get_body_length() + net_message::header_length),
[self, this] (boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
write_messages_.pop_front();
if (!write_messages_.empty()) {
do_write();
}
} else {
std::cerr << "error with writing to client " << id_ << " with error code: " << ec << std::endl;
}
});
}
You'll need to derive tcp_connection from std::shared_from_this<tcp_connection> and make sure that you have created the shared_ptr before setting up any handlers (e.g. don't create handlers in the constructor).
I refer to the example from boost example:async_tcp_client.cpp. This example shows how to set timeout for aync_read. So I write a test program to test async_write timeout according to this example.
The write code is:
bool AsioAsyncTCPClientImpl::send(const uint8_t* data, int32_t length)
{
if (!m_isConnected || length <= 0) return false;
m_deadline.expires_after(std::chrono::seconds(5));
boost::system::error_code ec;
boost::asio::async_write(m_client, boost::asio::buffer(data, length),
std::bind(&AsioAsyncTCPClientImpl::handleWrite, this, std::placeholders::_1, std::placeholders::_2));
return true;
}
the handleWrite code is:
void AsioAsyncTCPClientImpl::handleWrite(const boost::system::error_code& ec, std::size_t len)
{
if (!m_started) return;
if (ec == boost::asio::error::eof)
{
fireConnectionCallback(ConnectionState::Disconnected);
return;
}
if (ec)
{
fireErrorCallback(ec);
return;
}
m_deadline.expires_at(boost::asio::steady_timer::time_point::max());
}
From my test, if I disable the network or pull out the cable of the PC where the server is running, the async_write will always completed as normal, so the timeout set is not working. I am wondering if I miss something, hope some one familiar with this could give me some clue, Thanks for advance!
Update
The async_wait code:
bool AsioAsyncTCPClientImpl::start()
{
if (m_started) return true;
connect();
m_deadline.async_wait(std::bind(&AsioAsyncTCPClientImpl::checkTimeout, this));
m_started = true;
m_ioLoopThread = std::thread(&AsioAsyncTCPClientImpl::loopProcess, this);
return true;
}
void AsioAsyncTCPClientImpl::checkTimeout()
{
if (!m_started) return;
if (m_deadline.expiry() <= boost::asio::steady_timer::clock_type::now())
{
std::cout << "wait timeout" << std::endl;
disconnect();
m_deadline.expires_at(boost::asio::steady_timer::time_point::max());
}
m_deadline.async_wait(std::bind(&AsioAsyncTCPClientImpl::checkTimeout, this));
}
And I put the run method of io_context in a thread, I am not sure if this is right way to do it, because I don't want to run io_context.run() in main function.
void AsioAsyncTCPClientImpl::loopProcess()
{
while(m_started)
{
m_context.run();
}
std::cout << "loop process exited" << std::endl;
}
You never await the timer.
Something like
m_deadline.async_wait(
std::bind(&AsioAsyncTCPClientImpl::handleTimer, this, boost::asio::placeholders::errpr));
And then
void AsioAsyncTCPClientImpl::handleTimer(boost::system::error_code ec) {
if (!ec) {
std::cout << "Timeout expired" << std::endl;
m_client.cancel();
}
}
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.
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.