Windows ConnectEx要求插座为;最初绑定的“;,但是为了什么?

Windows ConnectEx要求插座为;最初绑定的“;,但是为了什么?,windows,network-programming,winsock,overlapped-io,Windows,Network Programming,Winsock,Overlapped Io,该函数需要一个“未连接、先前绑定的套接字”。事实上,如果我在我的示例中省略了这个步骤(见下文),那么将失败 以下是我目前的理解:在调用之前,将套接字连接到INADDR\u ANY和端口0(除非已经绑定): 或对于IPv6套接字: struct sockaddr_in6 addr; ZeroMemory(&addr, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_por

该函数需要一个“未连接、先前绑定的套接字”。事实上,如果我在我的示例中省略了这个步骤(见下文),那么将失败

以下是我目前的理解:在调用之前,将套接字连接到
INADDR\u ANY
和端口0(除非已经绑定):

或对于IPv6套接字:

struct sockaddr_in6 addr;
ZeroMemory(&addr, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any;
addr.sin6_port = 0;
rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
if (rc != 0) { ... bind failed; call WSAGetLastError to see why ... }
这允许操作系统为我们的套接字分配一个本地地址(与我们连接的远程地址相反)。自动执行此步骤,但不执行

我的问题是:

  • 我的评估正确吗

  • 是否有一种与地址族无关的自动绑定方法,或者我必须手动处理
    AF_INET
    AF_INET6
    AF_BTH
    (蓝牙)等

  • 使用ConnectEx示例(另请参见要点:):

    #包括
    #包括
    #包括
    #包括
    #pragma注释(lib,“Ws2_32.lib”)
    结构mswsock\u s{
    LPFN_CONNECTEX CONNECTEX;
    }mswsock;
    静态BOOL load_mswsock(无效)
    {
    插座;
    双字双字节;
    int rc;
    /*WSAIoctl需要虚拟套接字*/
    sock=socket(AF\u INET,sock\u STREAM,0);
    if(sock==无效的_套接字)
    返回FALSE;
    {
    GUID=WSAID_CONNECTEX;
    rc=WSAIoctl(sock,SIO_GET_EXTENSION_函数_指针,
    &guid,sizeof(guid),
    &mswsock.ConnectEx,sizeof(mswsock.ConnectEx),
    &dwBytes,NULL,NULL);
    如果(rc!=0)
    返回FALSE;
    }
    rc=闭合插座(插座);
    如果(rc!=0)
    返回FALSE;
    返回TRUE;
    }
    int main(int argc,char*argv[])
    {
    int rc;
    布尔ok;
    WSADATA WSADATA;
    插座;
    rc=WSAStartup(MAKEWORD(2,2)和wsaData);
    如果(rc!=0){
    printf(“WSAStartup失败:%d\n”,rc);
    返回1;
    }
    如果(LOBYTE(wsaData.wVersion)!=2 | | HIBYTE(wsaData.wVersion)!=2){
    printf(“您的计算机来自错误的千年。\n”);
    WSACleanup();
    返回1;
    }
    如果(!load_mswsock()){
    printf(“加载mswsock函数时出错:%d\n”,WSAGetLastError());
    返回1;
    }
    sock=socket(AF\u INET,sock\u STREAM,0);
    if(sock==无效的_套接字){
    printf(“套接字:%d\n”,WSAGetLastError());
    返回1;
    }
    /*ConnectEx要求最初绑定套接字*/
    {
    地址中的结构sockaddr\u;
    零内存(&addr,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin\u addr.s\u addr=INADDR\u ANY;
    地址sin_端口=0;
    rc=bind(sock,(SOCKADDR*)和addr,sizeof(addr));
    如果(rc!=0){
    printf(“绑定失败:%d\n”,WSAGetLastError());
    返回1;
    }
    }
    /*发出ConnectEx并等待操作完成*/
    {
    重叠ol;
    零内存(&ol,sizeof(ol));
    地址中的sockaddr_;
    零内存(&addr,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=inet_addr(“173.194.37.36”);//google.com
    地址sin_端口=htons(80);
    ok=mswsock.ConnectEx(sock,(SOCKADDR*)&addr,sizeof(addr),NULL,0,NULL,&ol);
    如果(确定){
    printf(“ConnectEx立即成功\n”);
    }else if(WSAGetLastError()==错误\u IO\u挂起){
    printf(“ConnectEx挂起”);
    德沃德·努比特斯;
    ok=GetOverlappedResult((句柄)sock、&ol、&numBytes,TRUE);
    如果(确定)
    printf(“ConnectEx成功\n”);
    其他的
    printf(“ConnectEx失败:%d\n”,WSAGetLastError());
    }否则{
    printf(“ConnectEx失败:%d\n”,WSAGetLastError());
    返回1;
    }
    }
    /*使套接字的性能更好*/
    rc=setsockopt(sock,SOL_SOCKET,SO_UPDATE_CONNECT_CONTEXT,NULL,0);
    如果(rc!=0){
    printf(“SO_更新_连接_上下文失败:%d\n”,WSAGetLastError());
    返回1;
    }
    /*如果未执行SO\u更新\u连接\u上下文,则此操作将失败*/
    rc=停机(sock和SD_均为);
    如果(rc!=0){
    printf(“关闭失败:%d\n”,WSAGetLastError());
    返回1;
    }
    printf(“完成”\n);
    返回0;
    }
    
    connect会自动执行此步骤,但ConnectEx不会

    我的评估正确吗

    有没有一种方法可以实现与地址族无关的自动绑定,或者我必须手动处理AF_INET、AF_INET6、AF_BTH(蓝牙)等

    我相信
    INADDR\u ANY
    在所有地址族中都是一堆零,因此您可以尝试使用
    memset()
    并完全忽略对
    addr.sin\u addr.s\u addr的赋值。这是否符合犹太教、便于携带、政治正确等是另一个我不想讨论的问题


    考虑到保存系统调用是其存在的动机,微软没有设法在内部调用
    ConnectEx()
    call
    bind()
    ,这似乎很奇怪,同时考虑到大多数程序根本不会绑定出站套接字。

    可以通过独立于地址族的方式获取ConnectEx的绑定地址

    解决方案1

    使用以下选项调用
    getaddrinfo

    pServiceName = "0"
    
    hints.ai_flags = AI_PASSIVE
    hints.ai_family = address family of the socket
    
    然后使用返回的地址列表的第一个结果

    要获取套接字的地址族,可以将
    getsockopt
    SO\u PROTOCOL\u INFOW
    一起使用

    解决方案2

    对地址结构使用
    SOCKADDR\u存储
    ,并调用
    MSTcpIP.h
    中定义的
    INETADDR\u SETANY
    。它支持
    AF\u INET
    AF\u INET6

    #include <stdio.h>
    #include <WinSock2.h>
    #include <MSWSock.h>
    #include <WS2tcpip.h>
    
    #pragma comment(lib, "Ws2_32.lib")
    
    struct mswsock_s {
        LPFN_CONNECTEX ConnectEx;
    } mswsock;
    
    static BOOL load_mswsock(void)
    {
        SOCKET sock;
        DWORD dwBytes;
        int rc;
    
        /* Dummy socket needed for WSAIoctl */
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == INVALID_SOCKET)
            return FALSE;
    
        {
            GUID guid = WSAID_CONNECTEX;
            rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
                          &guid, sizeof(guid),
                          &mswsock.ConnectEx, sizeof(mswsock.ConnectEx),
                          &dwBytes, NULL, NULL);
            if (rc != 0)
                return FALSE;
        }
    
        rc = closesocket(sock);
        if (rc != 0)
            return FALSE;
    
        return TRUE;
    }
    
    int main(int argc, char *argv[])
    {
        int rc;
        BOOL ok;
        WSADATA wsaData;
        SOCKET sock;
    
        rc = WSAStartup(MAKEWORD(2,2), &wsaData);
        if (rc != 0) {
            printf("WSAStartup failed: %d\n", rc);
            return 1;
        }
        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
            printf("Your computer is from the wrong millenium.\n");
            WSACleanup();
            return 1;
        }
    
        if (!load_mswsock()) {
            printf("Error loading mswsock functions: %d\n", WSAGetLastError());
            return 1;
        }
    
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == INVALID_SOCKET) {
            printf("socket: %d\n", WSAGetLastError());
            return 1;
        }
    
        /* ConnectEx requires the socket to be initially bound. */
        {
            struct sockaddr_in addr;
            ZeroMemory(&addr, sizeof(addr));
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = INADDR_ANY;
            addr.sin_port = 0;
            rc = bind(sock, (SOCKADDR*) &addr, sizeof(addr));
            if (rc != 0) {
                printf("bind failed: %d\n", WSAGetLastError());
                return 1;
            }
        }
    
        /* Issue ConnectEx and wait for the operation to complete. */
        {
            OVERLAPPED ol;
            ZeroMemory(&ol, sizeof(ol));
    
            sockaddr_in addr;
            ZeroMemory(&addr, sizeof(addr));
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = inet_addr("173.194.37.36"); // google.com
            addr.sin_port = htons(80);
    
            ok = mswsock.ConnectEx(sock, (SOCKADDR*) &addr, sizeof(addr), NULL, 0, NULL, &ol);
            if (ok) {
                printf("ConnectEx succeeded immediately\n");
            } else if (WSAGetLastError() == ERROR_IO_PENDING) {
                printf("ConnectEx pending\n");
    
                DWORD numBytes;
                ok = GetOverlappedResult((HANDLE) sock, &ol, &numBytes, TRUE);
                if (ok)
                    printf("ConnectEx succeeded\n");
                else
                    printf("ConnectEx failed: %d\n", WSAGetLastError());
            } else {
                printf("ConnectEx failed: %d\n", WSAGetLastError());
                return 1;
            }
        }
    
        /* Make the socket more well-behaved. */
        rc = setsockopt(sock, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
        if (rc != 0) {
            printf("SO_UPDATE_CONNECT_CONTEXT failed: %d\n", WSAGetLastError());
            return 1;
        }
    
        /* This will fail if SO_UPDATE_CONNECT_CONTEXT was not performed. */
        rc = shutdown(sock, SD_BOTH);
        if (rc != 0) {
            printf("shutdown failed: %d\n", WSAGetLastError());
            return 1;
        }
    
        printf("Done\n");
        return 0;
    }
    
    pServiceName = "0"
    
    hints.ai_flags = AI_PASSIVE
    hints.ai_family = address family of the socket