網絡編程之IPv4與IPv6相互操作
由于互聯網終端不斷增加,IPv4 地址長度(32位)已不能夠滿足要求,所以出現了 IPv6地址(128位),但是現有應用程序大部分還是采用 IPv4 地址形式,所以必須解決 IPv4 與 IPv6 之間的相互操作,使現有基于 IPv4 的應用程序能夠與基于 IPv6 的應用程序相互通信。那么我們怎么實現 IPv4 客戶端與 IPv6 服務器、IPv6 客戶端與 IPv4 服務器之間的通信。
IPv4 客戶端與 IPv6 服務器
假設我們主機是運行雙棧,即存在 IPv4 協議棧和 IPv6 協議棧,雙棧主機上的 IPv6 服務器既能處理 IPv4 客戶端,也能處理 IPv6 客戶端,因為 IPv4 可以映射成 IPv6 地址。下圖是 IPv4 客戶端與 IPv6 服務器之間的通信過程:
IPv6 服務器程序創建的套接字綁定到 IPv6 通配地址和 TCP 端口號 9999。假設客戶端和服務器主機都處于同一個以太網,當左側兩個客戶端都發送 SYN 報文段請求與服務器建立連接時,IPv4 客戶端主機在一個 IPv4 數據報中載送 SYN,IPv6 客戶端主機在一個 IPv6 數據報中載送 SYN。在以太網線上包含以太網首部、IP 首部、TCP 首部以及 TCP 數據,根據以太網首部中包含的類型字段區分 IP 類型是為 IPv4 還是 IPv6,因此 IP 首部中的目的 IP 地址格式根據以太網類型字段分為 IPv4 地址和 IPv6 地址。兩者的 TCP 首部是一樣的,TCP 首部中包含目的端口號(即 IPv6 服務器的端口號 9999)。
服務器的接收數據鏈路通過查看以太網類型字段把每幀傳遞給相應的 IP 模塊。IPv4 模塊結合其上的 TCP 模塊檢測到 IPv4 數據報的目的端口對應的是一個 IPv6 套接字,于是把該數據報 IPv4 首部中的源 IPv4 地址轉換成一個等價的 IPv4 映射的 IPv6 地址。當 accept 系統調用把這個已經接受的 IPv4 客戶端連接返回給服務器進程時,這個映射后的地址將作為客戶的 IPv6 地址返回給服務器的 IPv6 套接字(也就是說服務器根本不知道自己是在跟 IPv4 客戶端通信,客戶端也不知道自己和 IPv6 的服務器通信),該連接上其余的數據報都是 IPv4 數據報。對于 IPv6 客戶端,當 accept 系統調用把接受的 IPv6 客戶端連接返回給服務器進程時,該客戶的 IPv6 地址就是原來 IPv6 首部中的源地址,不需要進行映射,該連接上其余的數據報都是 IPv6 數據報。
IPv4 的 TCP 客戶端與 IPv6 的 TCP 服務器之間通信的步驟如下:
首先啟動 IPv6 服務器,創建一個 IPv6 的監聽套接字,并且該服務器把通配地址和端口號 9999 綁定到該套接字上;
IPv4 客戶端調用 gethostbyname 函數找到服務器主機的一個 A 記錄,服務器同時包含 A 記錄和 AAAA 記錄,即同時支持 IPv4 和 IPv6,對于 IPv4 客戶端來說只需要 A 記錄即可;
IPv4 客戶端調用 connect 函數向服務器發出連接請求,即客戶端主機向服務器主機發送一個 IPv4 的 SYN 數據報(該 IPv4 的 SYN 中的目的地是 IPv6 套接字);
服務器主機接收到來自客戶端的 IPv4 的 SYN 數據報后,設置一個標志指示本連接應使用 IPv4 映射的 IPv6 地址,并響應一個 IPv4 的SYN 和 ACK 數據報。當該鏈接建立后,由 accept 函數把這個 IPv4 映射的 IPv6 地址返回給服務器;
當服務器主機往這個 IPv4 映射的 IPv6 地址發送 TCP 報文段時,其 IP 棧產生目的地址為所映射 IPv4 地址的 IPv4 載送數據報。即客戶端和服務器之間所有通信都使用 IPv4 的載送數據報;
IPv6 客戶端與 IPv4 服務器
IPv6 的 TCP 客戶端與 IPv4 的 TCP 服務器之間通信的步驟如下:
首先啟動 IPv4 服務器,創建一個 IPv4 的監聽套接字;
IPv6 客戶端調用 getaddrinfo 函數查找 IPv6 地址;
IPv6 客戶端在作為函數參數的 IPv6 套接字地址結構中設置這個 IPv4 映射的 IPv6 地址后調用 connect 函數向服務器發出連接請求,內核檢測到這個映射地址后,自動向服務器主機發送一個 IPv4 的 SYN 數據報;
服務器主機接收到來自客戶端的 IPv4 的 SYN 數據報后,響應一個 IPv4 的SYN 和 ACK 數據報。連接通過使用 IPv4 數據報建立;
總結
雙棧主機上的 IPv6 服務器既能服務于 IPv4 客戶,又能服務于 IPv6 客戶。IPv4 客戶發送給這種服務器的仍然是 IPv4 數據報,不過服務器的協議棧會把客戶主機的地址轉換成一個 IPv4 映射的 IPv6 地址。類似地,雙棧主機上的 IPv6 客戶能夠與 IPv4 服務器通信,客戶的解析器會把服務器主機所有的 A 記錄作為 IPv4 映射的 IPv6 地址返回給客戶,而客戶指定這些地址之一調用 connect 將會使雙棧發送一個 IPv4 的 SYN 數據報。為了使套接字編程具有可移植性,在編程實現過程中,盡量避免使用 gethostbyname 和 gethostbyaddr 函數,而應該使用 getaddrinfo 和 getnameinfo 函數。