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

发布于 2024-08-18 01:21:54 字数 98 浏览 8 评论 0原文

除了更改路由表之外,Windows 中是否有可靠的方法来强制新创建的套接字使用特定的网络接口?据我了解,bind() 到接口的 IP 地址并不能保证这一点。

Is there a reliable way in Windows, apart from changing the routing table, to force a newly created socket to use a specific network interface? I understand that bind() to the interface's IP address does not guarantee this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

朱染 2024-08-25 01:21:54

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

仅供参考,这里还有另一个问题 在特定网络上执行 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-套接字绑定与路由表

在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

希望有帮助!

(Ok second time lucky..)

FYI there's another question here perform connect() on specific network adapter along the same lines...

According to The Cable Guy

Windows XP and Windows Server® 2003
use the weak host model for sends and
receives for all IPv4 interfaces and
the strong host model for sends and
receives for all IPv6 interfaces. You
cannot configure this behavior. The
Next Generation TCP/IP stack in
Windows Vista and Windows Server 2008
supports strong host sends and
receives for both IPv4 and IPv6 by
default on all interfaces except the
Teredo tunneling interface for a
Teredo host-specific relay.

So to answer your question (properly, this time) in Windows XP and Windows Server 2003 IP4 no, but for IP6 yes. And for Windows Vista and Windows 2008 yes (except for certain circumstances).

Also from https://forums.codeguru.com/showthread.php?487139-Socket-binding-with-routing-table

On Windows, a call to bind() affects
card selection only incoming traffic,
not outgoing traffic. Thus, on a
client running in a multi-homed system
(i.e., more than one interface card),
it's the network stack that selects
the card to use, and it makes its
selection based solely on the
destination IP, which in turn is based
on the routing table. A call to bind()
will not affect the choice of the card
in any way.

It's got something to do with
something called a "Weak End System"
("Weak E/S") model. Vista changed to a
strong E/S model, so the issue might
not arise under Vista
. But all prior
versions of Windows used the weak E/S
model.

With a weak E/S model, it's the
routing table that decides which card
is used for outgoing traffic in a
multihomed system.

See if these threads offer some
insight:

"Local socket binding on multihomed
host in Windows XP does not work" at
http://www.codeguru.com/forum/showthread.php?t=452337

"How to connect a port to a specified
Networkcard?" at
http://www.codeguru.com/forum/showthread.php?t=451117.
This thread mentions the
CreateIpForwardEntry() function, which
(I think) can be used to create an
entry in the routing table so that all
outgoing IP traffic with a specified
server is routed via a specified
adapter.

"Working with 2 Ethernet cards" at
http://www.codeguru.com/forum/showthread.php?t=448863

"Strange bind behavior on multihomed
system" at
http://www.codeguru.com/forum/showthread.php?t=452368

Hope that helps!

献世佛 2024-08-25 01:21:54

我不知道你为什么说绑定工作不可靠。当然,我没有进行详尽的测试,但以下解决方案对我有用(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,传递创建的socket是第1步,广播地址的sockaddr。

`

static WSADATA     wsaData;

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

`
...<剪断>

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;
 }

I'm not sure why you say bind is not working reliably. Granted I have not done exhaustive testing, but the following solution worked for me (Win10, Visual Studio 2019). I needed to send a broadcast message via a particular NIC, where multiple NICs might be present on a computer. In the snippet below, I want the broadcast message to go out on the NIC with IP of .202.106.

In summary:

  • create a socket
  • create a sockaddr_in address with the IP address of the NIC you want to send FROM
  • bind the socket to that FROM sockaddr_in
  • create another sockaddr_in with the IP of your broadcast address (255.255.255.255)
  • do a sendto, passing the socket created is step 1, and the sockaddr of the broadcast address.

`

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;
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文