WSAConnectByList 函数 (winsock2.h)

WSAConnectByList 函数与可能终结点集合中的一个建立连接,这些终结点由一组目标地址表示 (主机名和端口) 。 此函数采用传递给它的所有目标地址和本地计算机的所有源地址,并在放弃之前尝试使用所有可能的地址组合进行连接。

此函数支持 IPv4 和 IPv6 地址。

语法

BOOL WSAConnectByList(
  [in]      SOCKET               s,
  [in]      PSOCKET_ADDRESS_LIST SocketAddress,
  [in, out] LPDWORD              LocalAddressLength,
  [out]     LPSOCKADDR           LocalAddress,
  [in, out] LPDWORD              RemoteAddressLength,
  [out]     LPSOCKADDR           RemoteAddress,
  [in]      const timeval        *timeout,
  [in]      LPWSAOVERLAPPED      Reserved
);

参数

[in] s

标识未绑定和未连接的套接字的描述符。 请注意,与其他 Winsock 调用(例如 WSAConnect) )建立连接 (不同, WSAConnectByList 函数需要未绑定的套接字。

[in] SocketAddress

指向 SOCKET_ADDRESS_LIST 结构的指针,该结构表示连接到对等方可能的目标地址和端口对。 应用程序负责在SOCKET_ADDRESS_LIST的每个SOCKET_ADDRESS结构中填写端口号。

[in, out] LocalAddressLength

输入时,指针指向调用方提供的 LocalAddress 缓冲区的大小(以字节为单位)。 输出时,一个指针,指向在成功完成调用后系统填充的 LocalAddress 缓冲区中存储的本地地址的 SOCKADDR 的大小(以字节为单位)。

[out] LocalAddress

指向接收连接的本地地址 的 SOCKADDR 结构的指针。 参数的大小正好是 LocalAddressLength 中返回的大小。 这是 由 getsockname 函数返回的相同信息。 此参数可以为 NULL,在这种情况下, 将忽略 LocalAddressLength 参数。

[in, out] RemoteAddressLength

输入时,指针指向调用方提供的 RemoteAddress 缓冲区的大小(以字节为单位)。 输出时,一个指针,指向系统在成功完成调用后填充的 RemoteAddress 缓冲区中存储的远程地址的 SOCKADDR 的大小(以字节为单位)。

[out] RemoteAddress

指向接收连接的远程地址 的 SOCKADDR 结构的指针。 这是 getpeername 函数将返回的相同信息。 此参数可以为 NULL,在这种情况下, 将忽略 RemoteAddressLength

[in] timeout

在中止调用之前等待远程应用程序响应的时间(以毫秒为单位)。 此参数可以为 NULL ,在这种情况下 ,WSAConnectByList 将在连接成功建立或尝试连接后完成,并且所有可能的本地远程地址对都失败。

[in] Reserved

保留以供将来实现。 此参数必须设置为 NULL

返回值

如果建立连接, WSAConnectByList 将返回 TRUE ,如果调用方提供了这些缓冲区,则 LocalAddressRemoteAddress 参数已填充。

如果调用失败,则返回 FALSE 。 然后,可以调用 WSAGetLastError 以获取扩展错误信息。

返回代码 说明
WSAEHOSTUNREACH
无法访问作为 nodename 参数传递的主机。
WSAEINVAL
向该函数传递了无效参数。 Reserved 参数必须为 NULL
WSAENOBUFS
无法分配足够的内存。
WSAENOTSOCK
传递给函数的套接字无效。 s 参数不得INVALID_SOCKETNULL
WSAETIMEDOUT
在超过 超时 参数之前,未收到来自远程应用程序的响应。

注解

WSAConnectByList 类似于 WSAConnectByName 函数。 WSAConnectByList 不采用端口) (单个主机名和服务名称,而是 (主机地址和端口) 并连接到其中一个地址。 WSAConnectByList 函数旨在支持应用程序需要连接到潜在节点列表中任何可用节点的对等协作方案。 WSAConnectByList 与 IPv6 和 IPv4 版本兼容。

由地址列表表示的一组可能的目标由调用方提供。 WSAConnectByList 不仅仅是尝试连接到多个目标地址之一。 具体而言,该函数采用调用方传递的所有远程地址、所有本地地址,然后首先尝试使用成功几率最高的地址对进行连接。 因此, WSAConnectByList 不仅可确保在连接可能的情况下建立连接,还可以最大程度地减少建立连接的时间。

调用方可以指定 LocalAddressRemoteAddress 缓冲区和长度,以确定成功建立连接的本地地址和远程地址。

timeout 参数允许调用方限制函数建立连接所花费的时间。 在内部, WSAConnectByList (连接尝试) 执行多个操作。 在每个操作之间,检查 timeout 参数以查看是否已超过 超时 ,如果是,则中止调用。 请注意,一旦超过 超时 , (连接) 的单个操作将不会中断,因此 WSAConnectByList 调用可能需要比 timeout 参数中指定的值更长的时间超时。

WSAConnectByList 有限制:它仅适用于面向连接的套接字,例如SOCK_STREAM类型的套接字。 函数不支持重叠 I/O 或非阻塞行为。 即使套接字处于非阻止模式,WSAConnectByList 也会阻止。 WSAConnectByList 将尝试将 (一个) 连接到调用方提供的各种地址。 其中每个连接尝试都可能会失败,并显示不同的错误代码。 由于只能返回一个错误代码,因此返回的值是上次连接尝试中的错误代码。

若要在函数接受的单个地址列表中同时传递 IPv6 和 IPv4 地址,必须在调用函数之前执行以下步骤:

  • 必须在为AF_INET6地址系列创建的套接字上调用 setsockopt 函数,以便在调用 WSAConnectByList 之前禁用IPV6_V6ONLY套接字选项。 这是通过在将级别参数设置为 IPPROTO_IPV6 的套接字上调用 setockopt 函数来实现的, (请参阅IPPROTO_IPV6 Socket Options) ,optname 参数设置为 IPV6_V6ONLYoptvalue 参数值设置为零 。
  • 任何 IPv4 地址都必须以 IPv4 映射的 IPv6 地址格式表示,这允许仅 IPv6 应用程序与 IPv4 节点通信。 IPv4 映射的 IPv6 地址格式允许将 IPv4 节点的 IPv4 地址表示为 IPv6 地址。 IPv4 地址编码为 IPv6 地址的低序 32 位,高序 96 位包含固定前缀 0:0:0:0:0:FFFF。 RFC 4291 中指定了 IPv4 映射的 IPv6 地址格式。 有关详细信息,请参阅 www.ietf.org/rfc/rfc4291.txtMstcpip.h 中的 IN6ADDR_SETV4MAPPED 宏可用于将 IPv4 地址转换为所需的 IPv4 映射 IPv6 地址格式。

SocketAddressList 参数中传递的指针数组指向SOCKET_ADDRESS结构的数组,这是一种泛型数据类型。 RemoteAddressLocalAddress 参数也指向 SOCKADDR 结构。 调用 WSAConnectByList 时,预期将在这些参数中传递特定于网络协议或地址系列的套接字地址类型。 因此,对于 IPv4 地址,当作为参数传递时,指向 sockaddr_in 结构的指针将转换为 指向 SOCKADDR 的指针。 对于 IPv6 地址,当作为参数传递时,指向 sockaddr_in6 结构的指针将转换为 指向 SOCKADDR 的指针。 SocketAddressList 参数可以包含指向 IPv4 和 IPv6 地址混合的指针。 因此,一些 SOCKET_ADDRESS 指针可以 指向sockaddr_in 结构,而其他指针可以 用于sockaddr_in6 结构。 如果预期可以使用 IPv6 地址,则 RemoteAddressLocalAddress 参数应指向 sockaddr_in6 结构,并强制转换为 SOCKADDR 结构。 RemoteAddressLengthLocalAddressLength 参数必须表示这些较大结构的长度。

WSAConnectByList 函数返回 TRUE,套接字将 处于已连接套接字的默认状态。 在套接字上设置SO_UPDATE_CONNECT_CONTEXT之前,套接字 不会启用以前设置的属性或选项。 使用 setsockopt 函数设置 SO_UPDATE_CONNECT_CONTEXT 选项。

例如:

//Need to #include <mswsock.h> for SO_UPDATE_CONNECT_CONTEXT

int iResult = 0;

iResult = setsockopt( s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0 );

注意 当发出阻止的 Winsock 调用(例如 WSAConnectByList超时 参数设置为 NULL)时,Winsock 可能需要等待网络事件,然后才能完成调用。 在这种情况下,Winsock 执行可发出警报的等待, (在同一线程上计划的 APC) 异步过程调用可能会中断。 在 APC 内发出另一个阻止 Winsock 调用,该调用中断了同一线程上正在进行的阻止 Winsock 调用将导致未定义的行为,并且 Winsock 客户端绝不能尝试。
 
Windows Phone 8:Windows Phone 8 及更高版本上的 Windows Phone 应用商店应用支持此函数。

Windows 8.1Windows Server 2012 R2:Windows 8.1、Windows Server 2012 R2 及更高版本的 Windows 应用商店应用支持此功能。

示例

使用 WSAConnectByList 建立连接。

#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>

// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")


SOCKET
OpenAndConnect(SOCKET_ADDRESS_LIST *AddressList) 
{
    SOCKET ConnSocket = INVALID_SOCKET;

    int ipv6only = 0;
    int iResult;
    BOOL bSuccess;

    SOCKADDR_STORAGE LocalAddr = {0};
    SOCKADDR_STORAGE RemoteAddr = {0};

    DWORD dwLocalAddr = sizeof(LocalAddr);
    DWORD dwRemoteAddr = sizeof(RemoteAddr);

    ConnSocket = socket(AF_INET6, SOCK_STREAM, 0);
    if (ConnSocket == INVALID_SOCKET){
        return INVALID_SOCKET;
    }

    iResult = setsockopt(ConnSocket, IPPROTO_IPV6,
        IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only) );
    if (iResult == SOCKET_ERROR){
        closesocket(ConnSocket);
        return INVALID_SOCKET;       
    }

    // AddressList may contain IPv6 and/or IPv4Mapped addresses
    bSuccess = WSAConnectByList(ConnSocket,
            AddressList,
            &dwLocalAddr,
            (SOCKADDR*)&LocalAddr,
            &dwRemoteAddr,
            (SOCKADDR*)&RemoteAddr,
            NULL,
            NULL);
    if (bSuccess){
        return ConnSocket;
    } else {
        return INVALID_SOCKET;
    }
}

要求

   
最低受支持的客户端 Windows 8.1,Windows Vista [桌面应用 |UWP 应用]
最低受支持的服务器 Windows Server 2003 [桌面应用 | UWP 应用]
目标平台 Windows
标头 winsock2.h
Library Ws2_32.lib
DLL Ws2_32.dll

另请参阅

IPPROTO_IPV6套接字选项

SOCKADDR

SOCKET_ADDRESS

SOCKET_ADDRESS_LIST

WSAConnect

WSAConnectByName

WSAGetLastError

getaddrinfo

getpeername

getsockname

setsockopt