Delen via


Functieoproepen voor IPv6 Winsock-toepassingen

Nieuwe functies zijn geïntroduceerd in de Windows Sockets-interface die speciaal is ontworpen om Het programmeren van Windows Sockets eenvoudiger te maken. Een van de voordelen van deze nieuwe Windows Sockets-functies is geïntegreerde ondersteuning voor zowel IPv6 als IPv4.

Deze nieuwe Windows Sockets-functies zijn onder andere:

Daarnaast zijn nieuwe IP Helper-functies met ondersteuning voor zowel IPv6 als IPv4 toegevoegd om het programmeren van Windows Sockets te vereenvoudigen. Deze nieuwe IP Helper-functies zijn onder andere:

Bij het toevoegen van IPv6-ondersteuning aan een toepassing moeten de volgende richtlijnen worden gebruikt:

  • Gebruik WSAConnectByName om een verbinding tot stand te brengen met een eindpunt op basis van een hostnaam en poort. De functie WSAConnectByName is beschikbaar op Windows Vista en hoger.
  • Gebruik WSAConnectByList om een verbinding tot stand te brengen met een van de mogelijke eindpunten die worden vertegenwoordigd door een set doeladressen (hostnamen en poorten). De functie WSAConnectByList is beschikbaar op Windows Vista en hoger.
  • Vervang gethostbyname functieaanroepen door aanroepen naar een van de nieuwe getaddrinfo Windows Sockets-functies. De getaddrinfo functie met ondersteuning voor het IPv6-protocol is beschikbaar in Windows XP en hoger. Het IPv6-protocol wordt ook ondersteund in Windows 2000 wanneer de IPv6 Technology Preview voor Windows 2000 is geïnstalleerd.
  • Vervang gethostbyaddr functieaanroepen door aanroepen naar een van de nieuwe functies getnameinfo Windows Sockets. De getnameinfo functie met ondersteuning voor het IPv6-protocol is beschikbaar in Windows XP en hoger. Het IPv6-protocol wordt ook ondersteund in Windows 2000 wanneer de IPv6 Technology Preview voor Windows 2000 is geïnstalleerd.

WSAConnectByName

De WSAConnectByName functie vereenvoudigt het maken van verbinding met een eindpunt met behulp van een streamgebaseerde socket op basis van de hostnaam of het IP-adres van de bestemming (IPv4 of IPv6). Deze functie vermindert de broncode die nodig is om een IP-toepassing te maken die agnostisch is voor de versie van het gebruikte IP-protocol. WSAConnectByName vervangt de volgende stappen in een typische TCP-toepassing in één functieoproep:

  • Een hostnaam omzetten in een set IP-adressen.
  • Voor elk IP-adres:
    • Maak een socket van de juiste adresfamilie.
    • Probeert verbinding te maken met het externe IP-adres. Als de verbinding is geslaagd, wordt deze geretourneerd; anders wordt het volgende externe IP-adres voor de host geprobeerd.

De functie WSAConnectByName gaat verder dan alleen het omzetten van de naam en het maken van verbinding. De functie gebruikt alle externe adressen die worden geretourneerd door naamomzetting en alle bron-IP-adressen van de lokale computer. Het probeert eerst verbinding te maken met behulp van adresparen met de hoogste kans op succes. Daarom zorgt WSAConnectByName er niet alleen voor dat er een verbinding tot stand wordt gebracht, maar ook de tijd voor het tot stand brengen van de verbinding wordt geminimaliseerd.

Als u zowel IPv6- als IPv4-communicatie wilt inschakelen, gebruikt u de volgende methode:

  • De functie setsockopt moet worden aangeroepen op een socket die is gemaakt voor de AF_INET6-adresfamilie om de optie IPV6_V6ONLY socket uit te schakelen voordat u WSAConnectByName-aanroept. Dit wordt bereikt door de setsockopt-functie op de socket aan te roepen met de parameter op niveau ingesteld op IPPROTO_IPV6 (zie IPPROTO_IPV6 Socket Options), de optname parameter ingesteld op IPV6_V6ONLYen de optvalue parameterwaarde ingesteld op nul.

Als een toepassing verbinding moet maken met een specifiek lokaal adres of een specifieke poort, kan WSAConnectByName- niet worden gebruikt omdat de socketparameter voor WSAConnectByName een niet-afhankelijke socket moet zijn.

In het onderstaande codevoorbeeld ziet u dat er slechts enkele regels code nodig zijn om deze functie te gebruiken om een toepassing te implementeren die agnostisch is voor de IP-versie.

Verbinding maken met behulp van WSAConnectByName-

#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(LPWSTR NodeName, LPWSTR PortName) 
{
    SOCKET ConnSocket;
    DWORD 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;       
    }

    bSuccess = WSAConnectByName(ConnSocket, NodeName, 
            PortName, &dwLocalAddr,
            (SOCKADDR*)&LocalAddr,
            &dwRemoteAddr,
            (SOCKADDR*)&RemoteAddr,
            NULL,
            NULL);
    if (bSuccess){
        return ConnSocket;
    } else {
        return INVALID_SOCKET;
    }
}

WSAConnectByList

De functie WSAConnectByList brengt een verbinding tot stand met een host op basis van een set mogelijke hosts (vertegenwoordigd door een set doel-IP-adressen en poorten). De functie gebruikt alle IP-adressen en poorten voor het eindpunt en alle IP-adressen van de lokale computer en probeert verbinding te maken met behulp van alle mogelijke adrescombinaties.

WSAConnectByList- is gerelateerd aan de functie WSAConnectByName. In plaats van één hostnaam te gebruiken, accepteert WSAConnectByList een lijst met hosts (doeladressen en poortparen) accepteert en verbinding maakt met een van de adressen en poorten in de opgegeven lijst. Deze functie is ontworpen ter ondersteuning van scenario's waarin een toepassing verbinding moet maken met elke beschikbare host uit een lijst met mogelijke hosts.

Net als WSAConnectByName, vermindert de functie WSAConnectBy List aanzienlijk de broncode die nodig is voor het maken, binden en verbinden van een socket. Deze functie maakt het veel eenvoudiger om een toepassing te implementeren die agnostisch is voor de IP-versie. De lijst met adressen voor een host die door deze functie wordt geaccepteerd, kan IPv6- of IPv4-adressen zijn.

Als u wilt dat zowel IPv6- als IPv4-adressen worden doorgegeven in de lijst met één adres die door de functie wordt geaccepteerd, moeten de volgende stappen worden uitgevoerd voordat u de functie aanroept:

  • De setsockopt--functie moet worden aangeroepen op een socket die is gemaakt voor de AF_INET6 adresfamilie om de optie IPV6_V6ONLY socket uit te schakelen voordat u WSAConnectByList-aanroept. Dit wordt bereikt door de setsockopt-functie op de socket aan te roepen met de parameter op niveau ingesteld op IPPROTO_IPV6 (zie IPPROTO_IPV6 Socket Options), de optname parameter ingesteld op IPV6_V6ONLYen de optvalue parameterwaarde ingesteld op nul.
  • Alle IPv4-adressen moeten worden weergegeven in de IPv4-adresindeling die een IPv6-toepassing in staat stelt om alleen met een IPv4-knooppunt te communiceren. Met de IPv4-adresindeling voor IPv6-adressen kan het IPv4-adres van een IPv4-knooppunt worden weergegeven als een IPv6-adres. Het IPv4-adres wordt gecodeerd in de lage volgorde van 32 bits van het IPv6-adres en de 96 bits met hoge volgorde bevatten het vaste voorvoegsel 0:0:0:0:0:FFFF. De IPv4-adresindeling die is toegewezen aan IPv6 wordt opgegeven in RFC 4291. Zie www.ietf.org/rfc/rfc4291.txtvoor meer informatie. De IN6ADDR_SETV4MAPPED macro in Mstcpip.h kan worden gebruikt om een IPv4-adres te converteren naar de vereiste IPv4-adresindeling.

Verbinding maken met behulp van 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;
    DWORD 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;
    }
}

getaddrinfo

De getaddrinfo functie voert ook het verwerkingswerk van veel functies uit. Voorheen waren aanroepen naar een aantal Windows Sockets-functies vereist om een adres te maken, te openen en vervolgens te binden aan een socket. Met de getaddrinfo functie kunnen de regels van broncode die nodig zijn om dergelijke werkzaamheden uit te voeren aanzienlijk worden verminderd. In de volgende twee voorbeelden ziet u de broncode die nodig is om deze taken uit te voeren met en zonder de getaddrinfo functie.

Open, Connect en Bind met behulp van getaddrinfo-

#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(char *ServerName, char *PortName, int SocketType)
{
    SOCKET ConnSocket;
    ADDRINFO *AI;

    if (getaddrinfo(ServerName, PortName, NULL, &AI) != 0) {
        return INVALID_SOCKET;
    }

    ConnSocket = socket(AI->ai_family, SocketType, 0);
    if (ConnSocket == INVALID_SOCKET) {
        freeaddrinfo(AI);
        return INVALID_SOCKET;
    }

    if (connect(ConnSocket, AI->ai_addr, (int) AI->ai_addrlen) == SOCKET_ERROR) {
        closesocket(ConnSocket);
        freeaddrinfo(AI);
        return INVALID_SOCKET;
    }

    return ConnSocket;
}

Open, Connect en Bind zonder getaddrinfo

#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(char *ServerName, unsigned short Port, int SocketType) 
{
    SOCKET ConnSocket;
    LPHOSTENT hp;
    SOCKADDR_IN ServerAddr;
    
    ConnSocket = socket(AF_INET, SocketType, 0); /* Open a socket */
    if (ConnSocket < 0 ) {
        return INVALID_SOCKET;
    }

    memset(&ServerAddr, 0, sizeof(ServerAddr));
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(Port);

    if (isalpha(ServerName[0])) {   /* server address is a name */
        hp = gethostbyname(ServerName);
        if (hp == NULL) {
            return INVALID_SOCKET;
        }
        ServerAddr.sin_addr.s_addr = (ULONG) hp->h_addr;
    } else { /* Convert nnn.nnn address to a usable one */
        ServerAddr.sin_addr.s_addr = inet_addr(ServerName);
    } 

    if (connect(ConnSocket, (LPSOCKADDR)&ServerAddr, 
        sizeof(ServerAddr)) == SOCKET_ERROR)
    {
        closesocket(ConnSocket);
        return INVALID_SOCKET;
    }

    return ConnSocket;
}

U ziet dat beide broncodevoorbeelden dezelfde taken uitvoeren, maar het eerste voorbeeld, met behulp van de functie getaddrinfo, minder regels broncode vereist en IPv6- of IPv4-adressen kan verwerken. Het aantal regels broncode dat wordt geëlimineerd met behulp van de getaddrinfo functie varieert.

Notitie

In productiebroncode zou uw toepassing de set adressen doorlopen die worden geretourneerd door de gethostbyname of getaddrinfo functie. In deze voorbeelden wordt die stap weggelaten ten gunste van eenvoud.

 

Een ander probleem dat u moet oplossen bij het wijzigen van een bestaande IPv4-toepassing ter ondersteuning van IPv6, is gekoppeld aan de volgorde waarin functies worden aangeroepen. Zowel getaddrinfo als gethostbyname vereisen dat een reeks functieaanroepen in een specifieke volgorde wordt uitgevoerd.

Op platforms waar zowel IPv4 als IPv6 worden gebruikt, is de adresfamilie van de externe hostnaam niet van tevoren bekend. Adresomzetting met behulp van de getaddrinfo functie moet dus eerst worden uitgevoerd om het IP-adres en de adresfamilie van de externe host te bepalen. Vervolgens kan de socket functie worden aangeroepen om een socket van de adresfamilie te openen die wordt geretourneerd door getaddrinfo. Dit is een belangrijke wijziging in de manier waarop Windows Sockets-toepassingen worden geschreven, omdat veel IPv4-toepassingen vaak een andere volgorde van functieaanroepen gebruiken.

De meeste IPv4-toepassingen maken eerst een socket voor de AF_INET-adresfamilie, voeren vervolgens naamomzetting uit en gebruiken vervolgens de socket om verbinding te maken met het opgeloste IP-adres. Bij het maken van dergelijke toepassingen die geschikt zijn voor IPv6, moet de socket- functieaanroep worden uitgesteld totdat de naamomzetting is beëindigd wanneer de adresfamilie is afgetrokken. Als naamomzetting zowel IPv4- als IPv6-adressen retourneert, moeten afzonderlijke IPv4- en IPv6-sockets worden gebruikt om verbinding te maken met deze doeladressen. Al deze complexiteiten kunnen worden vermeden door de functie WSAConnectByName op Windows Vista en hoger te gebruiken, zodat toepassingsontwikkelaars worden aangemoedigd deze nieuwe functie te gebruiken.

In het volgende codevoorbeeld ziet u de juiste volgorde voor het uitvoeren van naamomzetting (uitgevoerd op de vierde regel in het volgende broncodevoorbeeld) en opent u vervolgens een socket (uitgevoerd in de regel 19th regel in het volgende codevoorbeeld). Dit voorbeeld is een fragment uit het bestand Client.c in de IPv6-Enabled Client Code in bijlage B. De functie PrintError die in het volgende codevoorbeeld wordt aangeroepen, wordt vermeld in het voorbeeld Client.c.

#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(char *Server, char *PortName, int Family, int SocketType)
{

    int iResult = 0;
    SOCKET ConnSocket = INVALID_SOCKET;

    ADDRINFO *AddrInfo = NULL;
    ADDRINFO *AI = NULL;
    ADDRINFO Hints;

    char *AddrName = NULL;

    memset(&Hints, 0, sizeof (Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;

    iResult = getaddrinfo(Server, PortName, &Hints, &AddrInfo);
    if (iResult != 0) {
        printf("Cannot resolve address [%s] and port [%s], error %d: %s\n",
               Server, PortName, WSAGetLastError(), gai_strerror(iResult));
        return INVALID_SOCKET;
    }
    //
    // Try each address getaddrinfo returned, until we find one to which
    // we can successfully connect.
    //
    for (AI = AddrInfo; AI != NULL; AI = AI->ai_next) {

        // Open a socket with the correct address family for this address.
        ConnSocket = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
        if (ConnSocket == INVALID_SOCKET) {
            printf("Error Opening socket, error %d\n", WSAGetLastError());
            continue;
        }
        //
        // Notice that nothing in this code is specific to whether we 
        // are using UDP or TCP.
        //
        // When connect() is called on a datagram socket, it does not 
        // actually establish the connection as a stream (TCP) socket
        // would. Instead, TCP/IP establishes the remote half of the
        // (LocalIPAddress, LocalPort, RemoteIP, RemotePort) mapping.
        // This enables us to use send() and recv() on datagram sockets,
        // instead of recvfrom() and sendto().
        //

        printf("Attempting to connect to: %s\n", Server ? Server : "localhost");
        if (connect(ConnSocket, AI->ai_addr, (int) AI->ai_addrlen) != SOCKET_ERROR)
            break;

        if (getnameinfo(AI->ai_addr, (socklen_t) AI->ai_addrlen, AddrName,
                        sizeof (AddrName), NULL, 0, NI_NUMERICHOST) != 0) {
            strcpy_s(AddrName, sizeof (AddrName), "<unknown>");
            printf("connect() to %s failed with error %d\n", AddrName, WSAGetLastError());
            closesocket(ConnSocket);
            ConnSocket = INVALID_SOCKET;
        }    
    }
    return ConnSocket;
}

IP-helperfuncties

Ten slotte moeten toepassingen die gebruikmaken van de FUNCTIE IP Helper GetAdaptersInfoen de bijbehorende structuur IP_ADAPTER_INFO, herkennen dat zowel deze functie als structuur beperkt zijn tot IPv4-adressen. Vervangingen met IPv6 voor deze functie en structuur zijn de GetAdaptersAddresses functie en de IP_ADAPTER_ADDRESSES structuur. IPv6-toepassingen die gebruikmaken van de IP Helper-API, moeten gebruikmaken van de GetAdaptersAddresses functie en de bijbehorende IP_ADAPTER_ADDRESSES structuur voor IPv6, die beide zijn gedefinieerd in de Microsoft Windows Software Development Kit (SDK).

Aanbevelingen

De beste methode om ervoor te zorgen dat uw toepassing IPv6-compatibele functieaanroepen gebruikt, is het gebruik van de getaddrinfo- functie voor het verkrijgen van host-naar-adresomzetting. Vanaf Windows XP maakt de getaddrinfo functie de gethostbyname functie overbodig en moet uw toepassing daarom de getaddrinfo functie gebruiken voor toekomstige programmeerprojecten. Hoewel Microsoft gethostbynameblijft ondersteunen, wordt deze functie niet uitgebreid ter ondersteuning van IPv6. Voor transparante ondersteuning voor het verkrijgen van IPv6- en IPv4-hostgegevens moet u getaddrinfogebruiken.

In het volgende voorbeeld ziet u hoe u de functie getaddrinfo het beste kunt gebruiken. U ziet dat de functie, wanneer deze correct wordt gebruikt, zowel IPv6- als IPv4-host-naar-adresomzetting correct verwerkt, maar ook andere nuttige informatie over de host krijgt, zoals het type sockets dat wordt ondersteund. Dit voorbeeld is een fragment uit het client.c-voorbeeld dat in bijlage B is gevonden.

#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")

int ResolveName(char *Server, char *PortName, int Family, int SocketType)
{

    int iResult = 0;

    ADDRINFO *AddrInfo = NULL;
    ADDRINFO *AI = NULL;
    ADDRINFO Hints;

   //
    // By not setting the AI_PASSIVE flag in the hints to getaddrinfo, we're
    // indicating that we intend to use the resulting address(es) to connect
    // to a service.  This means that when the Server parameter is NULL,
    // getaddrinfo will return one entry per allowed protocol family
    // containing the loopback address for that family.
    //
    
    memset(&Hints, 0, sizeof(Hints));
    Hints.ai_family = Family;
    Hints.ai_socktype = SocketType;
    iResult = getaddrinfo(Server, PortName, &Hints, &AddrInfo);
    if (iResult != 0) {
        printf("Cannot resolve address [%s] and port [%s], error %d: %s\n",
               Server, PortName, WSAGetLastError(), gai_strerror(iResult));
        return SOCKET_ERROR;
    }
     return 0;
}

Notitie

De versie van de getaddrinfo functie die IPv6 ondersteunt, is nieuw voor de Windows XP-versie van Windows.

 

Code die moet worden vermeden

Hostadresomzetting is traditioneel bereikt met behulp van de functie gethostbyname. Vanaf Windows XP:

  • De functie getaddrinfo maakt de functie gethostbyname functie verouderd.
  • Uw toepassingen moeten de functie getaddrinfo gebruiken in plaats van de functie gethostbyname.

Codeertaken

Een bestaande IPv4-toepassing wijzigen om ondersteuning toe te voegen voor IPv6-

  1. Verkrijg het hulpprogramma Checkv4.exe. Dit hulpprogramma wordt geïnstalleerd als onderdeel van de Windows SDK. Een oudere versie van het hulpprogramma Checkv4.exe was ook opgenomen als onderdeel van de Microsoft IPv6 Technology Preview voor Windows 2000.
  2. Voer het hulpprogramma Checkv4.exe uit op uw code. Zie Het Checkv4.exe Utility gebruiken voor meer informatie over het uitvoeren van het versiehulpprogramma voor uw bestanden.
  3. Het hulpprogramma waarschuwt u voor het gebruik van de gethostbyname, gethostbyaddr-en andere IPv4-functies en biedt aanbevelingen voor het vervangen door de IPv6-compatibele functie, zoals getaddrinfo en getnameinfo.
  4. Vervang eventuele exemplaren van de gethostbyname functie en de bijbehorende code naar wens door de getaddrinfo functie. Gebruik in Windows Vista de functie WSAConnectByName of WSAConnectByList, indien van toepassing.
  5. Vervang eventuele exemplaren van de functie gethostbyaddr en de bijbehorende code naar wens door de getnameinfo functie.

U kunt ook in uw codebasis zoeken naar exemplaren van de gethostbyname en gethostbyaddr-functies en al dit gebruik (en andere bijbehorende code, indien van toepassing) wijzigen in de getaddrinfo en getnameinfo functies.

IPv6-handleiding voor Windows Sockets-toepassingen

Gegevensstructuren wijzigen voor IPv6 Winsock-toepassingen

Dual-Stack Sockets voor IPv6 Winsock Applications

Gebruik van in code vastgelegde IPv4-adressen

problemen met de gebruikersinterface voor IPv6 Winsock-toepassingen

onderliggende protocollen voor IPv6 Winsock-toepassingen