I've been working on an async boost server program, and so far I've got it to connect. However I'm now getting a "Vector iterator not dereferencable" error.
I suspect the vector gets destroyed or dereferenced before he packet gets sent thus causing the error.
void start()
{
Packet packet;
packet.setOpcode(SMSG_PING);
send(packet);
}
void send(Packet packet)
{
cout << "DEBUG> Transferring packet with opcode " << packet.GetOpcode() << endl;
async_write(m_socket, buffer(packet.write()), boost::bind(&Session::writeHandler, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
}
void writeHandler(const boost::system::error_code& errorCode, size_t bytesTransferred)
{
cout << "DEBUG> Transfered " << bytesTransferred << " bytes to " << m_socket.remote_endpoint().address().to_string() << endl;
}
Start gets called once a connection is made.
packet.write() returns a uint8_t vector
Would it matter if I'd change
void send(Packet packet)
to
void send(Packet& packet)
Not in relation to this problem but performance wise.
All this depends on how your Packet class is implemented. How it is copied, .... Has the copy of Packet class do a deep copy or just a default copy? if it is a default copy and your class Packet is not a POD, this can be the reason, and you will need to do a deep copy.
In general it is better to pass a class parameter by const& so maybe you should try with
void send(Packet const& packet);
I have found a solution, as the vector would get destroyed I made a queue that contains the resulting packets and they get processed one by one, now nothing gets dereferenced so the problem is solved.
might want to change my queue to hold the packet class instead of the result but that's just a detail.
Related
I am struggeling to understand why my quite simple UDP receiver is getting heap-free-after-use error (diagnosed by ASAN). The idea is listen to a configurable number of local ports for incoming packets.
I am here posting a simplified version of the class
UdpReceiver.hpp
class UdpReceiver
{
public:
UdpReceiver(std::vector<int> listen_ports);
void run();
protected:
boost::asio::io_service m_io;
char m_receive_buffer[MAX_RECEIVE_LENGTH];
std::vector<udp::endpoint> m_endpoints;
std::vector<udp::socket> m_sockets;
void handleUdpData(const boost::system::error_code& error, size_t bytes_recvd, int idx);
};
UdpReceiver.cpp
UdpReceiver::UdpReceiver(std::vector<int> listen_ports) :
m_io()
{
int idx = 0;
try {
for (auto port: listen_ports) {
m_endpoints.push_back(udp::endpoint(udp::v4(), port));
m_sockets.push_back(udp::socket(m_io, m_endpoints[idx]));
m_sockets[idx].async_receive_from(
boost::asio::buffer(m_receive_buffer, MAX_RECEIVE_LENGTH), m_endpoints[idx],
boost::bind(&MessageParser::handleUdpData, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
idx)
);
idx++;
}
} catch(const std::exception &exc)
{
std::cerr <<exc.what();
exit(-1);
}
}
According to ASAN m_endpoints.push_back(udp::endpoint(udp::v4(), port)) allocates some dynamic memory which is again freed by a later iteration. This eventually gives me use-after-free which messes up my application in a unpredictable way.
I cant really understand how the use of std::vector should not work in this case. Any ideas?
The documentation for async_receive_from says: "Ownership of the sender_endpoint object is retained by the caller, which must guarantee that it is valid until the handler is called."
Your push_backs may reallocate the underlying storage, leaving async_receive_from with a dangling reference.
To avoid reallocation, reserve space for the necessary amount of elements before entering the loop:
m_endpoints.reserve(listen_ports.size());
m_sockets.reserve(listen_ports.size());
I am calling my server via TCP/IP. I am sending a couple of chars and want to receive an answer, that is send by my acknowledge-fct. But I only receive as many chars as I sent to the server.
constexpr int maxInputBufferLength{1024};
using boost::asio::ip::tcp;
struct session
: public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start(){
do_read();
}
private:
void do_read(){
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, maxInputBufferLength),
[this, self](boost::system::error_code ec, std::size_t length)
{
command_t currentCommand{data_};
if(currentCommand.commandStringVector.front()==driveCommand){
acknowledge("driveCommand triggered by TCP");
}
....
}
void acknowledge(std::string ackmessage){
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(ackmessage, maxInputBufferLength),
[this, self, ackmessage](boost::system::error_code ec, std::size_t length)
{
std::cout << ackmessage <<"\n";
if(ec.value()){
std::cerr << ec.category().name() << ':' << ec.value() << "\n";
}
});
}
char data_[maxInputBufferLength];
};
According to my little knowledge, my acknowledge function that calls async_write with its own boost::asio::buffer should send the whole buffer, which should contain ackmessage with a length of maxInputBufferLength which is a global constexpr int.
You have undefined behaviour, because you pass local variable into buffer. buffer is only wrapper for string, it can be treated as pair: pointer to data and size of data, it doesn't make copy of ackmessage. You need to ensure that ackmessage exists until handler for async_write is called. async_write returns immediately, so what you have now looks like:
acknowledge ()
{
ackmessage // local variable
async_write is called, it creates buffer to askmessage, pointer to string is stored
// async_write returns immediately
// ackmesssage is destroyed here
}
Fix:
1) consider using synchronous writing operation
2) if you want to stay with async you could store ackmessage as data member of session and then pass it as argument to buffer. You need to find some way of extending its lifetime until handler is invoked.
Here is nice example to resolve your issue, create shared_ptr from ackmessage and pass it to your handler - lambda, it extends lifetime of string which is being writing.
Since I am committed to develop some little audio applications that share audio content over network through the UDP protocol, I am currently drafting the code for a UDP Client class.
This mentioned class should receive the audio content of the other clients connected to network and also send the audio content processed on the local machine; all these contents are exchanged with a server, that works as a kind of content router.
Since audio content is generated by a process() method that is periodically called by the audio application, in order to not loose packets, each audio application should have a kind of UDP listener that is independent from the process() method and that should always be active; they should only share a buffer, or memory allocation, where audio data can be temporary saved and later processed.
Taking all this into account, I coded this method:
void udp_client::listen_to_packets() {
while (udp_client::is_listening) {
if ((udp_client::message_len = recvfrom(udp_client::socket_file_descr, udp_client::buffer, _BUFFER_SIZE, MSG_WAITALL, (struct sockaddr*) &(udp_client::client_struct), &(udp_client::address_len))) < 0) {
throw udp_client_exception("Error on receiving message.");
}
std::cout << "New message received!" << std::endl;
}
std::cout << "Stop listenig for messages!" << std::endl;
}
As you can see, the function uses udp_client::buffer that is the shared memory allocation I previously mentioned. In order to let it be always active, I was thinking to start a new thread or process at class construction and stop its execution at class destruction:
udp_client::udp_client():
is_listening(true) {
std::cout << "Constructing udp_client..." << std::endl;
std::thread listener = std::thread(udp_client::listen_to_packets);
}
udp_client::~udp_client() {
std::cout << "Destructing udp_client..." << std::endl;
udp_client::is_listening = false;
}
Of course, the above listed code doesn't work and as #user4581301 suggested, the listener and is_listening variable definitions have been moved to class attributes:
private:
std::atomic<bool> is_listening;
std::thread listener;
Furthermore the constructor and destructor have been modified a little:
udp_client::udp_client():
listener(&udp_client::listen_to_packets, this),
is_listening(true) {
std::cout << "Constructing udp_client..." << std::endl;
}
udp_client::~udp_client() {
std::cout << "Destructing udp_client..." << std::endl;
udp_client::is_listening = false;
listener.join();
}
Unfortunately, g++ still returns an error, saying that there are not constructors with two arguments for the std::thread class:
error: no matching constructor for initialization of 'std::thread'
listener(&udp_client::listen_to_packets, this)
So, what should I modify to make the code work properly?
Here you can see the implementation of the class (hoping that this link is allowed for Stack Overflow rules):
https://www.dropbox.com/sh/lzxlp3tyvoncvxo/AAApN5KLf3YAsOD0PV7wJJO4a?dl=0
I've set up a simple async tcp server using Asio (non-boost), which pretty much follows the code used here: http://think-async.com/Asio/asio-1.11.0/doc/asio/tutorial/tutdaytime3.html
I'm experiencing an issue where attempting to access a variable of the current tcp_connection instance inside the completion handler for async_read_some/async_receive throws an error. The variable in question is simply a pointer to an instance of an encryption class that I have created. It seems that this pointer becomes invalid (address of 0xFEEEFEEE) once the completion handler is called. Here's the tcp_connection class that gets created once a connection from a client is made:
class tcp_connection
: public enable_shared_from_this<tcp_connection> {
public:
typedef shared_ptr<tcp_connection> pointer;
static pointer create(asio::io_service &ios) {
return pointer(new tcp_connection(ios));
}
tcp::socket &socket() {
return socket_;
}
void start() {
byte* buf = new byte[4096];
socket_.async_receive(asio::buffer(buf, 4096), 0,
bind(&tcp_connection::handle_receive, this,
buf,
std::placeholders::_1, std::placeholders::_2));
}
private:
tcp_connection(asio::io_service &ios)
: socket_(ios) {
crypt_ = new crypt();
}
void handle_receive(byte* data, const asio::error_code &err, size_t len) {
cout << "Received packet of length: " << len << endl;
crypt_->decrypt(data, 0, len); // This line causes a crash, as the crypt_ pointer is invalid.
for (int i = 0; i < len; ++i)
cout << hex << setfill('0') << setw(2) << (int)data[i] << ", ";
cout << endl;
}
tcp::socket socket_;
crypt* crypt_;
};
I'm assuming this has something to do with the way Asio uses threads internally. I would have thought that the completion handler (handle_receive) would be invoked with the current tcp_connection instance, though.
Is there something I'm missing? I'm not too familiar with Asio. Thanks in advance.
Firstly, you should use shared_from_this to prevent tcp_connection to be "collected" when there are only extant async operations:
socket_.async_receive(asio::buffer(buf, 4096), 0,
bind(&tcp_connection::handle_receive, shared_from_this()/*HERE!!*/,
buf,
std::placeholders::_1, std::placeholders::_2));
Secondly, your tcp_connection class should implement Rule Of Three (at least cleanup crypt_ in the destructor and prohibit copy/assignment).
You also don't free up buf in your current sample.
Of course, in general, just use smart pointers for all of these.
Live On Coliru
I'm currently having a hard time with boost::asio, specially with the buffers in async_write operations.
When I want to write a packet, I use
async_write(sock_, boost::asio::buffer((char *)&t.getData(), sizeof(T)), boost::bind(&BoostTcpSocket::manageWrite, this, t, boost::asio::placeholders::error));
It works fine if i try to send one packet at the time. However if I make two followed call of async_write, it sends me twice the second packet. I have no problem with the packet life cycle.
I think the problem comes from the buffer, i'd like to have a packetQueue buffer instead of one packet buffer.
How can i do this ?
Thanks for your help
EDIT : here is more info about the code :
Here is how it works :
int BoostTcpSocket::manageWrite(Packet *t, const boost::system::error_code& error)
{
if (!er)
std::cout << "Success " << t.getData() << std::endl;
else
std::cout << "Error" << std::endl;
// delete t; => Packet LifeCycle isn't the problem here...
}
int main()
{
boost::asio::ip::tcp::socket sock_(io);
sock_.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1", 8080));
Packet *p = new Packet("data1");
Packet *p2 = new Packet("data2");
sock_.async_write(boost::asio::buffer((char *)&p->getData(), sizeof(Packet::data)), boost::bind(&BoostTcpSocket::manageWrite, this, p, boost::asio::placeholders::error));
sock_.async_write(boost::asio::buffer((char *)&p2->getData(), sizeof(Packet::data)), boost::bind(&BoostTcpSocket::manageWrite, this, p2, boost::asio::placeholders::error));
io.run();
}
This code basically sends me twice the data "data2" even though the output on the server side std::cout is Success data1 Success data2...
EDIT : Apparently i was doing something wrong somewhere else because it is now working.. Thanks everybody for your time & help !
The data at &t.getData() must remain intact throughout the entire asynchronous operation. You must not overwrite the data there before receiving a call back to BoostUdpSocket::manageWrite with a success indication.
You have bound two times *p in your async_write(..) callbacks. This leads to double deletion!
See boost::bind(..., *p, ...)
sock_.async_write(boost::asio::buffer((char *)&p->getData(), sizeof(Packet::data)), boost::bind(&BoostTcpSocket::manageWrite, this, *p, boost::asio::placeholders::error));
sock_.async_write(boost::asio::buffer((char *)&p2->getData(), sizeof(Packet::data)), boost::bind(&BoostTcpSocket::manageWrite, this, *p, boost::asio::placeholders::error));
BTW: if you wannt to pass *p as reference, consider using boost::ref(..) or boost::cref(..).
EDIT:
The double delete is, i think, not your problem, but passing *p without boost::ref(..) is the problem - since you're deleting an object which is not heap allocated (because it is a copy!).
EDIT2:
Oh i see - you have the code corrcted in the meantime. Is there still a runtime error?