在Windows中使用套接字的特定网络接口

问题描述 投票:0回答:3

除了更改路由表之外,Windows 中是否有可靠的方法来强制新创建的套接字使用特定的网络接口?据我了解,接口 IP 地址的

bind()
并不能保证这一点。

windows networking binding sockets bind
3个回答
20
投票

(好吧,第二次幸运..)

仅供参考,这里还有另一个问题在特定网络适配器上执行 connect() 沿着同样的路线...

王牌接线员

Windows XP 和 Windows Server® 2003 使用弱主机模型进行发送和 所有 IPv4 接口接收和 用于发送和的强大主机模型 所有 IPv6 接口接收。你 无法配置此行为。这 下一代 TCP/IP 堆栈 Windows Vista 和 Windows Server 2008 支持强大的主机发送和 IPv4 和 IPv6 均通过以下方式接收 除了以下所有接口上的默认值 Teredo 隧道接口 Teredo 主机特定中继。

所以回答你的问题(正确地,这次)在 Windows XP 和 Windows Server 2003 IP4 中不是,但对于 IP6 是。对于 Windows Vista 和 Windows 2008 是的(某些情况除外)。

也来自 https://forums.codeguru.com/showthread.php?487139-Socket-binding-with-routing-table

在Windows上,对bind()的调用会影响 仅选择传入流量的卡, 不是传出流量。因此,在一个 在多宿主系统中运行的客户端 (即,多个接口卡), 这是选择的网络堆栈 要使用的卡,并且它使其 选择仅基于 目标 IP,这又基于 在路由表上。调用bind() 不会影响卡的选择 以任何方式。

这有关系 所谓的“弱端系统” (“弱 E/S”)模型。 Vista 更改为 强大的 E/S 模型,因此问题可能 Vista下不会出现。但一切先于 Windows 版本使用弱 E/S 型号。

对于弱 E/S 模型, 路由表决定哪张卡 用于传出流量 多宿主系统。

看看这些线程是否提供了一些 见解:

“多宿主上的本地套接字绑定 Windows XP 中的主机无法工作” http://www.codeguru.com/forum/showthread.php?t=452337

《如何将一个端口连接到指定的端口》 网卡?”位于 http://www.codeguru.com/forum/showthread.php?t=451117。 这个线程提到了 CreateIpForwardEntry() 函数,其中 (我认为)可以用来创建一个 路由表中的条目,以便所有 具有指定的传出 IP 流量 服务器通过指定的路由 适配器。

“使用 2 个以太网卡”,位于 http://www.codeguru.com/forum/showthread.php?t=448863

“多宿主上的奇怪绑定行为 系统”在 http://www.codeguru.com/forum/showthread.php?t=452368

希望有帮助!


1
投票

我不确定你为什么说绑定工作不可靠。当然,我没有进行详尽的测试,但以下解决方案对我有用(Win10、Visual Studio 2019)。我需要通过特定的 NIC 发送广播消息,其中计算机上可能存在多个 NIC。在下面的代码片段中,我希望广播消息在 IP 为 .202.106 的 NIC 上发出。

总结:

  • 创建套接字
  • 使用您要发送的 NIC 的 IP 地址创建 sockaddr_in 地址
  • 将套接字绑定到 FROM sockaddr_in
  • 使用您的广播地址(255.255.255.255)的IP创建另一个sockaddr_in
  • 做一个sendto,传递第1步创建的socket,以及广播地址的sockaddr。

`

static WSADATA     wsaData;

static int ServoSendPort = 8888;
static char ServoSendNetwork[] = "192.168.202.106";
static char ServoSendBroadcast[] = "192.168.255.255";

` ... < snip >

if ( WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR )
      return false;


// Make a UDP socket
SOCKET ServoSendSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
int iOptVal = TRUE;
int iOptLen = sizeof(int);
int RetVal = setsockopt(ServoSendSocket, SOL_SOCKET, SO_BROADCAST, (char*)&iOptVal, iOptLen);
              
// Bind it to a particular interface
sockaddr_in ServoBindAddr={0};
ServoBindAddr.sin_family = AF_INET;
ServoBindAddr.sin_addr.s_addr = inet_addr( ServoSendNetwork ); // target NIC
ServoBindAddr.sin_port = htons( ServoSendPort );
int bindRetVal = bind( ServoSendSocket, (sockaddr*) &ServoBindAddr, sizeof(ServoBindAddr) );
if (bindRetVal == SOCKET_ERROR )
{
    int ErrorCode = WSAGetLastError();
    CString errMsg;
    errMsg.Format (  _T("rats!  bind() didn't work!  Error code %d\n"), ErrorCode );
    OutputDebugString( errMsg );
}


// now create the address to send to...
sockaddr_in ServoSendAddr={0};
ServoSendAddr.sin_family = AF_INET;
ServoSendAddr.sin_addr.s_addr = inet_addr( ServoSendBroadcast ); // 
ServoSendAddr.sin_port = htons( ServoSendPort );

...

#define NUM_BYTES_SERVO_SEND 20
unsigned char sendBuf[NUM_BYTES_SERVO_SEND];
int BufLen = NUM_BYTES_SERVO_SEND;

ServoSocketStatus = sendto(ServoSendSocket, (char*)sendBuf, BufLen, 0, (SOCKADDR *) &ServoSendAddr, sizeof(ServoSendAddr));
if(ServoSocketStatus == SOCKET_ERROR)
{
    ServoUdpSendBytes = WSAGetLastError();
    CString message;
    message.Format(_T("Error transmitting UDP message to Servo Controller: %d."), ServoSocketStatus);
    OutputDebugString(message);
    return false;
 }

0
投票

https://learn.microsoft.com/en-us/windows/win32/winsock/sol-socket-socket-options

看这个表情:

SO_DONTROUTE

表示传出数据应在套接字绑定的任何接口上发送,而不是在其他接口上路由。该选项仅对面向消息的协议有效。 Microsoft 提供商默默地忽略此选项,并始终查阅路由表以查找适当的传出接口。

那么windows就不行了???

© www.soinside.com 2019 - 2024. All rights reserved.