简体   繁体   中英

boost::asio, why does my socket immediately run callback function after calling async_receive_from?

I'm trying to do an async connection establishment using 3 way handshake. At the bottom of the code there is a main method that takes in an argument to determine whether it's acting as client or server.

If it's acting as server it creates a socket and is supposed to wait till it receives data then call the callback function multiplex to figure out how to handle the data it received.

If it's acting as client I will also create a socket that is asynchronously waiting to receive data but it will also send an syn packet via udp to the server using the create_connection method.

In the constructor for Socket I execute the method start_receive which should call async_receive_from on the udp socket. The problem is that it immediately called the callback function multiplex using an endpoint for 0.0.0.0:0 instead of just waiting to receive data.

#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/cstdint.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <unordered_map>
#include "packed_message.h"
#include "segment.pb.h"
#include "rtp.hpp"

#define DEBUG true
#define BUFFER_SIZE 10000

using boost::asio::ip::udp;
typedef boost::shared_ptr<rtp::Segment> SegmentPtr;
typedef std::vector<uint8_t> data_buffer;

// constructor for socket
rtp::Socket::Socket(boost::asio::io_service& io_service_, std::string source_ip, std::string source_port): 
    io_service_(io_service_),
    socket_(io_service_, udp::endpoint(boost::asio::ip::address::from_string(source_ip), std::stoi(source_port))),
    source_port(source_port),
    source_ip(source_ip)
{
    // accept incoming rtp segments
    std::cout << rtp::get_endpoint_str(socket_.local_endpoint()) << std::endl; 
    start_receive();
}

/**
 *  Accept incoming rtp segments
 */
void rtp::Socket::start_receive()
{
    data_buffer tmp_buf;
    socket_.async_receive_from(boost::asio::buffer(tmp_buf), remote_endpoint_, 
        boost::bind(&rtp::Socket::multiplex, this,
            tmp_buf,
            boost::asio::placeholders::error, 
            boost::asio::placeholders::bytes_transferred));

}

void rtp::Socket::multiplex(data_buffer& tmp_buf,
    const boost::system::error_code& error,
    std::size_t /*bytes_transferred*/)
{
    std::string identifier = rtp::get_endpoint_str(remote_endpoint_);

    if (connections.count(identifier) == 0 )
    {
        boost::shared_ptr<Connection> connection(new Connection(remote_endpoint_));
        connections.insert({rtp::get_endpoint_str(remote_endpoint_), connection});
        std::cout << rtp::get_endpoint_str(remote_endpoint_) << std::endl;
        connection_establishment(tmp_buf, connections.at(identifier));


    }
    else if(!(connections.at(identifier)->is_valid())) // connection not in list of connections
    {
        connection_establishment(tmp_buf, connections.at(identifier));


}

boost::shared_ptr<rtp::Connection> rtp::Socket::create_connection(std::string ip, std::string port)
{
    udp::resolver resolver_(io_service_);
    udp::resolver::query query_(ip, port);
    udp::endpoint remote_endpoint_ = *resolver_.resolve(query_);
    boost::shared_ptr<Connection> connection(new Connection(remote_endpoint_));

    connections.insert({rtp::get_endpoint_str(remote_endpoint_), connection});

    PackedMessage<rtp::Segment> m_packed_segment(SegmentPtr(new rtp::Segment()));
    boost::shared_ptr<data_buffer> message(new data_buffer);
    SegmentPtr ackseg(new rtp::Segment());
    ackseg->set_ack(true);
    PackedMessage<rtp::Segment> initialack(ackseg);
    initialack.pack(*message);



    socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
      boost::bind(&rtp::Socket::handle_send, this, message,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));

    return connection;

}

void rtp::Socket::connection_establishment(data_buffer& m_readbuf, boost::shared_ptr<Connection> connection)
{
    int buffer_position(0);
    PackedMessage<rtp::Segment> m_packed_segment(boost::shared_ptr<rtp::Segment>(new rtp::Segment()));
    boost::shared_ptr<data_buffer> message(new data_buffer);

    unsigned msg_len = m_packed_segment.decode_header(m_readbuf, buffer_position);
    buffer_position += HEADER_SIZE;
    m_packed_segment.unpack(m_readbuf, msg_len, buffer_position);
    buffer_position += msg_len;

    SegmentPtr synackseg = m_packed_segment.get_msg();
    if (synackseg->syn() && synackseg->ack())
    {
        SegmentPtr ackseg(new rtp::Segment());
        ackseg->set_ack(true);
        PackedMessage<rtp::Segment> finalack(ackseg);
        finalack.pack(*message);

        connection->set_valid(true);

        socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
            boost::bind(&rtp::Socket::handle_send, this,
                message,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));

    }
    else if (synackseg->syn() )
    {
        SegmentPtr synackseg(new rtp::Segment());
        synackseg->set_ack(true);
        synackseg->set_syn(true);
        PackedMessage<rtp::Segment> synack(synackseg);
        synack.pack(*message);

        socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
            boost::bind(&rtp::Socket::handle_send, this,
                message,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));


    }
    else if (synackseg->ack())
    {
        connection->set_valid(true);
    }

    start_receive();
}

void rtp::Socket::handle_send(boost::shared_ptr<data_buffer> /*message*/,
            const boost::system::error_code& /*error*/, 
            std::size_t /*bytes_transferred*/)
{
}

/**
 *  Get remote peer ip and port in string "<ip>:<port>"
 */
std::string rtp::get_endpoint_str(udp::endpoint remote_endpoint_)
{
    std::string ip = remote_endpoint_.address().to_string();
    std::string port = std::to_string(remote_endpoint_.port());
    return ip + ":" + port;
}


rtp::Connection::Connection(udp::endpoint remote_endpoint_):
    remote_endpoint_(remote_endpoint_),
    dest_ip(remote_endpoint_.address().to_string()),
    dest_port(std::to_string(remote_endpoint_.port())),
    valid(false)
{
}

bool rtp::Connection::is_valid()
{
    return valid;
}

void rtp::Connection::set_valid(bool val)
{
    if(DEBUG) std::cerr << "Connection Created" << std::endl;
    valid = val;
}


int main(int argc, char* argv[])
{
    if (argc == 1)
    {
        std::cerr << "Not enough args" << std::endl;
        return 1;
    }
    boost::asio::io_service io_service_;

    if (std::string(argv[1]) == u8"server")
    {
        rtp::Socket socket(io_service_, u8"127.0.0.1", u8"4545");
    }
    else if (std::string(argv[1]) == u8"client")
    {
        rtp::Socket socket(io_service_, u8"127.0.0.1", u8"4546");
        socket.create_connection(u8"127.0.0.1", u8"4545");

    }
    io_service_.run();
    return 0;

}

The problem is most likely these lines:

if (std::string(argv[1]) == u8"server")
{
    rtp::Socket socket(io_service_, u8"127.0.0.1", u8"4545");
}
else if (std::string(argv[1]) == u8"client")
{
    rtp::Socket socket(io_service_, u8"127.0.0.1", u8"4546");
    socket.create_connection(u8"127.0.0.1", u8"4545");

}

Here in each of the if statement bodies you declare a variable socket , but that is local only within the scope of the if statement body. Once that scope end the variable will be destructed and exist no more.

The behavior you see it probably related to that issue, that the objects you create in those scopes gets destructed.

You need to create a socket whose lifetime is longer than any inner scope, and will last until io_service_.run() returns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM