Panggilan Fungsi untuk Aplikasi Winsock IPv6
Fungsi baru telah diperkenalkan ke antarmuka Windows Sockets yang dirancang khusus untuk membuat pemrograman Windows Sockets lebih mudah. Salah satu manfaat fungsi Windows Sockets baru ini adalah dukungan terintegrasi untuk IPv6 dan IPv4.
Fungsi Windows Sockets baru ini mencakup yang berikut ini:
- WSAConnectByName
- WSAConnectByList
- keluarga fungsi getaddrinfo (getaddrinfo, GetAddrInfoEx, GetAddrInfoW, freeaddrinfo, FreeAddrInfoEx, FreeAddrInfoW, dan SetAddrInfoEx)
- keluarga fungsi getnameinfo (getnameinfo dan GetNameInfoW)
Selain itu, fungsi IP Helper baru dengan dukungan untuk IPv6 dan IPv4 telah ditambahkan untuk menyederhanakan pemrograman Windows Sockets. Fungsi Pembantu IP baru ini mencakup yang berikut ini:
Saat menambahkan dukungan IPv6 ke aplikasi, panduan berikut harus digunakan:
- Gunakan WSAConnectByName untuk membuat koneksi ke titik akhir yang diberi nama host dan port. Fungsi WSAConnectByName tersedia di Windows Vista dan yang lebih baru.
- Gunakan WSAConnectByList untuk membuat koneksi ke salah satu dari kumpulan titik akhir yang mungkin diwakili oleh sekumpulan alamat tujuan (nama host dan port). Fungsi WSAConnectByList tersedia di Windows Vista dan yang lebih baru.
- Ganti panggilan fungsi gethostbyname dengan panggilan ke salah satu fungsi getaddrinfo Windows Sockets baru. Fungsi getaddrinfo dengan dukungan untuk protokol IPv6 tersedia di Windows XP dan yang lebih baru. Protokol IPv6 juga didukung pada Windows 2000 ketika Pratinjau Teknologi IPv6 untuk Windows 2000 diinstal.
- Ganti panggilan fungsi gethostbyaddr dengan panggilan ke salah satu fungsi getnameinfo Windows Sockets baru. Fungsi getnameinfo dengan dukungan untuk protokol IPv6 tersedia di Windows XP dan yang lebih baru. Protokol IPv6 juga didukung pada Windows 2000 ketika Pratinjau Teknologi IPv6 untuk Windows 2000 diinstal.
WSAConnectByName
Fungsi WSAConnectByName menyederhanakan penyambungan ke titik akhir menggunakan soket berbasis aliran yang mengingat nama host atau alamat IP tujuan (IPv4 atau IPv6). Fungsi ini mengurangi kode sumber yang diperlukan untuk membuat aplikasi IP yang agnostik ke versi protokol IP yang digunakan. WSAConnectByName menggantikan langkah-langkah berikut dalam aplikasi TCP umum ke satu panggilan fungsi:
- Atasi nama host ke sekumpulan alamat IP.
- Untuk setiap alamat IP:
- Buat soket dari keluarga alamat yang sesuai.
- Mencoba menyambungkan ke alamat IP jarak jauh. Jika koneksi berhasil, koneksi akan kembali; jika tidak, alamat IP jarak jauh berikutnya untuk host dicoba.
Fungsi WSAConnectByName melampaui hanya menyelesaikan nama dan kemudian mencoba menyambungkan. Fungsi ini menggunakan semua alamat jarak jauh yang dikembalikan oleh resolusi nama dan semua alamat IP sumber komputer lokal. Ini pertama-tama mencoba untuk terhubung menggunakan pasangan alamat dengan peluang keberhasilan tertinggi. Oleh karena itu, WSAConnectByName tidak hanya memastikan bahwa koneksi akan dibuat jika memungkinkan, tetapi juga meminimalkan waktu untuk membuat koneksi.
Untuk mengaktifkan komunikasi IPv6 dan IPv4, gunakan metode berikut:
- Fungsi setsockopt harus dipanggil pada soket yang dibuat untuk keluarga alamat AF_INET6 untuk menonaktifkan opsi soket IPV6_V6ONLY sebelum memanggil WSAConnectByName. Ini dicapai dengan memanggil fungsi setsockopt pada soket dengan parameter tingkat yang diatur ke IPPROTO_IPV6 (lihat IPPROTO_IPV6 Opsi Soket), parameter nama optname diatur ke IPV6_V6ONLY, dan nilai parameter optvalue diatur ke nol.
Jika aplikasi perlu mengikat ke alamat atau port lokal tertentu, maka WSAConnectByName tidak dapat digunakan karena parameter soket ke WSAConnectByName harus berupa soket yang tidak terikat.
Contoh kode di bawah ini hanya menunjukkan beberapa baris kode yang diperlukan untuk menggunakan fungsi ini untuk mengimplementasikan aplikasi yang agnostik ke versi IP.
Membuat koneksi menggunakan 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
Fungsi WSAConnectByList membuat koneksi ke host yang diberikan sekumpulan host yang mungkin (diwakili oleh sekumpulan alamat IP tujuan dan port). Fungsi ini mengambil semua alamat IP dan port untuk titik akhir dan semua alamat IP komputer lokal, dan mencoba koneksi menggunakan semua kombinasi alamat yang mungkin.
WSAConnectByList terkait dengan fungsi WSAConnectByName. Alih-alih mengambil satu nama host, WSAConnectByList menerima daftar host (alamat tujuan dan pasangan port) dan terhubung ke salah satu alamat dan port dalam daftar yang disediakan. Fungsi ini dirancang untuk mendukung skenario di mana aplikasi perlu terhubung ke host yang tersedia dari daftar host potensial.
Mirip dengan WSAConnectByName, fungsi WSAConnectByList secara signifikan mengurangi kode sumber yang diperlukan untuk membuat, mengikat, dan menyambungkan soket. Fungsi ini membuatnya jauh lebih mudah untuk mengimplementasikan aplikasi yang agnostik ke versi IP. Daftar alamat untuk host yang diterima oleh fungsi ini mungkin alamat IPv6 atau IPv4.
Untuk mengaktifkan alamat IPv6 dan IPv4 untuk diteruskan dalam daftar alamat tunggal yang diterima oleh fungsi, langkah-langkah berikut harus dilakukan sebelum memanggil fungsi:
- Fungsi setsockopt harus dipanggil pada soket yang dibuat untuk keluarga alamat AF_INET6 untuk menonaktifkan opsi soket IPV6_V6ONLY sebelum memanggil WSAConnectByList. Ini dicapai dengan memanggil fungsi setsockopt pada soket dengan parameter tingkat yang diatur ke IPPROTO_IPV6 (lihat IPPROTO_IPV6 Opsi Soket), parameter nama optname diatur ke IPV6_V6ONLY, dan nilai parameter optvalue diatur ke nol.
- Alamat IPv4 apa pun harus diwakili dalam format alamat IPv6 yang dipetakan IPv4 yang memungkinkan aplikasi IPv6 saja untuk berkomunikasi dengan simpul IPv4. Format alamat IPv6 yang dipetakan IPv4 memungkinkan alamat IPv4 dari simpul IPv4 diwakili sebagai alamat IPv6. Alamat IPv4 dikodekan ke dalam 32 bit pesanan rendah dari alamat IPv6, dan urutan tinggi 96 bit menyimpan awalan tetap 0:0:0:0:0:FFFF. Format alamat IPv6 yang dipetakan IPv4 ditentukan dalam RFC 4291. Untuk informasi selengkapnya, lihat www.ietf.org/rfc/rfc4291.txt. Makro IN6ADDR_SETV4MAPPED dalam Mstcpip.h dapat digunakan untuk mengonversi alamat IPv4 ke format alamat IPv6 yang dipetakan IPv4 yang diperlukan.
Membuat Koneksi Menggunakan 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
Fungsi getaddrinfo juga melakukan pekerjaan pemrosesan dari banyak fungsi. Sebelumnya, panggilan ke sejumlah fungsi Windows Sockets diperlukan untuk membuat, membuka, lalu mengikat alamat ke soket. Dengan fungsi getaddrinfo, baris kode sumber yang diperlukan untuk melakukan pekerjaan tersebut dapat dikurangi secara signifikan. Dua contoh berikut mengilustrasikan kode sumber yang diperlukan untuk melakukan tugas-tugas ini dengan dan tanpa fungsi getaddrinfo .
Melakukan Buka, Sambungkan, dan Ikat menggunakan 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;
}
Melakukan Buka, Sambungkan, dan Ikat Tanpa Menggunakan 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;
}
Perhatikan bahwa kedua contoh kode sumber ini melakukan tugas yang sama, tetapi contoh pertama, menggunakan fungsi getaddrinfo , memerlukan lebih sedikit baris kode sumber, dan dapat menangani alamat IPv6 atau IPv4. Jumlah baris kode sumber yang dihilangkan dengan menggunakan fungsi getaddrinfo bervariasi.
Catatan
Dalam kode sumber produksi, aplikasi Anda akan melakukan iterasi melalui sekumpulan alamat yang dikembalikan oleh fungsi gethostbyname atau getaddrinfo. Contoh-contoh ini menghilangkan langkah itu demi kesederhanaan.
Masalah lain yang harus Anda atasi saat memodifikasi aplikasi IPv4 yang ada untuk mendukung IPv6 dikaitkan dengan urutan di mana fungsi dipanggil. Baik getaddrinfo dan gethostbyname mengharuskan urutan panggilan fungsi dilakukan dalam urutan tertentu.
Pada platform tempat IPv4 dan IPv6 digunakan, keluarga alamat nama host jarak jauh tidak diketahui sebelumnya. Jadi resolusi alamat menggunakan fungsi getaddrinfo harus dijalankan terlebih dahulu untuk menentukan alamat IP dan keluarga alamat host jarak jauh. Kemudian fungsi soket dapat dipanggil untuk membuka soket keluarga alamat yang dikembalikan oleh getaddrinfo. Ini adalah perubahan penting dalam bagaimana aplikasi Windows Sockets ditulis, karena banyak aplikasi IPv4 cenderung menggunakan urutan panggilan fungsi yang berbeda.
Sebagian besar aplikasi IPv4 pertama-tama membuat soket untuk keluarga alamat AF_INET, lalu melakukan resolusi nama, lalu menggunakan soket untuk menyambungkan ke alamat IP yang diselesaikan. Saat membuat aplikasi seperti itu berkemampuan IPv6, panggilan fungsi soket harus ditunda sampai setelah resolusi nama ketika keluarga alamat telah ditentukan. Perhatikan bahwa jika resolusi nama mengembalikan alamat IPv4 dan IPv6, maka soket IPv4 dan IPv6 terpisah harus digunakan untuk menyambungkan ke alamat tujuan ini. Semua kompleksitas ini dapat dihindari dengan menggunakan fungsi WSAConnectByName pada Windows Vista dan yang lebih baru, sehingga pengembang aplikasi didorong untuk menggunakan fungsi baru ini.
Contoh kode berikut menunjukkan urutan yang tepat untuk melakukan resolusi nama terlebih dahulu (dilakukan di baris keempat dalam contoh kode sumber berikut), lalu membuka soket (dilakukan di baris ke-19 dalam contoh kode berikut). Contoh ini adalah kutipan dari file Client.c yang ditemukan di Kode Klien berkemampuan IPv6 di Lampiran B. Fungsi PrintError yang disebut dalam contoh kode berikut tercantum dalam sampel 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;
}
Fungsi Pembantu IP
Akhirnya, aplikasi yang menggunakan fungsi Pembantu IP GetAdaptersInfo, dan struktur terkait IP_ADAPTER_INFO, harus mengenali bahwa fungsi dan struktur ini terbatas pada alamat IPv4. Penggantian yang diaktifkan IPv6 untuk fungsi dan struktur ini adalah fungsi GetAdaptersAddresses dan struktur IP_ADAPTER_ADDRESSES. Aplikasi berkemampuan IPv6 yang menggunakan API Pembantu IP harus menggunakan fungsi GetAdaptersAddresses dan struktur IP_ADAPTER_ADDRESSES berkemampuan IPv6 yang sesuai, keduanya didefinisikan dalam Kit Pengembangan Perangkat Lunak (SDK) Microsoft Windows.
Rekomendasi
Pendekatan terbaik untuk memastikan aplikasi Anda menggunakan panggilan fungsi yang kompatibel dengan IPv6 adalah dengan menggunakan fungsi getaddrinfo untuk mendapatkan terjemahan host-to-address. Dimulai dengan Windows XP, fungsi getaddrinfo membuat fungsi gethostbyname tidak perlu, dan oleh karena itu aplikasi Anda harus menggunakan fungsi getaddrinfo sebagai gantinya untuk proyek pemrograman di masa mendatang. Meskipun Microsoft akan terus mendukung gethostbyname, fungsi ini tidak akan diperluas untuk mendukung IPv6. Untuk dukungan transparan untuk mendapatkan informasi host IPv6 dan IPv4, Anda harus menggunakan getaddrinfo.
Contoh berikut menunjukkan cara terbaik menggunakan fungsi getaddrinfo . Perhatikan bahwa fungsi, ketika digunakan dengan benar seperti yang ditunjukkan contoh ini, menangani terjemahan host-to-address IPv6 dan IPv4 dengan benar, tetapi juga mendapatkan informasi berguna lainnya tentang host, seperti jenis soket yang didukung. Sampel ini adalah kutipan dari sampel Client.c yang ditemukan di Lampiran B.
#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;
}
Catatan
Versi fungsi getaddrinfo yang mendukung IPv6 baru untuk rilis Windows XP Windows.
Kode untuk Dihindari
Terjemahan alamat host secara tradisional telah dicapai menggunakan fungsi gethostbyname. Dimulai dengan Windows XP:
- Fungsi getaddrinfo membuat fungsi gethostbyname kedaluarsa.
- Aplikasi Anda harus menggunakan fungsi getaddrinfo alih-alih fungsi gethostbyname .
Tugas Pengkodan
Untuk mengubah aplikasi IPv4 yang ada untuk menambahkan dukungan untuk IPv6
- Dapatkan utilitas Checkv4.exe. Utilitas ini diinstal sebagai bagian dari Windows SDK. Versi lama alat Checkv4.exe juga disertakan sebagai bagian dari Pratinjau Teknologi Microsoft IPv6 untuk Windows 2000.
- Jalankan utilitas Checkv4.exe terhadap kode Anda. Lihat Menggunakan Utilitas Checkv4.exe untuk mempelajari tentang menjalankan utilitas versi terhadap file Anda.
- Utilitas memberi tahu Anda tentang penggunaan gethostbyname, gethostbyaddr, dan fungsi khusus IPv4 lainnya, dan memberikan rekomendasi tentang cara menggantinya dengan fungsi yang kompatibel dengan IPv6 seperti getaddrinfo dan getnameinfo.
- Ganti instans apa pun dari fungsi gethostbyname , dan kode terkait yang sesuai, dengan fungsi getaddrinfo . Pada Windows Vista, gunakan fungsi WSAConnectByName atau WSAConnectByList jika sesuai.
- Ganti instans apa pun dari fungsi gethostbyaddr, dan kode terkait yang sesuai, dengan fungsi getnameinfo.
Atau, Anda dapat mencari basis kode Anda untuk instans fungsi gethostbyname dan gethostbyaddr, dan mengubah semua penggunaan tersebut (dan kode terkait lainnya, yang sesuai) ke fungsi getaddrinfo dan getnameinfo.
Topik terkait