【问题标题】:How to use a specfic Networkinterface/Ip with Boost Asio sockets?如何使用带有 Boost Asio 套接字的特定网络接口/IP?
【发布时间】:2014-01-20 08:36:33
【问题描述】:

我有一个 Debian/linux 服务器,它有几个 IP 地址,都分配给同一个物理网卡。 /etc/network/interfaces 配置文件如下所示(xx 代表数字)

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 176.xx.xx.144
    netmask 255.255.255.0
    network 176.xx.xx.0
    broadcast 176.xx.xx.255
    gateway 176.xx.xx.254

auto eth0:0
allow-hotplug eth0:0
iface eth0:0 inet static
    address 46.xx.xx.57
    netmask 255.255.255.255
    broadcast 46.xx.xx.57

auto eth0:1
allow-hotplug eth0:1
iface eth0:1 inet static
    address 94.xx.xx.166
    netmask 255.255.255.255
    broadcast 94.xx.xx.166

//IPv6 Stuff...

我正在开发一个使用 Boost Asio 来处理所有网络连接的客户端应用程序。在这个应用程序中,我希望能够使用特定的网络接口/IP 地址连接到外部服务器。我发现this 类似的问题,但是简单地将boost::asio::ip::tcp::socket 绑定到特定端点然后连接到外部服务器是行不通的。这是我尝试过的一个最小工作示例:

#include <iostream>
#include <boost/asio.hpp>

int main( int argC, char *argV[] ) {
    boost::asio::io_service ioService;
    boost::asio::ip::tcp::socket socket(ioService);

    boost::asio::ip::tcp::endpoint localEndpoint(
        boost::asio::ip::address::from_string("94.xx.xx.166"), 0);

    boost::asio::ip::tcp::resolver resolver(ioService);
    boost::asio::ip::tcp::resolver::iterator remoteEndpoint = 
        resolver.resolve(boost::asio::ip::tcp::resolver::query("haatschii.de", "80"));

    socket.open(boost::asio::ip::tcp::v4());

    std::cout   << "Before binding socket has local endpoint: " 
                << socket.local_endpoint().address().to_string() 
                << ":" << socket.local_endpoint().port() << std::endl;

    socket.bind(localEndpoint);

    std::cout   << "Before connecting socket has local endpoint: " 
                << socket.local_endpoint().address().to_string() 
                << ":" << socket.local_endpoint().port() << std::endl;

    boost::asio::connect(socket, remoteEndpoint);

    std::cout   << "After connecting socket has local endpoint: " 
                << socket.local_endpoint().address().to_string() 
                << ":" << socket.local_endpoint().port() << std::endl;

    //Test request to a page that echos our IP address.
    boost::asio::write(socket, 
        boost::asio::buffer("GET /ip.php HTTP/1.1\r\nHost: haatschii.de\r\nAccept: */*\r\n\r\n", 57));

    //Parse server response (not important for this code example)
    return 0;
}

当我在我的服务器上运行它时,我得到:

Before binding socket has local endpoint: 0.0.0.0:0
Before connecting socket has local endpoint: 94.xx.xx.166:38399
After connecting socket has local endpoint: 176.xx.xx.144:45959
External server says we are using IP: 176.xx.xx.144

现在我有点迷茫,因为我不知道还能尝试什么。我不一定需要一个可移植的解决方案,任何适用于这个 Debian 设置的东西都可以。

更新

我将为适合我的设置的解决方案提供奖励。如有必要,我可以更改 /etc/network/interfaces 配置文件。然而,为了重用我的代码,任何解决方案都必须使用 Boost Asio 套接字(至少作为包装器)。

【问题讨论】:

  • 链接问题中的基本方法似乎是正确的,但(我在那里问过)我从未使用过 lowest_layer() 函数,通常只使用 open()bind() 直接使用成员socket 对象的函数。
  • @Chad 那么open()-&gt;bind()-&gt;connect() 方法对您有用吗?我将在没有lowest_layer() 的情况下对其进行测试,但是我很难想象这是问题所在(请参阅其他问题)。或者您是否有一个有效的代码示例?
  • 你为什么要复制你的own question?不管怎样,看看Boost Asio examples
  • open() + bind() 似乎对我们有用。我主要处理接收端,通常处理UDP multicast。对于TCP,我们确保在盒子上正确设置了静态路由,但我知道如果我们bind() 到错误的接口,那么我们的TCP 连接将会失败。
  • @SteveVanOpstal,另一个问题是关于为什么某些特定方法不起作用,而不是关于实现此问题的规范方法。但是我决定将它们合并到这个中,因为你在某种程度上是对的,另一个不再有意义了。我知道 Boost Asio 示例,但我不知道他们如何回答我的问题...

标签: c++ linux sockets boost boost-asio


【解决方案1】:

要绑定到特定接口,您必须先打开连接。你做到了——到目前为止一切都很好。但在那之后你打电话给boost::asio::connect(socket, remoteEndpoint);,它将为你关闭连接(可以说是一种服务)。

Boost 告诉您它确实如此 - 但您必须仔细观察。在connect 重载版本的参数参考中,您正在使用它会说

参数

s

要连接的套接字。如果套接字已经打开,它将被关闭。

或在 boost/asio/impl/connect.hpp 中的实现:

// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

[...]

template <typename Protocol, typename SocketService,
    typename Iterator, typename ConnectCondition>
Iterator connect(basic_socket<Protocol, SocketService>& 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 = connect_condition(ec, iter);
    if (iter != end)
    {
      s.close(ec);
      s.connect(*iter, ec);
      if (!ec)
        return iter;
    }
  }

  if (!ec)
    ec = boost::asio::error::not_found;

  return end;
}

(注意s.close(ec);


解决方案

应该很简单。将boost::asio::connect... 替换为

socket.connect(*remoteEndpoint);

(或在各个远程端点上循环,类似于 boost 源代码,如有必要。)

【讨论】:

  • 就这么简单!谢谢
  • 我和这个问题的作者有完全相同的问题,但是boost::asio::ip::udp::socket。如果我绑定并连接套接字,我将无法通过绑定和连接的套接字接收数据。我没有检索异常,但我想知道我做错了什么。我知道 UDP 是无连接的,但是为什么 Boost.Asio API 提供这两种方法呢?
  • 如果我阅读了here 提供的答案,我希望使用bind 和connect 都应该以如下方式工作: 1. 绑定的套接字地址用于对[async]_receive 的所有调用。 2. 连接的套接字地址用于所有对[async]_send 的调用。因此,如果使用connect,则无需使用receive_fromsend_to 方法。我错过了什么吗?顺便说一句,我正在使用 Boost 1.49.0。
【解决方案2】:

通常,您可以使用以下工作流程:

void connect_handler(const boost::system::error_code& error)
{
  if (!error) { // Connect succeeded.
  }
}
...
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::ip::tcp::endpoint remote_endpoint(
    boost::asio::ip::address::from_string("1.2.3.4"), 12345); // server address
socket.open(boost::asio::ip::tcp::v4());
socket.bind(boost::asio::ip::tcp::endpoint(
    boost::asio::ip::address::from_string("1.2.3.55"), // your local address
    7777)
);
socket.async_connect(remote_endpoint, connect_handler);

更多信息可以在here找到。

【讨论】:

  • 感谢您的回答。也许我做错了什么,但这对我不起作用。我编辑了我的问题并添加了一个最小的代码示例。如果有什么问题,请告诉我。
  • 不适合我
  • 对不起,我弄错了cmets,这个方法效果很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-05
  • 2021-11-24
  • 2023-03-19
  • 2012-01-16
  • 2010-09-25
  • 2015-02-19
相关资源
最近更新 更多