溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用Socket編程從IPv4轉(zhuǎn)向IPv6支持

發(fā)布時(shí)間:2021-09-10 17:38:51 來源:億速云 閱讀:208 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹如何使用Socket編程從IPv4轉(zhuǎn)向IPv6支持,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

目前運(yùn)行主流的IT系統(tǒng)中,用于解決分布式系統(tǒng)內(nèi)部模塊及不同的系統(tǒng)間通信的一種主要的解決方案就是使用套接字Socket來開發(fā)應(yīng)用。由于當(dāng)前大部分正在運(yùn)行的IT系統(tǒng)中使用套接字Socket開發(fā)環(huán)境基本上都是基于IPv4完成的,因此在IT系統(tǒng)由IPv4向IPv6演進(jìn)方案中如何完成這部分相關(guān)應(yīng)用的演進(jìn)就顯得尤為的關(guān)鍵

1. 基于SOCKET技術(shù)的接口協(xié)議

通常用BSD Socket API (Windows平臺(tái)用Win Socket API)作為基礎(chǔ)開發(fā)應(yīng)用協(xié)議。以下是IT系統(tǒng)常用的接口協(xié)議:

基于SOCKET技術(shù)的接口協(xié)議通常以SOKET API作為為基礎(chǔ)開發(fā)應(yīng)用協(xié)議,下表是IT系統(tǒng)常用的接口協(xié)議:
序號(hào) 協(xié)議名稱 類型
1 TELNET TCP
2 SSH TCP
3 FTP TCP
4 TFTP TCP
5 SNMP UDP
6 SOCKET自定義 TCP/UDP,IT系統(tǒng)或廠商基于SOKET API定義的私有協(xié)議

Telnet協(xié)議是TCP/IP協(xié)議族中的一員,是Internet遠(yuǎn)程登陸服務(wù)的標(biāo)準(zhǔn)協(xié)議。應(yīng)用Telnet協(xié)議能夠把本地用戶所使用的計(jì)算機(jī)變成遠(yuǎn)程主機(jī)系統(tǒng)的一個(gè)終端。它提供了三種基本服務(wù):
(1) Telnet定義一個(gè)網(wǎng)絡(luò)虛擬終端為遠(yuǎn)的系統(tǒng)提供一個(gè)標(biāo)準(zhǔn)接口??蛻魴C(jī)程序不必詳細(xì)了解遠(yuǎn)的系統(tǒng),他們只需構(gòu)造使用標(biāo)準(zhǔn)接口的程序;
(2) Telnet包括一個(gè)允許客戶機(jī)和服務(wù)器協(xié)商選項(xiàng)的機(jī)制,而且它還提供一組標(biāo)準(zhǔn)選項(xiàng); .
(3) Telnet對(duì)稱處理連接的兩端,即Telnet不強(qiáng)迫客戶機(jī)從鍵盤輸入,也不強(qiáng)迫客戶機(jī)在屏幕上顯示輸出。

SSH的英文全稱是Secure Shell。通過使用SSH,你可以把所有傳輸?shù)臄?shù)據(jù)進(jìn)行加密,這樣“中間人”這種攻擊方式就不可能實(shí)現(xiàn)了,而且也能夠防止DNS和IP欺騙。還有一個(gè)額外的好處就是傳輸?shù)臄?shù)據(jù)是經(jīng)過壓縮的,所以可以加快傳輸?shù)乃俣取?SSH有很多功能,它既可以代替telnet,又可以為ftp、pop、甚至ppp提供一個(gè)安全的“通道”。

從客戶端來看,SSH提供兩種級(jí)別的安全驗(yàn)證: 第一種級(jí)別(基于口令的安全驗(yàn)證)只要你知道自己帳號(hào)和口令,就可以登錄到遠(yuǎn)程主機(jī)。所有傳輸?shù)臄?shù)據(jù)都會(huì)被加密,但是不能保證你正在連接的服務(wù)器就是你想連接的服務(wù)器??赡軙?huì)有別的服務(wù)器在冒充真正的服務(wù)器, 也就是受到“中間人”這種方式的攻擊。第二種級(jí)別(基于密匙的安全驗(yàn)證)需要依靠密匙,也就是你必須為自己創(chuàng)建一對(duì)密匙,并把公用密匙放在需要訪問的服務(wù)器上。如果你要連接到SSH服務(wù)器上,客戶端軟件就會(huì)向服務(wù)器發(fā)出請(qǐng)求,請(qǐng)求用你的密匙進(jìn)行安全驗(yàn)證。服務(wù)器收到請(qǐng)求之后,先在你在該服務(wù)器的HOME目錄下尋找你的公用密匙,然后把它和你發(fā)送過來的公用密匙進(jìn)行比較。如果兩個(gè)密匙一致,服務(wù)器就用公用密匙加密“質(zhì)詢”(challenge)并把它發(fā)送給客戶端軟件??蛻舳塑浖盏健百|(zhì)詢”(CHAP)之后就可以用你的私人密匙解密再把它發(fā)送給服務(wù)器。用這種方式,你必須知道自己密匙的口令。但是,與第一種級(jí)別相比,第二種級(jí)別不需要在網(wǎng)絡(luò)上傳送口令。第二種級(jí)別不僅加密所有傳送的數(shù)據(jù),而且“中間人”這種攻擊方式也是不可能的(因?yàn)樗麤]有你的私人密匙)。

FTP是TCP/IP協(xié)議組中的協(xié)議之一,它工作在TCP模型的應(yīng)用層之上,使用TCP傳輸,F(xiàn)TP需要兩個(gè)端口,一個(gè)端口是作為控制連接端口,也就是21端口,用于發(fā)送指令給服務(wù)器端以及等待服務(wù)器端的響應(yīng);另一個(gè)端口是數(shù)據(jù)傳輸端口,端口號(hào)20(僅PORT模式),是用來建立數(shù)據(jù)傳輸通道的。

TFTP全稱為Trivial File Transfer Protocol,中文名叫簡單文件傳輸協(xié)議。大家可以從它的名稱上看出,它適合傳送“簡單”的文件。與FTP不同的是,它使用的是UDP的69端口,因此它可以穿越許多防火墻。不過它也有缺點(diǎn),比如傳送不可靠、沒有密碼驗(yàn)證等。雖然如此,它還是非常適合傳送小型文件的。

SNMP(Simple Network Management Protocol,簡單網(wǎng)絡(luò)管理協(xié)議)的前身是簡單網(wǎng)關(guān)監(jiān)控協(xié)議(SGMP),用來對(duì)通信 <http://baike.baidu.com/view/15007.htm>線路進(jìn)行管理。隨后,人們對(duì)SGMP進(jìn)行了很大的修改,特別是加入了符合Internet < http://baike.baidu.com/view/11165.htm>定義的SMI和MIB <http://baike.baidu.com/view/141513.htm>:體系結(jié)構(gòu),改進(jìn)后的協(xié)議就是著名的SNMP。SNMP的目標(biāo)是管理互聯(lián)網(wǎng)  <http://baike.baidu.com/view/6825.htm>Internet上眾多廠家生產(chǎn)的軟硬件 <http://baike.baidu.com/view/25278.htm>平臺(tái),因此SNMP受Internet標(biāo)準(zhǔn)網(wǎng)絡(luò)管理框架的影響也很大?,F(xiàn)在SNMP已經(jīng)出到第三個(gè)版本的協(xié)議  <http://baike.baidu.com/view/36190.htm>,其功能較以前已經(jīng)大大地加強(qiáng)和改進(jìn)了。

SNMP的體系結(jié)構(gòu)是圍繞著以下四個(gè)概念和目標(biāo)進(jìn)行設(shè)計(jì)的:保持管理代理(Agent)的軟件成本盡可能低;最大限度地保持遠(yuǎn)程管理的功能,以便充分利用Internet的網(wǎng)絡(luò)資源 <http://baike.baidu.com/view/8439.htm>;體系結(jié)構(gòu)  <http://baike.baidu.com/view/1188494.htm>必須有擴(kuò)充的余地;保持SNMP的獨(dú)立性 <http://baike.baidu.com/view/562176.htm>,不依賴于具體的計(jì)算機(jī)  <http://baike.baidu.com/view/3314.htm>、網(wǎng)關(guān) <http://baike.baidu.com/view/807.htm>和網(wǎng)絡(luò)傳輸協(xié)議 <http://baike.baidu.com/view/16807.htm>。

2. SOCKET API接口的差異性:

基于套接字Socket API所開發(fā)的應(yīng)用中,基本上都使用相同的編程模型,且所有通信的基本操作如connect、accept、listen、send/sendto、read/readfrom等都是通過Socket API函數(shù)來完成的。這一點(diǎn)無論在IPv4的網(wǎng)絡(luò)環(huán)境下還是IPv6的網(wǎng)絡(luò)環(huán)境下基本上都是一致的,這就保證了基于套接字Socket開發(fā)的應(yīng)用軟件在軟件結(jié)構(gòu)上基本沒有變化,IPv4向IPv6演進(jìn)所帶來的變化主要集中在那些與地址相關(guān)的API函數(shù)上(包括地址有關(guān)的數(shù)據(jù)結(jié)構(gòu)體)。RFC2553針對(duì)IPv6帶來的套接字Socket API函數(shù)上的變化有明確的定義,IPv4和IPv6體現(xiàn)在套接字Socket API函數(shù)級(jí)別的差異可以用下面的表格來表示:

映射項(xiàng) 功能說明 IPv4 IPv6
常量定義 地址族 AF_INET AF_INET6
  協(xié)議族 PF_INET PF_INET6
IP地址結(jié)構(gòu)體 結(jié)構(gòu)體 sockaddr_in sockaddr_in6
 結(jié)構(gòu)體成員:套接口長度 sin_len sin6_len
 結(jié)構(gòu)體成員:協(xié)議族 sin_family sin6_family
 結(jié)構(gòu)體成員:端口號(hào) sin_port sin6_port
地址 通配地址 INADDR_ANY in6addr_any
 環(huán)回地址 INADDR_LOOPBACK in6addr_loopback
地址-表達(dá)式轉(zhuǎn)換函數(shù) 字符串地址轉(zhuǎn)為IP地址 inet_aton( ) inet_pton( )
 IP地址結(jié)構(gòu)轉(zhuǎn)為字符串 inet_ntoa( ) inet_ntop( )
名字-地址轉(zhuǎn)換函數(shù) 根據(jù)名字獲得IP地址 gethostbyname() getaddrinfo()
 根據(jù)IP地址獲得名字 gethostbyaddr() getnameinfo()
 根據(jù)名字獲得IP地址 gethostbyname2() getaddrinfo()
 根據(jù)服務(wù)名獲得全部服務(wù)信息 getservbyname() getaddrinfo ()
 根據(jù)服務(wù)端口獲得全部服務(wù)信息 getservbyport() getaddrinfo()

那些使用C/C++等語言開發(fā)的應(yīng)用軟件基本上主要關(guān)注這些API函數(shù)及結(jié)構(gòu)體的變化就可以了。

JAVA作為主要編程語言的情況:

Java是目前IT系統(tǒng)廣泛使用的程序設(shè)計(jì)語言,在JDK的java.net包中(包括javax.net包)提供了完整的對(duì)套接字Socket編程的類定義,使用Java語言開發(fā)的應(yīng)用都是基于這些類來完成的。JDK 從1.4版本開始部分支持IPv6協(xié)議,到了JDK1.5、JDK1.6就完全支持IPv6協(xié)議棧?,F(xiàn)網(wǎng)調(diào)研的結(jié)果表明大多的IT系統(tǒng)都是可以運(yùn)行在JDK1.5或以上的版本中。因此我們可以認(rèn)為目前我們使用的JDK就已經(jīng)具備了IPv6的能力。在JDK中和IPv4及IPv6相關(guān)的類只有兩個(gè):java.net.Inet4Address和java.net.Inet6Address,  也就是說如果要區(qū)分IPv4和IPv6的話,通過區(qū)分這兩個(gè)類就可以了,而且這兩個(gè)類均繼承自同一個(gè)父類java.net.InetAddress,在JDK中,和套接字Socket相關(guān)的其他所有的類都僅僅與這個(gè)父類java.net.InetAddress相關(guān)(都應(yīng)用的該類),而與IPv4(java.net.Inet4Address)及IPv6(java.net.Inet6Address)沒有直接的關(guān)系,即這些類對(duì)于是IPv4還是IPv6是透明的,因此從套接字Socket API接口這個(gè)層面上來看沒有差異。

3. 軟件重構(gòu)涉及到的主要技術(shù)

通過上面的分析,我們知道了IPv4向IPv6演進(jìn)所帶來的套接字Socket API接口上的差異,在了解及關(guān)注這些差異的基礎(chǔ)上,我們通過一些關(guān)鍵技術(shù)點(diǎn)的分析幫助我們重構(gòu)軟件代碼以實(shí)現(xiàn)向IPv6的演進(jìn)。
在使用C/C++語言來開發(fā)的Socket軟件中,主要關(guān)注以下幾個(gè)技術(shù)關(guān)鍵點(diǎn):

3.1 地址結(jié)構(gòu)的變化

IPv4環(huán)境下,通常使用的地址結(jié)構(gòu)體sockaddr_in在頭文件中定義如下:

struct sockaddr_in{
 short int sin_family;
 unsigned short int sin_port;
 struct in_addr sin_addr; 
 unsigned char sin_zero[8];
};

struct in_addr{
 unsigned long s_addr; 
};


面向IPv4編程的socket編程中,針對(duì)上述地址結(jié)構(gòu)體的賦值example如下:

rcv_udp_addr.sin_family = AF_INET;
rcv_udp_addr.sin_addr.s_addr = htonl(INADDR_ANY);
rcv_udp_addr.sin_port = htons(UDPRCV_PORT);

IPv6環(huán)境下,地址結(jié)構(gòu)體的發(fā)生對(duì)應(yīng)的變化,定義如下:

struct sockaddr_in6 {
 uint8_t sin6_len;  
 sa_family_t sin6_family;
 in_port_t sin6_port; 
 uint32_t sin6_flowinfo; 
 struct in6_addr sin6_addr; 
 uint32_t sin6_scope_id;    
};

struct in6_addr {
 uint8_t s6_addr[16];
};

相對(duì)的對(duì)于IPv6地址結(jié)構(gòu)體的賦值如下:

rcv_udp_addr.sin6_family = PF_INET;
rcv_udp_addr.sin6_addr.s6_addr =in6addr_any;
rcv_udp_addr.sin6_prot = htons (UDPRCV_PORT);

需要注意的是地址在IPv4中的地址結(jié)構(gòu)體中sin_addr是主機(jī)字節(jié)順序(經(jīng)過htons()函數(shù)轉(zhuǎn)換),
而在IPv6中,直接使用的是網(wǎng)絡(luò)字節(jié)順序。

3.2 socket的創(chuàng)建

建立socket的連接的API函數(shù)定義原型如下:
int socket(int domain,int type,int protocol);

對(duì)于IPv4而言,domain=AF_INET, 而IPv6,則domain=PF_INET6
例如創(chuàng)建TCP的Socket語句如下:

sock_tcp_ipv4 = socket(AF_INET,SOCK_STREAM,0);
sock_tcp_ipv6 = socket(PF_INET6,SOCK_STREAM,0);

3.3 字符串地址和網(wǎng)絡(luò)順序IP地址的相互轉(zhuǎn)換

在IPv4環(huán)境中,使用int inet_aton(const char *cp, struct in_addr *np)來完成從字符串地址到IP地址的轉(zhuǎn)換,在IPv6環(huán)境中,使用int inet_pton(int Af,const char *src,void *dst),多一個(gè)輸入?yún)?shù),使用樣例:inet_pton(AF_INET6,hostname,&snd_tcp_addr.sin6_addr);

反之,當(dāng)由網(wǎng)絡(luò)順序的IP地址轉(zhuǎn)換為字符串的時(shí)候,IPv4中,使用char *inet_ntoa(struct in_addr_in)而在IPv6環(huán)境中,使用const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt)這個(gè)函數(shù),參數(shù)發(fā)生了明顯的變化,使用樣例:
inet_ntop(PF_INET6, &rcv_udp_addr_sin6_addr,ip,sizeof(ip));

3.4 主機(jī)名和地址的轉(zhuǎn)換(域名解析)

在IPv4環(huán)境中,使用gethostbyname()或者gethostbyaddr()函數(shù)來實(shí)現(xiàn)轉(zhuǎn)換;在IPv6環(huán)境中,則使用getnameinfo()或者getaddrinfo()函數(shù)來完成轉(zhuǎn)換,需要包含以下的三個(gè)頭文件:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

3.5 組播地址常量

對(duì)于組播的應(yīng)用,在IPv6環(huán)境下增加了一些常量的定義,在調(diào)用套接字Socket API相關(guān)函數(shù)的時(shí)候使用。代碼重構(gòu)的時(shí)候,關(guān)注的常量如下表所示:
     常量 IPv6
IPV6_MULTICAST_IF 設(shè)置某個(gè)網(wǎng)絡(luò)接口為發(fā)送組播數(shù)據(jù)報(bào)的接口
IPV6_MULTICAST_HOPS 對(duì)于發(fā)送出的組播數(shù)據(jù)報(bào)為其設(shè)置hop的范圍
IPV6_MULTICAST_LOOP 如果組播源本身也屬于所發(fā)送出的組播數(shù)據(jù)的目標(biāo)組播組,并且該選項(xiàng)設(shè)置為1的話,將會(huì)存本地發(fā)送的時(shí)候產(chǎn)生回環(huán)。該選項(xiàng)的缺省值為1
IPV6_JOIN_GROUP 加入組播組
IPV6_LEAVE_GROUP 離開組播組
 樣例代碼:
rc=setsockopt(s,IPPROTO_IPV6,IPV6_MULTICAST_HOPS,(char )&gTtl,sizeof(gTt1));

以上的幾個(gè)技術(shù)關(guān)鍵點(diǎn)適合于Unix、Linux平臺(tái)下的C/C++語言來開發(fā)的Socket軟件的重構(gòu)。相對(duì)于Windows平臺(tái),以上幾點(diǎn)基本上也是適合的(畢竟Winsock庫還是基本兼容標(biāo)準(zhǔn)的Socket API定義的),但是相對(duì)于Windows平臺(tái)在重構(gòu)代碼是需要多注意下面幾點(diǎn)特殊性:

(1) 確保Winsock庫的版本在2.2或以上
對(duì)于Windows平臺(tái)而言,只有Winsock庫在2.2以上才支持IPv6。
(2) IP地址信息存儲(chǔ)
 在Winsock2庫中,使用新的SOCKADDR_STORGE結(jié)構(gòu)體來存儲(chǔ)地址信息,這樣可以屏蔽IPv4地址和IPv6地址差異。
(3) 套接字Connect操作
 在Winsock2庫中,為了兼容IPv4和IPv6,,提供了兩個(gè)新的Connect函數(shù)(WSAConnectByName()和WSAConnectByList())用于建立Socket連接,與標(biāo)準(zhǔn)的connect()函數(shù)相比,這兩個(gè)Connect函數(shù)的優(yōu)勢(shì)在于IP地址可以是IPv4的地址,也可以是IPv6的地址。


在現(xiàn)場(chǎng)調(diào)研的過程中,我們也發(fā)現(xiàn)了許多的IT系統(tǒng)在使用使用C/C++語言開發(fā)Socket軟件的時(shí)候,并不是直接使用原始的套接字Socket API函數(shù)或在此基礎(chǔ)上的封裝的庫來完成的,而是使用了第三方的庫來完成的,這里我們對(duì)常用的用于網(wǎng)絡(luò)編程第三方的庫也進(jìn)行相關(guān)的分析。

在Linux、Unix及 Windows平臺(tái)下,ACE(The Adaptive Comunication Environment)是一個(gè)被廣泛用來進(jìn)行網(wǎng)絡(luò)應(yīng)用開發(fā)的軟件包,ACE軟件包從5.3版本開始就提供了對(duì)IPv6的支持,ACE軟件包中提供的有關(guān)IP地址封裝的類是ACE_INET_Addr,該類屏蔽了IP地址不同版本(IPv4、IPv6)的區(qū)別,相應(yīng)類的成員方法也都兼容了不同地址版本,因此建立在ACE軟件包基礎(chǔ)上進(jìn)行的網(wǎng)絡(luò)應(yīng)用的開發(fā)是不需要做這種區(qū)分的(輸入的地址是IPv4的地址,則ACE_INET_Addr實(shí)例化的是具備IPv4特性的實(shí)例,輸入地址是IPv6的地址,則ACE_INET_Addr實(shí)例化的是具備IPv6特性的實(shí)例),  在重構(gòu)代碼過程中重點(diǎn)需要關(guān)注的是當(dāng)前ACE庫是否具備了支持IPv6的能力,即檢查編譯ACE庫的時(shí)候是否定有#ACE_HAS_IPV6宏定義。

相對(duì)于C/C++開發(fā)的Socket應(yīng)用程序而言,使用Java程序設(shè)計(jì)語言開發(fā)的Socket應(yīng)用程序在從IPv4環(huán)境重構(gòu)到IPv6環(huán)境中基本上不需要做什么代碼的重構(gòu),這主要取決于JDK目前的版本已經(jīng)提供了對(duì)IPv6的支持且有關(guān)IP地址的差異(IPv4和IPv6)已經(jīng)被屏蔽(上一節(jié)已經(jīng)進(jìn)行了分析JDK類庫的情況),例如下面的代碼:
Socket echosocket = new Socket(“hostip”,7);
如果”hostip”對(duì)應(yīng)的是IPv4的地址,則echosocket 是IPv4類型的Socket,如果”hostip”對(duì)應(yīng)的是IPv6的地址,則echosocket是IPv6類型,一條語句對(duì)于這兩種地址類型都適應(yīng)。


雖然JDK對(duì)于我們提供了很多的方便,使得我們的代碼基本上不需要重構(gòu),但是還是有兩個(gè)問題需要考慮:

1) Java虛擬機(jī)運(yùn)行的操作系統(tǒng)環(huán)境
 必須確保JVM所運(yùn)行的操作系統(tǒng)環(huán)境支持IPv6, 以Sun的JVM為例,要向支持IPv6, 對(duì)于操作系統(tǒng)的要求如下:
(1) Solaris 8 或更高的版本;
(2) Linux Kernel 2.1.2或更高版本(2.4.0版本以上可以更好的支持);
(3) Windows XP sp1、Windows 2003或更高的版本。
相對(duì)于其他的JVM(例如IBM的JVM、Oracle的JRockit等)需要檢查相應(yīng)的對(duì)主機(jī)操作系統(tǒng)的要求。

2) 如何分是IP4和IPv6的地址

雖然大部分Java開發(fā)的應(yīng)用中都不用關(guān)心到底是用的是IPv4的地址還是IPv6的地址,在某些情況下,例如需要輸出連接的IP地址信息或者需要獲得地址信息的詳細(xì)內(nèi)容,這個(gè)時(shí)候是需要區(qū)分的。前面已經(jīng)分析過java.net包中大部分類的成員函數(shù)使用的地址類是java.net.InetAddress,這個(gè)類是java.net.Inet4Address和java.net.Inet6Address類的父類,因此可以通過檢查java.net.InetAddress的子類類型來確認(rèn)到底地址使用的是IPv4的地址還是IPv6的地址,樣例代碼如下:

     InetAddress  address = Socket.getInetAddress();
     if ( address  instanceof  java.net.Inet4Address ) {
        Inet4Address  address_ipv4 = (java.net.Inet4Address)address;
        address_ipv4.getAddress();
……… //相關(guān)的IPv4的地址操作
    } else if ( address  instanceof  java.net.Inet6Address ) {
        Inet6Address  address_ipv6 = (java.net.Inet6Address)address;
        address_ipv6.getScopeId();
……..//相關(guān)的IPv6地址操作
        } else {
            ……….
        }

 由于JDK所提供的有關(guān)套接字Socket相關(guān)的類非常的完善,基本上有關(guān)Java語言開發(fā)Socket應(yīng)用很少使用第三方的包來完成。即使是那些建立在java.net包基礎(chǔ)上的應(yīng)用于更高層協(xié)議應(yīng)用第三方的包,例如Apache Commons中的Net包,該軟件包封裝了ntp、smtp、ftp、telnet、pop3等上層協(xié)議,也都是秉承了java.net包的特色,在軟件包中使用java.net.InetAddress作為地址類型,從而屏蔽了IPv4和IPv6的差異,使得功能上都同時(shí)支持IPv4環(huán)境和IPv6環(huán)境。


1.1.1.1. IPv4/IPV6雙棧實(shí)現(xiàn)方式
本文前面已經(jīng)分析認(rèn)為從IPv4向IPv6演進(jìn)的最佳解決方案是IT系統(tǒng)提供對(duì)雙棧應(yīng)用的支撐,下面將對(duì)如何使得那些基于套接字Socket技術(shù)實(shí)現(xiàn)的接口支持雙棧進(jìn)行重點(diǎn)分析。

目前的IT系統(tǒng)大都是建立在操作系統(tǒng)上層的應(yīng)用軟件,要想應(yīng)用系統(tǒng)支持IPv4及IPv6雙棧,操作系統(tǒng)首先要支持雙棧,因此在重構(gòu)應(yīng)用軟件之前,確認(rèn)或升級(jí)操作系統(tǒng)至支持雙棧是必要條件。

前面已經(jīng)分析過基于套接字Socket技術(shù)實(shí)現(xiàn)的接口都采用相同的編程模式,即典型的客戶-服務(wù)器模式(Client-Server),針對(duì)于服務(wù)器端而言,雙棧模式下服務(wù)器端Socket應(yīng)該能夠同時(shí)對(duì)綁定的IPv4地址及IPv6地址進(jìn)行監(jiān)聽,從而完成對(duì)不同地址的服務(wù)請(qǐng)求。想要達(dá)到這一目的可以有以下幾種方法:

1) 實(shí)施全地址監(jiān)聽
針對(duì)服務(wù)端而言,如果將服務(wù)端監(jiān)聽的地址綁定為”::”(IPv4中的0.0.0.0),將意味著服務(wù)器監(jiān)聽系統(tǒng)地址列表中的所有地址,即無論是IPv4的地址還是IPv6的地址都將被服務(wù)器監(jiān)聽以提供服務(wù)。例如下面的代碼:
int port = 1099;
ServerSocket  server = new ServerSocket(port);
Socket s ;
while (true) {
        S = server.accept();
        doClientStuff(s);
       }
服務(wù)器端將監(jiān)聽地址列表中(無論IPv4地址還是IPv6地址)所有地址的1099端口,客戶端無論訪問的是IPv4的地址還是IPv6的地址,只要端口是1099,都將獲得服務(wù)器端的服務(wù)。
顯然,這種模式是一種最簡單的重構(gòu)模式。

2) 兼容IPv4的IPv6地址
服務(wù)器端監(jiān)聽的IPv6地址是兼容地址(地址模式為::w.x.y.z)或者是IPv4映射的地址(地址模式為::ffff:w.x.y.z),那么在雙棧模式下,服務(wù)器實(shí)際上是對(duì)兩個(gè)地址進(jìn)行監(jiān)聽,例如,服務(wù)器端綁定監(jiān)聽的IPv6地址是::ffff:192.158.112.8, 那么實(shí)際上服務(wù)器對(duì)”::ffff:192.158.112.8”這個(gè)IPv6的地址進(jìn)行監(jiān)聽,同時(shí)也對(duì)192.158.112.8這個(gè)地址進(jìn)行監(jiān)聽,客戶端Socket訪問的目標(biāo)地址無論是192.158.112.8這個(gè)IPv4地址,還是訪問”::ffff:192.158.112.8”這個(gè)IPv6的地址,服務(wù)端都可以提供服務(wù)。

3) IPv4地址與IPv6地址各自獨(dú)立
服務(wù)端需要監(jiān)聽的地址是兩個(gè)完全獨(dú)立的IPv4地址及IPv6地址,此時(shí)通過分別對(duì)兩個(gè)不同的地址分別監(jiān)聽來完成雙棧應(yīng)用支撐,參見下面的樣例代碼:

  SOCKET   ServerSocket[FD_SETSIZE];
  ADDRINFO  AI0;// 存儲(chǔ)的是IPv6的地址
 ADDRINFO  AI1;// 存儲(chǔ)的是IPv4的地址

  ServerSocket[0] = socket(AF_INET6, SOCK_STREAM,PF_INET6);
   ServerSocket[1] = socket(AF_INET, SOCK_STREAM,PF_INET);
   ………..
   bind(ServerSocket[0], AI0->ai_addr,AI0->ai_addrlen);
   bind(ServerSocket[1], AI1->ai_addr,AI1->ai_addrlen);
   …………
   select (2, &ServerSet, 0, 0, 0); // select()系統(tǒng)調(diào)用:異步I/O多工

   if ( FD_ISSET(ServerSocket[0], &ServerSet) ) {
      //IPv6連接   sockv6 = accept(….)
     …….
   }
   if ( FD_ISSET(ServerSocket[1], &ServerSet) ) {
      //IPv4 連接  sockv4 = accept(…..)
      …..
  }

顯然,這種模式是一種比較復(fù)雜的重構(gòu)模式。

    與服務(wù)器端復(fù)雜的重構(gòu)方案相比,客戶端的改造就會(huì)相對(duì)簡單,在一個(gè)支持雙棧應(yīng)用的系統(tǒng)中,客戶端每一個(gè)Socket連接實(shí)例,都只會(huì)在IPv4模式及IPv6模式間選擇一種,這種選擇可以通過相應(yīng)的配置文件來完成,每一次創(chuàng)建客戶端Socket實(shí)例之前,依據(jù)從配置文件中讀取出的服務(wù)端地址類型(或者自動(dòng)分析地址格式來獲得類型)配置項(xiàng)來決定創(chuàng)建哪一種類型的Socket實(shí)例。相對(duì)于Java開發(fā)的客戶端而言,由于JDK對(duì)IPv6很好的適應(yīng)性(前面已經(jīng)分析過),代碼幾乎不需要做重構(gòu),相對(duì)于C/C++開發(fā)的客戶端而言,需要根據(jù)不同服務(wù)端IP地址的類型,使用不同的地址結(jié)構(gòu)體及參數(shù)等來創(chuàng)建Socket實(shí)例(關(guān)鍵技術(shù)一節(jié)已經(jīng)分析)。

 對(duì)于實(shí)際運(yùn)行環(huán)境是單棧的系統(tǒng),分別對(duì)IPv4和IPv6開發(fā)出不同的版本是不可取的。通常的解決方案(無論是客戶端還是服務(wù)端)和雙棧環(huán)境下客戶端的解決方案相類似,即通過選擇配置文件選擇項(xiàng)來確定當(dāng)前實(shí)例的運(yùn)行環(huán)境是IPv4環(huán)境還是IPv6環(huán)境。

關(guān)于如何使用Socket編程從IPv4轉(zhuǎn)向IPv6支持就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI