[英]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'd 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昨天,在我的谷歌搜索中,我发现: Binding boost asio to local tcp endpoint
By performing the open, bind, and async_connect, I was able to bind to a specific network card and I started seeing traffic in Wireshark.通过执行 open、bind 和 async_connect,我能够绑定到特定的网卡,并且我开始在 Wireshark 中看到流量。
However, Wireshark reports that the socket has been given a random port rather than the one I specified.但是,Wireshark 报告套接字已被赋予一个随机端口,而不是我指定的端口。 I would think if the port was in use it would have filled out the error_code passed to the bind method.
我想如果端口正在使用它会填写传递给绑定方法的 error_code 。
What am I doing wrong?我做错了什么?
Here is my minimal example, extracted and edited from my real 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 selected rather than my requested port 6000.正如您在以下屏幕截图中看到的,选择了一个随机端口 32781,而不是我请求的端口 6000。
6000 is the remote endpoint port, and it is correctly used (otherwise, you wouldn't be connecting to the server side). 6000 是远程端点端口,它被正确使用(否则,您将无法连接到服务器端)。
From: https://idea.popcount.org/2014-04-03-bind-before-connect/来自: https : //idea.popcount.org/2014-04-03-bind-before-connect/
A TCP/IP connection is identified by a four element tuple: {source IP, source port, destination IP, destination port}.
TCP/IP 连接由四元素元组标识:{源 IP、源端口、目标 IP、目标端口}。 To establish a TCP/IP connection only a destination IP and port number are needed, the operating system automatically selects source IP and port.
要建立 TCP/IP 连接,只需要目标 IP 和端口号,操作系统会自动选择源 IP 和端口。
Since you do not bind to a local port, one is selected randomly from the "ephemeral port range".由于您没有绑定到本地端口,因此会从“临时端口范围”中随机选择一个。 This is, by far, the usual way to connect.
到目前为止,这是通常的连接方式。
Fear not:不要害怕:
It is possible to ask the kernel to select a specific source IP and port by calling
bind()
before callingconnect()
可以通过在调用
connect()
之前调用bind()
来要求内核选择特定的源 IP 和端口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.样本是python。
You do that, but still get another port.你这样做了,但仍然得到另一个端口。 It's likely that the hint port is not available.
提示端口可能不可用。
Check the information on SO_REUSEADDR
and SO_REUSEPORT
in the linked article查看链接文章中关于
SO_REUSEADDR
和SO_REUSEPORT
的信息
I doubt topic starter is still interested in this question, but for all of future seekers like myself, here is the solution.我怀疑话题启动者是否仍然对这个问题感兴趣,但对于像我这样的所有未来寻求者,这里是解决方案。
The issue here is that boost::asio::connect
closes the socket before calling connect
for every endpoint in the provided range:这里的问题是
boost::asio::connect
在为提供的范围内的每个端点调用connect
之前关闭套接字:
From boost/asio/impl/connect.hpp:来自 boost/asio/impl/connect.hpp:
template <typename Protocol BOOST_ASIO_SVC_TPARAM,
typename Iterator, typename ConnectCondition>
Iterator connect(basic_socket<Protocol BOOST_ASIO_SVC_TARG>& s,
Iterator begin, Iterator end, ConnectCondition connect_condition,
boost::system::error_code& ec)
{
ec = boost::system::error_code();
for (Iterator iter = begin; iter != end; ++iter)
{
iter = (detail::call_connect_condition(connect_condition, ec, iter, end));
if (iter != end)
{
s.close(ec); // <------
s.connect(*iter, ec);
if (!ec)
return iter;
}
...
}
That is why bound address is reset.这就是绑定地址被重置的原因。 To keep it bound one can use
socket.connect/async_connect(...)
directly为了保持绑定,可以直接使用
socket.connect/async_connect(...)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.