Boost :: asio :: ip :: tcp :: socket - how to bind to a specific local port

I am making a client socket.

To make things easier for my testers, I would like to specify the network card and port that the socket will use.

Yesterday, in my google search, I found: Binding boost asio to local tcp endpoint

By doing the open, bind and async_connect function, I was able to bind to a specific NIC and I started seeing traffic in Wireshark.

However, Wireshark reports that the socket is assigned a random port, not the one I specified. I would have thought that if the port was in use it would fill in the error_code passed to the binding method.

What am I doing wrong?

Here is my minimal example, extracted and edited from my real-world solution.

// Boost Includes
#include <boost/asio.hpp>
#include <boost/atomic.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/thread/condition_variable.hpp>

// Standard Includes
#include <exception>
#include <memory>
#include <string>
#include <sstream>


boost::asio::io_service        g_ioService;                     /** ASIO sockets require an io_service to run on*/
boost::thread                  g_thread;                        /** thread that will run the io_service and hence where callbacks are called*/
boost::asio::ip::tcp::socket   g_socket(g_ioService);           /** Aync socket*/
boost::asio::ip::tcp::resolver g_resolver(g_ioService);         /** Resolves IP Addresses*/

//--------------------------------------------------------------------------------------------------
void OnConnect(const boost::system::error_code & errorCode, boost::asio::ip::tcp::resolver::iterator endpoint)
{
    if (errorCode || endpoint == boost::asio::ip::tcp::resolver::iterator())
    {
        // Error - An error occured while attempting to connect
        throw std::runtime_error("An error occured while attempting to connect");
    }

    // We connected to an endpoint

    /*
    // Start reading from the socket
    auto callback = boost::bind(OnReceive, boost::asio::placeholders::error);
    boost::asio::async_read_until(g_socket, m_receiveBuffer, '\n', callback);
    */
}


//--------------------------------------------------------------------------------------------------
void Connect()
{
    const std::string hostName = "10.84.0.36";
    const unsigned int port = 1007;

    // Resolve to translate the server machine name into a list of endpoints
    std::ostringstream converter;
    converter << port;
    const std::string portAsString = converter.str();

    boost::asio::ip::tcp::resolver::query query(hostName, portAsString);

    boost::system::error_code errorCode;
    boost::asio::ip::tcp::resolver::iterator itEnd;
    boost::asio::ip::tcp::resolver::iterator itEndpoint = g_resolver.resolve(query, errorCode);

    if (errorCode || itEndpoint == itEnd)
    {
        // Error - Could not resolve either machine
        throw std::runtime_error("Could not resolve either machine");
    }

    g_socket.open(boost::asio::ip::tcp::v4(), errorCode);
    if (errorCode)
    {
        // Could open the g_socket
        throw std::runtime_error("Could open the g_socket");
    }

    boost::asio::ip::tcp::endpoint localEndpoint(boost::asio::ip::address::from_string("10.86.0.18"), 6000);
    g_socket.bind(localEndpoint, errorCode);
    if (errorCode)
    {
        // Could bind the g_socket to local endpoint
        throw std::runtime_error("Could bind the socket to local endpoint");
    }

    // Attempt to asynchronously connect using each possible end point until we find one that works
    boost::asio::async_connect(g_socket, itEndpoint, boost::bind(OnConnect, boost::asio::placeholders::error, boost::asio::placeholders::iterator));
}

//--------------------------------------------------------------------------------------------------
void g_ioServiceg_threadProc()
{
    try
    {
        // Connect to the server
        Connect();

        // Run the asynchronous callbacks from the g_socket on this thread
        // Until the io_service is stopped from another thread
        g_ioService.run();
    }
    catch (...)
    {
        throw std::runtime_error("unhandled exception caught from io_service g_thread");
    }
}

//--------------------------------------------------------------------------------------------------
int main()
{
    // Start up the IO service thread
    g_thread.swap(boost::thread(g_ioServiceg_threadProc));

    // Hang out awhile
    boost::this_thread::sleep_for(boost::chrono::seconds(60));

    // Stop the io service and allow the g_thread to exit
    // This will cancel any outstanding work on the io_service
    g_ioService.stop();

    // Join our g_thread
    if (g_thread.joinable())
    {
        g_thread.join();
    }

    return true;
}

      

As you can see in the following screenshot, a random port 32781 was chosen, not my requested port 6000.

Wireshark Screen Shot

+3


source to share


1 answer


6000 is the port of the remote endpoint and it is being used correctly (otherwise you won't be connecting to the server side).

From: https://idea.popcount.org/2014-04-03-bind-before-connect/

A TCP / IP connection is identified by four tuples: {source IP address, source port, destination IP address, destination port}. Only the destination IP address and port number are needed to establish a TCP / IP connection, the operating system will automatically select the source IP address and port.

Since you are not binding to a local port, one of them is chosen at random from the "ephemeral port range". This is definitely the usual way to connect.

Do not be afraid:



It is possible to ask the kernel to select a specific source IP and port by calling bind()

before callingconnect()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Let the source address be 192.168.1.21:1234
s.bind(("192.168.1.21", 1234))
s.connect(("www.google.com", 80))

      

The sample is python.

You do this, but you still get a different port. The hint port is probably not available.

Check information about SO_REUSEADDR

and SO_REUSEPORT

in the linked article

+1


source







All Articles