1. 程式人生 > >客戶端底層 Socket 實現IPV4 IPV6網路環境的相容

客戶端底層 Socket 實現IPV4 IPV6網路環境的相容

先貼上蘋果給出的IPV6相關介紹的地址,很全面也比較詳細: https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/

UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html

蘋果指出的IPV6是個什麼鬼呢?

實際上是指IPV6 DNS64/NAT64網路。IPV4和IPV6本就是兩個不同的協議,是不相容的。而V6又不可能立刻完全取代V4,所以就需要過渡技術來讓兩者互通,DNS64/NAT64技術就是用來保證IPV6網路環境下的終端可以訪問IPV4網路環境下的資源(注意,是單向的)。

DNS64/NAT64又是什麼鬼呢?

實際上就是網路供應商在IPV6網路與IPV4網路之間架上DNS64/NAT64伺服器來負責一個協議轉換的工作。借蘋果給出的圖來形象的展示一下,如下圖,終端在IPV6環境可直接訪問IPV6環境下的伺服器,而訪問IPV4環境下的伺服器時則需要經過DNS64/NAT64伺服器來實現協議轉換。

我們可以使用 OS X 10.11及以上系統的雙網絡卡Mac(乙太網口+無線網絡卡)來自己搭建一個IPV6 DNS64/NAT64網路環境。原理依然借用蘋果的圖來形象的展示一下,如下圖,MAC電腦擔任DNS64/NAT64伺服器的角色,其共享的無線網路就是一個IPV6-only網路,終端連線該無線網後即處於IPV6環境。

具體的搭建步驟很簡單,網上搜索一大堆,連結就不貼了。


OK,轉入正題,看看客戶端相容V4 V6的socket用法。

IPV6 環境下客戶端 Socket 程式設計與 IPV4 的區別主要在 socket 、connect 兩個函式的使用上,其餘的 read、write 等函式的使用都與 IPV4 一樣。

socket() 的函式原型為 int socket(int domain, int type, int protocol),兩者的區別在第一個引數的使用上,IPV4 為 AF_INET,IPV6 為 AF_INET6。

connect() 的函式原型為 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen),兩者的區別在第二個和第三個的使用上,尤其是第二個引數的結構體,而該結構體引數則需要使用函式 getaddrinfo() 來獲得,以實現相容。

getaddrinfo() 函式的資訊很容易查到,不多解釋,先貼上專案程式碼裁剪後deamon程式碼,程式碼詳細介紹請檢視註釋。

int getconnectsocket_tcp(const char * ip_str, uint32_t ip, const char * port_str, uint16_t port)
{
    int fd4 = -1, fd6 = -1;
    int ret;
    struct addrinfo hits;
    struct addrinfo * results, * aitmp;
    char host[40], portstr[6];

    //任意一個fd建立成功,都有機會成功連線伺服器
    fd4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    fd6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
    if (fd4 == -1 && fd6 == -1){
        printf("socket create error:%s\n", strerror(errno));
        return -1;
    }
    
    //設定非阻塞
    set_sock_nonblocking(fd4);
    set_sock_nonblocking(fd6);

    
    //初始化 getaddrinfo 所需引數 hits
    memset((void *)&hits, 0, sizeof(hits));
    hits.ai_family = AF_UNSPEC;     //注意這裡不要指定協議,這樣getaddrinfo函式會根據網路環境返回得到的全部地址(包含ipv4和ipv6)
    hits.ai_socktype = SOCK_STREAM;
    hits.ai_protocol = 0;
    hits.ai_flags = 0;

    //獲取伺服器的地址和埠的字串,域名IP均可
    if(ip_str != NULL){
        memcpy(host, ip_str, sizeof(host));
    }else{
        snprintf(host, sizeof(host), "%d", ip);
    }
    if(port_str != NULL){
        memcpy(portstr, port_str, sizeof(portstr));
    }else{
        snprintf(portstr, sizeof(portstr), "%d", port);
    }
    
    //呼叫getaddrinfo函式來獲取伺服器的IP地址,結果存放在以第四個引數results為首地址的連結串列中
    //需要注意的是:只有IOS9.2和OS X 10.11.2及以上系統支援IPV6地址的合成,即第一個引數host傳入IPV4地址,也能得到對應的IPV6地址
    //            IOS9.2和OS X 10.11.2以下系統只有第一個引數傳入域名時才能得到IPV6地址,若傳入IPV4地址則只能得到IPV4地址
    ret = getaddrinfo(host, portstr, &hits, &results);
    if(ret != 0){
        printf("getaddrinfo error:%s\n", strerror(errno));
        return -2;
    }
    
    
    //results連結串列中會存在ipv4和ipv6地址,所以遍歷該連結串列,當有一個connect成功時即可結束遍歷直接返回
    //注意兩個connect第二個引數雖然都是aitmp->ai_addr,卻指的不同的結構體,第三個引數應該傳入對應結構體的大小
    for(aitmp = results; aitmp != NULL; aitmp = aitmp->ai_next){
        switch(aitmp->ai_family){
            case AF_INET:
                ret = connect(fd4, aitmp->ai_addr, sizeof(struct sockaddr_in));
                if(ret == 0 || errno == EINPROGRESS || errno == EWOULDBLOCK){
                    if(fd6 >= 0)
                        close(fd6);
                    return fd4;
                }
                printf("IPV4 connect error:%s\n", strerror(errno));
                break;
            case AF_INET6:
                ret = connect(fd6, aitmp->ai_addr, sizeof(struct sockaddr_in6));
                if(ret == 0 || errno == EINPROGRESS || errno == EWOULDBLOCK){
                    if(fd4 >= 0)
                        close(fd4);
                    return fd6;
                }
                printf("IPV6 connect error:%s\n", strerror(errno));
                break;
        }
    }
    
    printf("connect fail!\n");
    if(fd4 >= 0)
        close(fd4);
    if(fd6 >= 0)
        close(fd6);
    return -3;
}

connect成功以後其他操作都無需改變,所以IPV4 IPV6環境的相容其實就這麼簡單。

簡單提一下,其中 getaddrinfo 函式實際上就是前往上文提到的DNS64伺服器來獲取IP地址(特殊一點的DNS解析)。依然借用蘋果的圖,原理如下圖:



需要注意的是程式設計中會涉及到幾個結構體的相互關係(sockaddr、sockaddr_in、sockaddr_in6等)。(其中sockaddr *比較迷惑人,而實際上把這裡sockaddr * 理解成void *後,很多問題就明瞭了)

幾個結構體的資訊可以參考以下兩個地址:

http://blog.csdn.net/an_zhenwei/article/details/8591115

http://xyliufeng.iteye.com/blog/718862

除文中提到得連結,本文額外參考地址:

http://www.jianshu.com/p/a6bab07c4062

相關推薦

客戶底層 Socket 實現IPV4 IPV6網路環境相容

先貼上蘋果給出的IPV6相關介紹的地址,很全面也比較詳細: https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/NetworkingOvervi

JAVA SOCKET網路程式設計,服務接收多個客戶連線的實現

這是基於前面一篇文章的一個簡單擴充套件,當然這不是最好的實現 服務端如果要想接收多個客戶端連線,必須死迴圈去接收新的客戶端的連線請求,一個完整的客戶端服務端socket連線通過一個執行緒來維護 package com.tree.demo.socket; import

高效能網路通訊框架Netty-Netty客戶底層與Java NIO對應關係

5.1 Netty客戶端底層與Java NIO對應關係 在講解Netty客戶端程式時候我們提到指定NioSocketChannel用於建立客戶端NIO套接字通道的例項,下面我們來看NioSocketChannel是如何建立一個Java NIO裡面的SocketChannel的。 首先我們來看

在Android上實現SSL握手,實現伺服器和客戶之間Socket互動

public class MySSLSocket extends Activity {      private static final int SERVER_PORT = 50030;//埠號      private static final String SERVER_IP = "218.206.17

Linux----網路程式設計(TCP網路通訊客戶伺服器程式設計實現多程序)

1、伺服器ser.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <assert.h> 5 #in

java在線聊天項目1.2版 ——開啟多個客戶,分別實現數據庫註冊和登錄功能後,成功登陸則登錄框消失,好友列表窗出現

false als blog string def iat ets cat med 登錄框消失語句 dispose(); 好友列表窗出現 使用new FriendsFrame(phone,s); 登陸對話框代碼修改如下: package com.swift.frame;

OpenLDAP客戶配置,實現用戶認證(原創)

pen img sss fuse sys src file 由於 onf 1.工作過程 OpenLDAP服務分為客戶端和服務端兩個部分,服務端的配置過程這裏不再贅述。當服務端配置結束後,在服務端的ldap數據庫中,應存放著用戶的信息,客戶端通過安裝nss-p

IdentityServer4客戶JWT解密實現(基於.net4.0)

空字符串 token true iba localhost urn 代碼 github substr 情景:公司項目基於.net4.0,web客戶端實現單點登錄需要自己解密id_token,對於jwt解密,.net提供了IdentityModel類庫,但是4.0中該類庫不可

Python每日進階--如何在客戶使用socket

Python 提供了兩個級別訪問的網路服務。: 低級別的網路服務支援基本的 Socket,它提供了標準的 BSD Sockets API,可以訪問底層作業系統Socket介面的全部方法。 高級別的網路服務模組 SocketServer, 它提供了伺服器中心類,可以簡化網

搭建FTP伺服器與客戶(1) - Python實現

FTP背景介紹:FTP(File Transfer Protocol)協議,顧名思義為檔案傳輸協議。由已故的Jon Postel與Joyce Reynolds開發,並於1985年10月釋出。其底層基於TCP/IP協議。FTP目前主要用於匿名下載公共檔案,也可以在兩臺跨系統的計算機之間傳輸檔案。為了實現F

MQTT Java客戶Eclipse paho實現資料的傳送和接收

MQTT(MQ Telemetry Transport)是IBM開發的一種網路應用層的協議 使用場景: 1、不可靠、網路頻寬小的網路 2、執行的裝置CPU、記憶體非常有限 特點: 1、基於釋出/訂閱模型的協議 2、他是二進位制協議,二進位制的特點就是緊湊、佔用

Ribbon使用及其客戶負載均衡實現原理分析

1、ribbon負載均衡測試 (1)consumer工程新增依賴 <dependency> <groupId>org.springframework.cloud</groupId> <artif

【微信小程式控制硬體②】 開始微信小程式之旅,匯入小程式Mqtt客戶原始碼,實現簡單的驗證和通訊於伺服器!(附帶原始碼)

本博文由熱愛分享熱愛技術的半顆心臟原創,非官方人員、非組織名義編寫,博文如有不對或侵犯您的權益,請及時留言,第一時間糾正! 一、前言; 繼續我們的小程式控制智慧硬體(包括esp8266)學

WebService客戶幾種實現方式

一、釋出一個webservice服務。 /** * * 基於soap的服務 */ @WebService(name = "Login",// 定義Port名稱 serviceName = "MyService", // 修改WebService服務名稱 targetNamespa

web本地客戶快取圖片實現

瀏覽器快取是提高使用者體驗和提升程式效能的一個很重要的途徑,通過瀏覽器的快取控制,可以對實時性要求不高的資料進行快取,可以減少甚至不需要再次對伺服器的請求就可以顯示資料。 本文將介紹如果通過HTTP協議中的header來控制瀏覽器的快取行為,建議大家在看的時候寫程式碼試驗下,這樣對這些header的理解

客戶骨架屏實現

一直以來,無論是Web還是iOS、Android的應用中,為了提升應用的載入等待這段時間的使用者感知體驗,各種技術層出不窮。其中,尤以菊花圖以及由它衍生各種載入動畫最為突出。 對於菊花圖我們自不必多說,現在對於載入的設計體驗有了比菊花載入體驗更棒的方法,即大家常看到的Skeleton Screen Load

ftp客戶的簡單實現

根據ftp協議實現簡單版的ftp客戶端與ftp伺服器進行互傳檔案 由於業務需要使用FTP伺服器進行檔案傳輸,因為一些因素的限制,需要自己實現一個ftp的客戶端與ftp伺服器進行檔案傳輸的任務, 利用抓包工具對ftp協議進行分析,實現了一個簡單的ftp客戶端 實驗環境

Python TCP 客戶(配合socket多執行緒伺服器)

''' Python TCP 客戶端(配合socket多執行緒伺服器) by 鄭瑞國 1、建立網路套接字c 2、建立網路連線 3、收發資訊 ''' import socket c = socket.socket() #1、建立網路套接字c c.connect(('127.

Android手機客戶通過JSP實現與Tomcat伺服器通訊(Msql資料庫,Json作為載體)--服務程式碼

伺服器端主要程式碼: 1.首先構建一個Person類,用來儲存使用者資訊 public class Person private String name; private String address; private Integer age; public P

Android:新浪微博拉起客戶分享——完美實現同時分享圖片和文字(Intent.ACTION_SEND)

新浪微博拉起客戶端分享——完美實現同時分享圖片和文字(Intent.ACTION_SEND) 點選事件後處理: private void  share(String content, Uri uri){