日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

IPv6套接字编程介绍

發(fā)布時(shí)間:2025/4/14 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IPv6套接字编程介绍 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

IPv6套接字編程

1.概述

由于互聯(lián)網(wǎng)用戶的日益增加,網(wǎng)絡(luò)需求日益擴(kuò)大,IPv4地址也日益緊張。人們?yōu)榱私鉀Q地址日趨耗盡的問題,采用了CIDRNAT等技術(shù)來(lái)延緩地址耗盡的速度,但這并不能從根本上解決IPv4目前存在的問題,IPv4地址耗盡只是一個(gè)時(shí)間問題。隨著互聯(lián)網(wǎng)的發(fā)展,Internet骨干路由器的路由表也日益擴(kuò)大,這使得路由器必須維護(hù)大量路由表。

由于IPv6可以解決傳統(tǒng)的IP技術(shù)的瓶頸問題,因此,它會(huì)推動(dòng)整個(gè)信息產(chǎn)業(yè)的發(fā)展。目前,第三代移動(dòng)技術(shù)的基本協(xié)議就采用IPv6,這意味著下一代互聯(lián)網(wǎng)具有移動(dòng)性,將來(lái)手機(jī)或其他個(gè)人移動(dòng)終端都將具有全球唯一的IPv6地址,因而IPv6技術(shù)將會(huì)變得越來(lái)越重要。

1.1套接字與通信

1.1.1套接字的概念

套接字Socket,是指從應(yīng)用程序中接受計(jì)算機(jī)網(wǎng)絡(luò)通信服務(wù)時(shí)的應(yīng)用程序接口,簡(jiǎn)單的說(shuō)就是通信的兩方的一種約定,用套接字中的相關(guān)函數(shù)來(lái)完成通信過程。套接字是個(gè)抽象編程概念,它把用戶代碼與TCP/IP協(xié)議堆棧的底層實(shí)現(xiàn)隔離開了,TCP套接字可以使用戶快速地開發(fā)出自定義協(xié)議的客戶/服務(wù)器應(yīng)用程序。

套接字是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元??梢詫⑻捉幼挚醋鞑煌鳈C(jī)間的進(jìn)程進(jìn)行雙向通信的端點(diǎn),它構(gòu)成了單個(gè)主機(jī)內(nèi)及整個(gè)網(wǎng)絡(luò)間的編程界面。套接字存在于通信域中,通信域是為了處理一般的線程通過套接字通信而引進(jìn)的一種抽象概念。套接字通常和同一個(gè)域中的套接字交換數(shù)據(jù)(數(shù)據(jù)交換也可能穿越域的界限,但這時(shí)一定要執(zhí)行某種解釋程序)。各種進(jìn)程使用這個(gè)相同的域互相之間用Internet協(xié)議簇來(lái)進(jìn)行通信。

1.1.2套接字應(yīng)用程序編程接口

套接字應(yīng)用程序編程接口是網(wǎng)絡(luò)應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信時(shí)所使用的接口,即應(yīng)用程序與協(xié)議棧軟件之間的接口,簡(jiǎn)稱套接字編程接口(Socket API)。它定義了應(yīng)用程序與協(xié)議棧軟件進(jìn)行交互時(shí)可以使用的一組操作,決定了應(yīng)用程序使用協(xié)議棧的方式、應(yīng)用程序所能實(shí)現(xiàn)的功能、以及開發(fā)具有這些功能的程序的難度。圖1-1為應(yīng)用進(jìn)程通過套接字接入到網(wǎng)絡(luò)的示意圖。

?

套接口是對(duì)網(wǎng)絡(luò)中不同主機(jī)上應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點(diǎn)的抽象,一個(gè)套接口就是網(wǎng)絡(luò)上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)絡(luò)協(xié)議棧交換數(shù)據(jù)的機(jī)制。要想實(shí)現(xiàn)套接字編程接口,可以采用兩種實(shí)現(xiàn)方式:

? ? 一種是在操作系統(tǒng)的內(nèi)核中增加相應(yīng)的軟件來(lái)實(shí)現(xiàn);

? ? 一種是通過開發(fā)操作系統(tǒng)之外的函數(shù)庫(kù)來(lái)實(shí)現(xiàn)。

在這里我們采用的是第一種實(shí)現(xiàn)方式。

1.1.3套接字的分類

套接字具有三種類型:

1)數(shù)據(jù)報(bào)套接字(Datagram? SOCKET

數(shù)據(jù)報(bào)套接字提供無(wú)連接的不保證可靠的獨(dú)立的數(shù)據(jù)報(bào)傳輸服務(wù)。在Internet通信域中,數(shù)據(jù)報(bào)套接字使用UDP數(shù)據(jù)報(bào)協(xié)議形成的進(jìn)程間通路,具有UDP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn)。圖1-2揭示了基于UDP協(xié)議的數(shù)據(jù)報(bào)套接字的工作模型。

2)流式套接字(Stream SOCKET

? ?? 流式套接字提供雙向的、有序的、無(wú)重復(fù)的、無(wú)記錄邊界的可靠的數(shù)據(jù)流傳輸服務(wù)。在Internet通信域中,流式套接字使用TCP協(xié)議形成的進(jìn)程間通路,具有TCP協(xié)議為上層所提供的服務(wù)的所有特點(diǎn),在使用流式套接字傳輸數(shù)據(jù)之前,必須在數(shù)據(jù)的發(fā)送端和接收端之間建立連接。 圖1-3揭示了基于TCP協(xié)議的流式套接字的工作模型

3)原始式套接字(RAW SOCKET

原始式套接字允許對(duì)較低層次的協(xié)議,如IPICMP直接訪問,用于檢驗(yàn)新的協(xié)議的實(shí)現(xiàn)。

每一個(gè)正被使用的套接字都有它確定的類型,只有相同類型的套接字才能相互通信。

1.1.4套接字的應(yīng)用場(chǎng)合

1)不管是采用對(duì)等模式或者客戶機(jī)/服務(wù)器模式,通信雙方的應(yīng)用程序都需要開發(fā)。

2)雙方所交換數(shù)據(jù)的結(jié)構(gòu)和交換數(shù)據(jù)的順序有特定的要求,不符合現(xiàn)在成熟的應(yīng)用層協(xié)議,甚至需要自己去開發(fā)應(yīng)用層協(xié)議,自己設(shè)計(jì)最適合的數(shù)據(jù)結(jié)構(gòu)和信息交換規(guī)程。

1.1.5套接字的一般通信過程

網(wǎng)絡(luò)通信一般為Client/Server模式

Server:運(yùn)行一個(gè)特定的程序,它申請(qǐng)一個(gè)Socket,該Socket在某一個(gè)Port監(jiān)聽客戶機(jī)的連接。

Client:申請(qǐng)一個(gè)Socket,將該Socket與服務(wù)器端的Port相聯(lián),服務(wù)器在接受該Client的連接后,新生成一個(gè)Port,在該新Port上與Client通信;原Port繼續(xù)監(jiān)聽,準(zhǔn)備接受新的Client的連接。

1-5 server通過另一個(gè)端口與client建立連接

套接字是網(wǎng)絡(luò)上與另一個(gè)應(yīng)用程序建立連接并通信的一個(gè)句柄。

?

1.2 IPv6

1.2.1 IPv6協(xié)議

IPv6是因特網(wǎng)協(xié)議第六版(Internet Protocol Version Six)的縮寫。目前,在Internet中廣泛使用的IP協(xié)議是被人們稱為IP第四版的IPv4協(xié)議。IPv4協(xié)議只使用了32位的IP地址,在迅速發(fā)展的Internet中,發(fā)生了地址的絕度數(shù)嚴(yán)重不足的問題。為了解決這個(gè)問題,人們對(duì)IP的第六版本進(jìn)行了標(biāo)準(zhǔn)化,并且目前已經(jīng)有一些操作系統(tǒng)對(duì)它進(jìn)行支持。在IPv6協(xié)議中,IP地址的長(zhǎng)度變?yōu)?span lang="en-us">128位,在Internet中能夠連接巨大數(shù)目的主機(jī)。

IPv6協(xié)議對(duì)IPv4的改進(jìn)表現(xiàn)在:

1)擴(kuò)展地址空間。IP地址長(zhǎng)度由32位增加到128位。

2)簡(jiǎn)化的首部格式,優(yōu)化路由選擇。IPv4首部的某些字段被取消或改為選項(xiàng),以減少報(bào)文分組處理過程中常用情況的處理開銷,并使得IPv6首部的帶寬開銷盡可能低。

3)支持?jǐn)U展首部和選項(xiàng)。IPv6的選項(xiàng)放在單獨(dú)的擴(kuò)展首部中,位于報(bào)文分組中IPv6基本首部和傳送層首部之間。因?yàn)榇蠖鄶?shù)IPv6選項(xiàng)首部不會(huì)被報(bào)文分組投遞路徑上的任何路由器檢查和處理,直至其到達(dá)最終目的地,這種組織方式有利于改進(jìn)路由器在處理包含選項(xiàng)的報(bào)文分組時(shí)的性能。IPv6的另一改進(jìn),是其選項(xiàng)與IPv4不同,可具有任意長(zhǎng)度,不限于40字節(jié)。

4)支持認(rèn)證和加密機(jī)制。IPv6定義了一種擴(kuò)展,可支持權(quán)限驗(yàn)證和數(shù)據(jù)完整性并支持保密性要求。

5)支持自動(dòng)配置。IPv6支持多種形式的自動(dòng)配置,從孤立網(wǎng)絡(luò)結(jié)點(diǎn)地址的即插即用自動(dòng)配置,到DHCP提供的全功能的設(shè)施。

6)服務(wù)質(zhì)量能力。IPv6增加了一種新的能力,如果某些報(bào)文分組屬于特定的工作流,發(fā)送者要求對(duì)其給予特殊處理,則可對(duì)這些報(bào)文分組加標(biāo)號(hào),例如非缺省服務(wù)質(zhì)量通信業(yè)務(wù)或實(shí)時(shí)服務(wù)。

?

?

?

?

?

1.2.2 IPv6數(shù)據(jù)報(bào)

1-6 IPv6數(shù)據(jù)報(bào)報(bào)頭

0? ? ? ? 4? ? ? ? ? ? ? ? 12? ? ? 16? ? ? ? ? ? ? ? 24? ? ? ? ? ? ?? 31

版本號(hào)

通信分類

流標(biāo)識(shí)符

有效負(fù)載長(zhǎng)度

下一首部

跳數(shù)限制

源地址(128位)

目的地址(128位)

有效負(fù)載

0-多個(gè)擴(kuò)展首部+高層數(shù)據(jù))

1 IPv6數(shù)據(jù)報(bào)報(bào)頭

Ipv6協(xié)議的結(jié)構(gòu)體定義如下:

Struct ip6_hdr{

? Union{

Struct ip6_hdrctl{

?? u_int32_t ip6_unl_flow;/*4位的版本,8位的傳輸與分類,20位的流標(biāo)識(shí)符*/

u_int16_t ip6_unl_plen;/*報(bào)頭長(zhǎng)度*/

u_int8_t ip6_unl_nxt;/*下一個(gè)報(bào)頭*/

u_int8_t ip6_unl_hlim;/*跨度限制*/

}ip6_unl ;

u_int8_t ip6_un2_vfc;/*4位的版本號(hào)跨度為4位的傳輸分類*/

}ip6_ctlun ;

struct in6_addr ip6_src;/*發(fā)送端地址*/

struct in6_addr ip6_dst;/*接收端地址*/

};

? #define ip6_vfc? ? ? ? ? ? ? ip6_ctlun.ip6_un2_vfc

? #define ip6_flow? ? ? ? ? ?? ip6_ctlun.ip6_unl.ip6_unl_flow

#define ip6_plen? ? ? ? ? ? ?ip6_ctlun.ip6_unl.ip6_unl_plen

#define ip6_nxt? ? ? ? ? ? ? ip6_ctlun.ip6_unl.ip6_unl_nxt

#define ip6_hlim? ? ? ? ? ?? ip6_ctlun.ip6_unl.ip6_unl_hlim

#define ip6_hops? ? ? ? ? ?? ip6_ctlun.ip6_unl.ip6_unl_hops

?

1.3? ?IPv6IPv4的兼容性問題

由于IPv6IPv4在地址長(zhǎng)度,數(shù)據(jù)報(bào)格式等方面存在許多不同點(diǎn),因此在IPv6套接字編程的時(shí)候,如何使程序既能適應(yīng)Ipv6的特點(diǎn),又能消除不同地址間的差異,使程序既能處理IPv4的地址,又能處理IPv6地址,實(shí)現(xiàn)IPv6IPv4的兼容,對(duì)程序編程者來(lái)說(shuō)顯得非常重要。IPv6套接字編程將綜合考慮各種情況,解決IPv6IPv4、IPv6Ipv6節(jié)點(diǎn)間的通信問題。

?

?

2.從IPv4網(wǎng)絡(luò)向IPv6網(wǎng)絡(luò)過渡

目前,Internet中的絕大部分節(jié)點(diǎn)用的都是IPv4地址。為了解決IPv4地址的缺陷,Internet將逐步向IPv6過渡,在一段時(shí)間后,使Internet中所有的節(jié)點(diǎn)都具備處理IPv6地址的能力。

為了達(dá)到這個(gè)目標(biāo),因特網(wǎng)工程任務(wù)組(IETF)已經(jīng)設(shè)計(jì)出兩種解決方案。通過這兩種方案,可以使IPv6無(wú)縫地移植到IPv4中。這兩種方法就是:雙棧協(xié)議和隧道技術(shù)。

?

雙棧協(xié)議

? ?? 雙協(xié)議棧(dual stack)是指在完全過渡到 IPv6 之前,使一部分主機(jī)(或路由器)裝有兩個(gè)協(xié)議棧,一個(gè) IPv4 和一個(gè) IPv6

雙棧網(wǎng)絡(luò)的建設(shè)有兩種模式:

? ? (1)完全雙棧網(wǎng)絡(luò),即所有網(wǎng)絡(luò)設(shè)備、用戶終端都支持IPv4、IPv6雙協(xié)議棧,用戶通信既可使用IPv4協(xié)議棧也可使用IPv6協(xié)議棧。

?? ?(2)有限雙棧網(wǎng)絡(luò),網(wǎng)絡(luò)中部分網(wǎng)絡(luò)設(shè)備、用戶終端采用雙協(xié)議棧,這些用戶可使用IPv4IPv6與其它用戶互聯(lián)互通,但新增的網(wǎng)絡(luò)設(shè)備和用戶終端則僅使用IPv6協(xié)議棧,應(yīng)用基于IPv6協(xié)議棧。

雙協(xié)議棧的具體工作方式如下:

(1)若應(yīng)用程序使用的目的地址為IPv4地址,則使用IPv4協(xié)議;

(2)若應(yīng)用程序使用的目的地址為IPv4兼容的IPv6地址,則同樣使用IPv4協(xié)議,區(qū)別僅在于此時(shí)的IPv6封裝在IPv4中;

(3)若應(yīng)用程序使用的目的地址是一個(gè)非IPv4兼容的IPv6地址,則使用IPv6協(xié)議,而且很可能要采用隧道等機(jī)制來(lái)進(jìn)行路由傳送;

(4)若應(yīng)用程序使用域名作為目標(biāo)地址,則先從DNS服務(wù)器得到相應(yīng)的IPv4/IPv6地址,然后根據(jù)地址情況進(jìn)行相應(yīng)的處理。

2.2隧道技術(shù)

? ? 所謂隧道,就是在一方將IPv6的包封裝在IPv4包里,然后在目的地對(duì)其解析,得到IPv6包。通過隧道,IPv6分組被作為無(wú)結(jié)構(gòu)無(wú)意義的數(shù)據(jù),封裝在IPv4數(shù)據(jù)報(bào)中,被IPv4網(wǎng)絡(luò)傳輸。由于IPv4網(wǎng)絡(luò)把IPv6數(shù)據(jù)當(dāng)作無(wú)結(jié)構(gòu)無(wú)意義數(shù)據(jù)傳輸,因此不提供幀自標(biāo)示能力,所以只有在IPv4連接雙方都同意時(shí)才能交換IPv6分組,否則收方會(huì)將IPv6分組當(dāng)成IPv4分組而造成混亂。

?

IPv6協(xié)議中,為了存儲(chǔ)通信所需要的IP地址和端口號(hào),定義了一個(gè)sockaddr_in6的結(jié)構(gòu)體。sockaddr_in6的結(jié)構(gòu)如下:

struct sockaddr_in6{

u_int8_t ? ? ? ? ? ? ?sin6_len;

u_int8_ ? ? ? ? ? ? ? sin6_family;

u_int16_t ? ? ? ? ? ? sin6_port;

u_int32_t ? ? ? ? ? ? sin6_flowinfo;

struct in6_addr ?? ? ?sin6_addr;

u_int32_t ? ? ? ? ? ? sin6_scope_id;

};

數(shù)據(jù)類型u_int8_t8位無(wú)符號(hào)整數(shù)typedef unsigned char u_int8_t。u_int16_t u_int32_t亦類似。

sin6_len域中存儲(chǔ)有sockaddr_in6結(jié)構(gòu)體的長(zhǎng)度。在 sin6_family域中存儲(chǔ)有表示IPv6地址系列的AF_INET6.sin6_port域中存儲(chǔ)有一個(gè)傳輸層所使用的端口號(hào)。在sin6_flowinfo域中,存儲(chǔ)有一個(gè)在QoS中所使用的流標(biāo)識(shí)符。在sin6_addr域中,存儲(chǔ)有IPv6協(xié)議的地址。在sin6_scope_id域中,存儲(chǔ)有表示范圍的ID.

IPv6協(xié)議的地址是由下面的int6_addr結(jié)構(gòu)體加以定義的:

struct int6_addr{

u_int8_t s6_addr[16];

};

in6_addrIPv6協(xié)議的地址相同為16Byte,但為了在操作系統(tǒng)內(nèi)部處理方便起見,實(shí)際上是由聯(lián)合體來(lái)定義的。

IPv4協(xié)議中,相對(duì)于sockaddr_in6結(jié)構(gòu)體的為sockaddr_in結(jié)構(gòu)體,其中缺少域sin6_flowinfosin6_scope_id。

?

3.2 addrinfo

?? addrinfo結(jié)構(gòu)體是為了消除IPv6協(xié)議與IPv4協(xié)議之間的差異,編制統(tǒng)一的程序而追加的。在各臺(tái)主機(jī)中,考慮能夠賦予多個(gè)IPv4地址或IPv6地址,將addrinfo結(jié)構(gòu)體設(shè)計(jì)為具有下面的列表結(jié)構(gòu):

? Struct addrinfo{

int? ? ?ai_flags;

int? ?? ai_family;

int? ?? ai_socktype;

int? ?? ai_protocol;

size_t? ai_addrlen;

char?? ?*ai_canonname;

struct sockaddr? *ai_addr;

struct addrinfo? *ai_next;

? };

ai_flags域中,能夠設(shè)定3位的標(biāo)志位。它們分別是AI_PASSIVE、AI_CANONNAME、AI_NUMERICHOST。AI_PASSIVEIPv4協(xié)議中指定INADDR_ANY的時(shí)候,不需要指定具體的主機(jī),而是利用任意的主機(jī)。AI_CANONNAME是在最初的列表結(jié)構(gòu)中存儲(chǔ)正式名稱的時(shí)候所設(shè)定的值。AI_NUMERICHOST不使用DNS進(jìn)行檢索,只使用IP地址,它在不想使用DNS查詢處理、需要等待一定時(shí)間的時(shí)候等情況下才使用。

ai_family域表示了地址系列。在地址系列中,具有表示IPv4協(xié)議的AF_INETIPv6協(xié)議的AF_INET6等。

ai_socktype域表示了套接字的類型。在一個(gè)套接字的類型域中,具有下面的三種類型:表示流型的SOCK_STREAM,表示數(shù)據(jù)報(bào)型的SOCK_DGRAM,表示raw IPSOCK_RAW。

ai_protocol域表示了傳輸層所使用的協(xié)議。在使用TCP協(xié)議時(shí),它為IPPROTO_TCP;在使用UDP協(xié)議時(shí),它為IPPROTO_UDP;在不使用傳輸層時(shí),在該域中存儲(chǔ)0.

ai_addrlen域表示ai_addr的長(zhǎng)度。ai_canonname域表示ai_addr的別名。

ai_addr域表示了訪問sockaddr_insockaddr_in6的指針。ai_next域表示列表的下一個(gè)地址。在列表結(jié)束時(shí),在該域中存儲(chǔ)NULL。

?

?

4IPv6套接字編程中用到的函數(shù)

4.1 socket()函數(shù)

在使用套接字的時(shí)候,利用socket系統(tǒng)調(diào)用來(lái)打開一個(gè)套接字。socket系統(tǒng)調(diào)用的語(yǔ)法如下所示:

#include <sys/types.h>

#include <sys/socket.h>

?

int socket(int domain,int type,int protocol);

domain域中,指定地址系列(協(xié)議系列)。地址系列表示所使用的地址體系。在TCP/IP協(xié)議中,將IP地址和端口號(hào)所形成的地址體系指定為AF_INETAF_INET6,將表示TCP/IP協(xié)議的地址體系指定為PF_INET。

type變量中,指定所使用的協(xié)議的類型,其中可以指定下面的值:

#define sock_STREAM? ? ? ?? /*流式套接字*/

#define sock_DGRAM? ? ? ?? /*數(shù)據(jù)報(bào)套接字*/

#define sock_RAW? ? ? ? ? ? /*原始式套接字*/

#define sock_RDM? ? ? ? ? ? /*可靠傳輸報(bào)文*/

#define sock_SEQPACKET? ?? /*序列包流*/

在使用TCP協(xié)議時(shí),指定為SOCK_STREAM;在使用UDP協(xié)議時(shí),指定為SOCK_DGRAM;在使用原始IP協(xié)議時(shí),指定為SOCK_RAW

protocol域中,指定所使用的協(xié)議類型。在使用TCP協(xié)議或UDP協(xié)議時(shí),由于指定type就可以確定方法,所以在protocol域中,缺省值為0

如果成功地調(diào)用了socket系統(tǒng)調(diào)用,則打開一個(gè)套接字,并且返回一個(gè)可以利用該套接字的描述符。在發(fā)生錯(cuò)誤時(shí),則返回值為-1。

?

4.2 bind()函數(shù)

在利用自己的主機(jī)指定所使用的IP地址和端口號(hào)時(shí),一般都使用bind系統(tǒng)調(diào)用。關(guān)于bind系統(tǒng)調(diào)用語(yǔ)法,如下所示:

#include <sys/types.h>

#include <sys/socket.h>

?

int bind(int s,struct sockaddr *my_addr,socklen_t addrlen);

在變量s中,指定的是利用socket系統(tǒng)調(diào)用所打開的套接字的描述符。在my_addr指針中,指定自己的IP地址和端口號(hào)。在addrlen域中,指定結(jié)構(gòu)體my_addr的大小。

在利用bind系統(tǒng)調(diào)用將一個(gè)IP地址設(shè)置為INADDR_ANY的時(shí)候,該主機(jī)或路由器的所有IP地址都能夠接收到一個(gè)包。在主機(jī)中,除了NICIP地址之外,都帶有一個(gè)循環(huán)測(cè)試(loopback)的IP地址(127.0.0.1)。并且,在使用路由器等時(shí),由于準(zhǔn)備了多個(gè)接口,所以帶有多個(gè)IP地址。這時(shí),如果使用bind系統(tǒng)調(diào)用來(lái)指定一個(gè)IP地址,那么處理IP數(shù)據(jù)報(bào)的接收端地址和指定的IP地址之外,不能夠接收到通信。但是,在指定為INADDR_ANY時(shí),無(wú)論接收端的IP地址是什么,都能夠接收到包。

在服務(wù)器中,無(wú)論是TCP協(xié)議還是UDP協(xié)議,都必須使用bind系統(tǒng)調(diào)用來(lái)指定自己的端口號(hào)。在客戶機(jī)中,可以有操作系統(tǒng)來(lái)指定一個(gè)端口號(hào)。如果使用bind系統(tǒng)調(diào)用來(lái)指定的端口號(hào)時(shí),能夠自動(dòng)地分配一個(gè)端口號(hào)。另外,在TCP協(xié)議的客戶機(jī)中,可以省略執(zhí)行bind系統(tǒng)調(diào)用。

?

4.3 close()函數(shù)

在結(jié)束對(duì)套接字的使用的時(shí)候,使用close系統(tǒng)調(diào)用。關(guān)于close系統(tǒng)調(diào)用的語(yǔ)法,如下面的語(yǔ)句所示:

#include <unistd.h>

int close(int s);

在變量s中,存儲(chǔ)著利用socket系統(tǒng)調(diào)用所打開的套接字的描述符,或存儲(chǔ)著accept系統(tǒng)調(diào)用的返回值。

?

4.4 sendto()函數(shù)

? ? 套接字函數(shù)可以分為兩種:一種為無(wú)連接型的函數(shù),另一種為面向連接型的函數(shù)。

? ? #include <sys/types.h>

? ? #include <sys/socket.h>

?

? ? int sendto(int s,const void *msg,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);

raw IP協(xié)議中,必須使用無(wú)連接型的函數(shù)。在UDP協(xié)議中,通常都是使用無(wú)連接型的函數(shù),但是,也可以使用面向連接型的函數(shù)。在TCP協(xié)議中,必須使用面向連接型的函數(shù)。

UDP協(xié)議中,如果沒有使用connect系統(tǒng)調(diào)用,則可以使用一個(gè)無(wú)連接型的函數(shù)。如果使用connect系統(tǒng)調(diào)用,也可以使用一個(gè)面向連接型的函數(shù)。

對(duì)于報(bào)文的發(fā)送與接收,可以利用sendto系統(tǒng)調(diào)用或recvfrom系統(tǒng)調(diào)用。在發(fā)送報(bào)文的時(shí)候,使用sendto系統(tǒng)調(diào)用;而在接收?qǐng)?bào)文的時(shí)候,則使用recvfrom系統(tǒng)調(diào)用。無(wú)論是哪一種系統(tǒng)調(diào)用,都必須在實(shí)際參數(shù)中指定訪問sockaddr結(jié)構(gòu)體的指針。

在變量s中,指定的是一個(gè)利用socket系統(tǒng)調(diào)用所打開的端口號(hào)描述符。在msg結(jié)構(gòu)體中,存儲(chǔ)著所發(fā)送報(bào)文的存儲(chǔ)器的初始地址,在len變量中,指定的是所發(fā)送報(bào)文的字節(jié)數(shù);在to結(jié)構(gòu)體中,指定的是接收端的IP地址和接收端的端口號(hào);在tolen變量中,指定結(jié)構(gòu)體to的大小;

flags變量中,通常指定為0。

sendto系統(tǒng)調(diào)用的返回值是已經(jīng)發(fā)送報(bào)文的字節(jié)數(shù)。嚴(yán)格地來(lái)講,該返回值并不是在計(jì)算機(jī)網(wǎng)絡(luò)上所傳輸?shù)淖止?jié)數(shù),而是從應(yīng)用程序傳遞給套接字模塊的字節(jié)數(shù)。在發(fā)生錯(cuò)誤時(shí),返回值為-1

?

4.5 recvfrom()函數(shù)

#include <sys/types.h>

#include <sys/socket.h>

int recvfrom(int s,void *hbuf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen);

在變量s中,指定的是利用socket系統(tǒng)調(diào)用所打開的一個(gè)套接字描述符。在buf指針中,存儲(chǔ)的是所接收到報(bào)文的緩沖區(qū)的起始地址;len變量中指定的是buf中所能夠存儲(chǔ)的最大字節(jié)數(shù)。在from結(jié)構(gòu)體中,存儲(chǔ)著所接收到的包的發(fā)送端IP地址和發(fā)送端端口號(hào);在fromlen變量中,存儲(chǔ)這結(jié)構(gòu)體from的大小;在flags變量中,通常指定為0。

recvfrom系統(tǒng)調(diào)用的返回值是所接收到的報(bào)文的字節(jié)數(shù)。在發(fā)生錯(cuò)誤時(shí),返回值為-1

?

?

4.6 connect()函數(shù)

? ? 在指定通信對(duì)方的IP地址的時(shí)候,通常采用connect系統(tǒng)調(diào)用。在TCP協(xié)議中,需要傳輸建立連接請(qǐng)求的包。在UDP協(xié)議中,并不使用sendto系統(tǒng)調(diào)用或recvfrom系統(tǒng)調(diào)用,而是通過send系統(tǒng)調(diào)用或recv系統(tǒng)調(diào)用進(jìn)行通信。connect系統(tǒng)調(diào)用的語(yǔ)法格式如下:

? ? #include <sys/types.h>

#include <sys/socket.h>

int connect(int s,const struct sockaddr *addr,socklen_t addrlen);

在變量s中,指定的是利用socket系統(tǒng)調(diào)用所打開的一個(gè)套接字描述符。在addr結(jié)構(gòu)體中,指定的是通信對(duì)方的IP地址和端口號(hào)。在addrlen變量中,指定的是addr結(jié)構(gòu)體的大小。

?

4.7 listen()函數(shù)

當(dāng)服務(wù)器接收到TCP協(xié)議連接的時(shí)候,執(zhí)行一個(gè)listen系統(tǒng)調(diào)用。關(guān)于listen系統(tǒng)調(diào)用的語(yǔ)法格式如下:

#include <sys/socket.h>

int listen(int s,int backlog);

在變量s中,存儲(chǔ)著利用socket系統(tǒng)調(diào)用所打開的一個(gè)套接字描述符。在backlog變量中,指定的是隊(duì)列的長(zhǎng)度。如果listen系統(tǒng)調(diào)用正常,則返回值為0;否則發(fā)生錯(cuò)誤時(shí),返回值為-1

?

4.8 getaddrinfo()函數(shù)

IPv4中使用gethostbyname()函數(shù)完成主機(jī)名到地址解析,但是該API不允許調(diào)用者指定所需地址類型的任何信息,返回的結(jié)構(gòu)只包含了用于存儲(chǔ)IPv4地址的空間。為了解決該問題,IPv6中引入了getaddrinfo()的新API,它是協(xié)議無(wú)關(guān)的,既可用于IPv4也可用于IPv6。getaddrinfo函數(shù)能夠處理名字到地址以及服務(wù)到端口這兩種轉(zhuǎn)換,調(diào)用該函數(shù)會(huì)獲得一個(gè)addrinfo結(jié)構(gòu)的列表,調(diào)用的返回值是addrinfo的結(jié)構(gòu)(列表)指針。

#include <sys/socket.h>

#include <netdb.h>

int getaddrinfo(const char* nodename,const char* servname,

const struct addrinfo* hints,struct addrinfo** res);

nodename域指定了一個(gè)域名或IP地址。在這個(gè)域中,既能夠指定IPv6協(xié)議的地址,又能夠指定IPv4協(xié)議的地址。servname域能夠指定表示端口號(hào)的服務(wù)器名或表示端口號(hào)的數(shù)字。在指定一個(gè)域名或服務(wù)器名的時(shí)候,與使用C語(yǔ)言處理字符串的規(guī)則是相同的,在字符串的最后要追加“/0。

hints域中,指定想要獲得的信息。例如,在只想獲得與IPv6協(xié)議有關(guān)的信息時(shí),在addrinfo結(jié)構(gòu)體的ai_family中設(shè)定AF_INET6之后,再指定hints。在不特別指定時(shí),將hints域設(shè)置為NULL。

res域中,使用一個(gè)addrinfo結(jié)構(gòu)體來(lái)保存;列表結(jié)構(gòu)的開始地址。

IPv6協(xié)議中,在一個(gè)端口能夠指定多個(gè)IP地址。并且在過渡期中,也有將IPv6地址和IPv4地址這兩種地址都賦予一個(gè)NIC(網(wǎng)絡(luò)適配器、網(wǎng)卡)的情況存在。調(diào)用一次getaddrinfo函數(shù),即可檢索到所以的IP地址,并使用一個(gè)addrinfo結(jié)構(gòu)體存儲(chǔ)到該列表結(jié)構(gòu)中。

?

4.9 getnameinfo()函數(shù)

? ? getnameinfo()getaddrinfo()的互補(bǔ)函數(shù)。它把一個(gè)套接字地址轉(zhuǎn)換為對(duì)應(yīng)的主機(jī)名和服務(wù)。它是一個(gè)“協(xié)議無(wú)關(guān)”的函數(shù),既能處理IPv4地址,又能處理IPv6地址。它集合了gethostbyaddrin()函數(shù)和getservbyport()函數(shù)的功能,但getnameinfo()消除了地址族依靠的特性。

#include <sys/socket.h>

#include <netdb.h>

int getnameinfo(const struct sockaddr *sa, socklen_t salen,

? ? ? ? ? ? ? ? ? ? ?? char *host, size_t hostlen,

? ? ? ? ? ? ? ? ? ? ?? char *serv, size_t servlen, int flags);

sa域是一個(gè)指向sockaddr結(jié)構(gòu)體的指針,其中指定了IP地址和端口號(hào)。

host域是存放主機(jī)名的字符型指針,而serv域是存放服務(wù)的指針,即端口號(hào)。

flag域用于控制getnameinfo()的操作,它允許的值如下面所列:

NI_DGRAM

當(dāng)知道處理的是數(shù)據(jù)報(bào)套接口的時(shí)候,調(diào)用者應(yīng)該設(shè)置NI_DGRAM標(biāo)志,因?yàn)樵谔捉涌诘刂方Y(jié)構(gòu)中給出的僅僅是IP地址和端口號(hào),getnameinfo無(wú)法就此確定所用協(xié)議是TCP還是UDP。比如端口514,在TCP端口上提供rsh服務(wù),而在UDP端口上則提供syslog服務(wù)。

NI_NOFQDN

該標(biāo)志導(dǎo)致返回的主機(jī)名稱被截去第一個(gè)點(diǎn)號(hào)之后的內(nèi)容。比如假設(shè)套接口結(jié)構(gòu)中的IP地址為91.168.42.2,那么不設(shè)置該標(biāo)志返回的主機(jī)名為sina.aiwen.com,那么如果設(shè)置了該標(biāo)志后返回的主機(jī)名則為sina。

NI_NUMERICHOSTNI_NUMERICSERVNI_NUMERICSCOPE

NI_NUMERICHOST標(biāo)志通知getnameinfo不要調(diào)用DNS而是以數(shù)值表達(dá)格式作為字符串返回IP地址類似的NI_NUMERICSERV標(biāo)志指定以十進(jìn)制數(shù)格式作為字符串返回端口號(hào)以代替查找服務(wù)名NI_NUMERICSCOPE則指定以數(shù)值格式作為字符串返回范圍標(biāo)識(shí)以代替其名字。

NI_NAMEREQD

該標(biāo)志通知getnameinfo函數(shù)如果無(wú)法適用DNS反向解析出主機(jī)名則直接返回一個(gè)錯(cuò)誤。需要把客戶的IP地址映射成主機(jī)名的那些服務(wù)器可以使用該特性。

?

?

4.10 inet_pton()函數(shù)和inet_ntop()函數(shù)

inet_pton函數(shù)是一個(gè)將域名或ASCII碼表示的IP地址變換為使用字節(jié)來(lái)表示的IP地址的函數(shù)。inet_ntop函數(shù)則是inet_pton函數(shù)的逆,即把使用字節(jié)來(lái)表示的IP地址轉(zhuǎn)為使用字符串所表示的IP地址。

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int inet_pton(int af,const char *src,void *dst);

const char *inet_ntop(int af,const void *src,char *dst,size_t size);

af域中,指定地址系列;在srcdst域中,分別指定變換前所存儲(chǔ)的地址信息以及返回后所存儲(chǔ)的地址信息。在inet_ntopsize域中,指定了從dst開始的緩沖區(qū)的大小。如果不指定充分大的緩沖區(qū),則不能夠進(jìn)行變換處理。

?

4.11 memset()函數(shù)

? ? void *memset(void *s,int c,size_t n)

memset函數(shù)用來(lái)對(duì)一段內(nèi)存空間全部設(shè)置為某個(gè)字符,常用于內(nèi)存空間初始化。將已開辟內(nèi)存空間s的首n個(gè)字節(jié)的值設(shè)為值c。

?

4.12 memcpy()函數(shù)

extern void *memcpy(void *dest, void *src, unsigned int count);

memcpy函數(shù)把src所指內(nèi)存區(qū)域復(fù)制count個(gè)字節(jié)到dest所指內(nèi)存區(qū)域,但srcdest所指內(nèi)存區(qū)域不能重疊,函數(shù)返回指向dest的指針。memcpy用來(lái)做內(nèi)存拷貝,你可以拿它拷貝任何數(shù)據(jù)類型的對(duì)象,而strcpy就只能拷貝字符串了,它遇到'/0'就結(jié)束拷貝。

?

5.程序的地址族無(wú)關(guān)性

5.1 地址族無(wú)關(guān)的概念

在套接字編程中,我們經(jīng)常說(shuō)到程序必須實(shí)現(xiàn)地址族無(wú)關(guān)(address-family independent)。什么是地址族無(wú)關(guān)呢?所謂的地址族無(wú)關(guān),就是要求程序在處理IP地址時(shí),能消除不同IP地址間的差異性,可對(duì)不同的IP地址進(jìn)行統(tǒng)一的無(wú)差別的處理。不需修改程序,即可對(duì)不同的IP地址進(jìn)行預(yù)定的處理,實(shí)現(xiàn)相應(yīng)功能。

?

5.2 為什么程序需要地址族無(wú)關(guān)

?? 程序具備地址族無(wú)關(guān)性可以使程序消除一系列不足,使程序更具靈活性:

(1)為了支持IPv4/v6雙棧環(huán)境,網(wǎng)絡(luò)程序必須能夠同時(shí)正確處理IPv4IPv6。如果在程序中規(guī)定了地址族為AF_INETAF_INET6,那么程序?qū)o(wú)法在IPv4/v6雙棧環(huán)境中正確運(yùn)行。

(2)當(dāng)一個(gè)新的協(xié)議投入使用后,我們總是希望以前的網(wǎng)絡(luò)程序能夠適應(yīng)新協(xié)議,而不需為了適應(yīng)新協(xié)議而對(duì)程序進(jìn)行重寫。這包括在IP層,雖然現(xiàn)在還沒有開發(fā)IPv7的計(jì)劃,但誰(shuí)也不能肯定未來(lái)會(huì)不會(huì)開發(fā)。在傳輸層亦如此。

(3)目前,已經(jīng)有足夠的工具支持網(wǎng)絡(luò)程序的地址族無(wú)關(guān),比如sockaddr_storagegetaddrinfogetnameinfo。

(4)在一些操作系統(tǒng)中可能不支持地址族。如果在編程中引入了地址族,可能會(huì)導(dǎo)致程序不能正確執(zhí)行。而程序的地址族無(wú)關(guān)可以解決此類問題。

(5)程序的地址族無(wú)關(guān)可以使程序更簡(jiǎn)潔,提高程序的移植性。

(6)有些應(yīng)用程序接口(API)不支持IPv6,比如gethostbyname()。

?

5.3 AF_INET6替代AF_INETsockaddr_in6替代sockaddr_in的不足

在重寫IPv4依靠(IPv4 dependent)程序的時(shí)候,能不能只是簡(jiǎn)單地把AF_INET代替為AF_INET6,sockaddr_in替代為sockaddr_in6,而使程序地址族無(wú)關(guān)呢?

這樣做有幾個(gè)缺點(diǎn):

首先,用gethostbyname2(3),程序只能連接IPv6目的地址,而不能連接IPv4目的地址。在一個(gè)IPv4/v6雙棧環(huán)境中,FQDN(Fully Qualified Domain Name,完全合格域名/全稱域名,是指主機(jī)名加上全路徑,全路徑中列出了序列中所有域成員)可以被分解為多個(gè)IPv4地址和多個(gè)IPv6地址??蛻舳藨?yīng)該盡可能地連接分解出來(lái)的IP地址,而不只是連接IPv6地址。

第二,IPv6支持范圍IPv6地址(scoped IPv6 addresses),用gethostbyname2(3)并不能處理范圍IPv6地址,因?yàn)?/span>gethostbyname2(3)不返回范圍標(biāo)識(shí)符(scope? identification)。

第三,在程序中指定地址族為AF_INET6將使程序只能在支持IPv6的內(nèi)核中運(yùn)行,因?yàn)橐粋€(gè)不支持IPv6的內(nèi)核通常沒有AF_INET6套接字的支持。如果想讓一個(gè)單一雙態(tài)程序(既能處理IPv4地址,又能處理IPv6地址)能正確運(yùn)行在IPv4-only內(nèi)核、IPv6內(nèi)核和IPv4/v6雙棧內(nèi)核中,地址族無(wú)關(guān)是必須的。

第四,這樣的程序并不能適應(yīng)未來(lái)的需要。如果一些新協(xié)議投入使用,這樣的程序?qū)⒉豢杀苊庵貙憽?/span>IPv4IPv6的過渡的花費(fèi)是巨大的,在此過渡過程中,把其他問題一起解決未嘗不是一件好事。

第五,用地址族無(wú)關(guān)方法進(jìn)行編程,可以使程序獲得更高的移植性和穩(wěn)定性。

?

5.4 套接字編程的地址族無(wú)關(guān)指南

1)使用sockaddrs用于地址表示

為了處理IPv4IPv6地址,建議使用sockaddrs,如sockaddr_insockaddr_in6。用sockaddrs,可以使數(shù)據(jù)包含地址族的標(biāo)識(shí),這樣的話我們就可以在傳遞地址數(shù)據(jù)時(shí)知道其地址族。

當(dāng)需要一個(gè)預(yù)留空間給一個(gè)sockaddr時(shí),可以使用結(jié)構(gòu)體sockaddr_storage。結(jié)構(gòu)體sockaddr_storage有足夠大的空間來(lái)存儲(chǔ)任何類型的sockaddr。

使用sockaddr的另一個(gè)重要原因是一個(gè)IPv6地址并不能唯一地確定一個(gè)端點(diǎn),還必須加上一個(gè)一個(gè)范圍標(biāo)識(shí)符,指定出口端(outgoing interface)。

2)把文本表示轉(zhuǎn)換為sockaddrs。利用getaddrinfo(3)可以實(shí)現(xiàn)。

3)把二進(jìn)制地址表示轉(zhuǎn)換為文本??梢岳?/span>getnameinfo(3)實(shí)現(xiàn)。

?

?

6IPv6套接字編程

6.1? 編寫能處理IPv6地址的程序

為了使程序能夠處理IPv6地址,我們知道可以用基于socket的應(yīng)用程序接口,通過使用getaddrinfogetnameinfo來(lái)使程序具備地址族無(wú)關(guān)的能力。

getaddrinfo()應(yīng)用舉例:

const struct sockaddr * foo(hostname,servname)

? ?? const char *hostname;

? ?? const char *servname;

{

? ?? struct addrinfo hints,*res;

? ?? static struct sockaddr_storage ss;

? ?? int error;

?

? ?? memset(&hints,0,sizeof(hints));

? ?? hints.ai_socktype=SOCK_STREAM;

? ?? error=getaddrinfo(hostname,servname,&hints,&res);

? ?? if(error){

? ? ? ? fprintf(stderr,”%s/%s:%s/n”,hostname,servname,gai_strerror(error));

? ? ? ? exit(1);

? ? }

?

? ? if(res->ai_addrlen sizeof(ss)){

? ? ? ? fprintf(stderr,”sockaddr too large/n”);

? ? ? ? exit(1);

? ? }

? ? memcpy(&ss,res->aiaddr,res-ai_addrlen);

? ? freeaddrinfo(res);

? ? return(const struct sockaddr *)&ss;

}

getaddrinfo(3)非常靈活,具有許多狀態(tài)操作。比如,如果你想避免DNS lookup,你可以在hints.ai_flags域中指定為AI_NUMERICHOST。通過AI_NUMERICHOSTgetaddrinfo(3)將只接收數(shù)字表示的地址。

getaddrinfo(3)用范圍識(shí)別(scope identification)處理IPv6字符串地址,所以程序不需要對(duì)范圍識(shí)別做任何的特殊處理。

?

getnameinfo(3)也非常靈活,既支持?jǐn)?shù)字的地址,也支持FQDNFully Qualified Domain Name:完全合格域名/全稱域名,是指主機(jī)名加上全路徑,全路徑中列出了序列中所有域成員)表示的地址。getnameinfo(3)同時(shí)也可以把端口號(hào)轉(zhuǎn)換成字符串。所以getnameinfo(3)能同時(shí)支持IPv4IPv6,并不需要區(qū)分是支持IPv4還是支持IPv6。最后一個(gè)參數(shù)可以控制getnameinfo(3)的行為。

struct sockaddr *sa;

char hbuf[NI_MAXHOST];

sbuf[NI_MAXSERV] ;

int error ;

?

error=getnameinfo(sa,salen,hbuf,sizeof(hbuf),

?? NI_NUMERICHOST|NI_NUMERICSERV);

if(error){

fprintf(stderr,”error:%s/n”,gai_strerror(error));

exit(1);

}

fprintf(“addr:%s port:%s/n”,hbuf,sbuf);

?

error=getnameinfo(sa,salen,hbuf,sizeof(hbuf),0);

?

if(error){

fprintf(stderr,”error:%s/n”,gai_strerror(error));

exit(1);

}

fprintf(“addr:%s port:%s/n”,hbuf,sbuf);

?

error=getnameinfo(sa,salen,hbuf,sizeof(hbuf),NULL,0,NI_NAMEREQD);

if(error){

fprintf(stderr,”error:%s/n”,gai_strerror(error));

exit(1);

}

printf(“FQDN:%s/n”,hbuf);

?

getnameinfo(3)需要生成IPv6地址字符串的范圍標(biāo)識(shí),但也沒必要擔(dān)心在sin6_scope_id域中的范圍標(biāo)識(shí)。

?

6.2移植可用的程序來(lái)支持IPv6

為了找到需要重寫的部分,我們需要找出IPv4依賴的功能調(diào)用和IPv4依賴的數(shù)據(jù)類型:

%grep gethostby *.c *.h

%grep inet_aton *.c *.h

%grep sockaddr_in *.c *.h

%grep in_addr *.c *.h

然而,如果程序編寫不正確并且在intu_int32_t中傳遞32位的二進(jìn)制表示的IPv4地址的話,對(duì)in_addr將毫無(wú)用處,還需識(shí)別在哪個(gè)變量中存儲(chǔ)了IPv4地址。

如果套接字編程借口(socket API)是由單個(gè)*.c文件生成,那么將容易移植。否則,我們必須弄清IPv4依靠的數(shù)據(jù)是怎樣傳送的,然后把它們重寫成協(xié)議族無(wú)關(guān)的程序。有時(shí)候,IPv4依靠的數(shù)據(jù)類型用在結(jié)構(gòu)體定義和功能原型中。在這種情況下,我們需要辨別出地址族無(wú)關(guān)的代碼。

下面的例子是IPv4依賴的部分程序代碼:

struct foo{

struct sockaddr_in dst;

}

?

struct foo *

setaddr(in)

? ? ? struct in_addr in;

{

? ? ? struct foo *foo;

?

? ? ? foo=malloc(sizeof(*foo));

? ? ? if(!foo)

? ? ? ? ? return NULL;

? ? ? memset(foo,0,sizeof(*foo));

?

? ? ? ? ? foo->dst.sin_family=AF_INET;

? ? ? ? ? foo->dst.sin_addr=in;

? ? ? ? ? return foo;

}

改變結(jié)構(gòu)體的定義是比較簡(jiǎn)單的,要么把結(jié)構(gòu)體改為struct sockaddr_storage,要么定義一個(gè)struct addrinfo*。而改為功能原型則要難得多。有時(shí)候,傳遞struct sockaddr *會(huì)容易些,但如果你要處理多個(gè)地址的話,用struct addrinfo *會(huì)顯得更明智些。

下面是重寫后的程序,實(shí)現(xiàn)了地址族無(wú)關(guān),但不支持多地址:

struct foo{

? ?? struct sockaddr_storage dst;

};

struct foo *

setaddr(sa,salen)

? ? ?? struct sockaddr *sin;

? ? ?? socklen_t salen;

{

? ? ?? struct foo *foo;

? ? ? ? ?? if(salen>sizeof(foo->dst))

? ? ? ? ? ? ? ? return NULL;

?

? ? ? ? ?? foo=malloc(sizeof(*foo));

? ? ? ? ?? if(!foo)

? ? ? ? ? ? ? return NULL;

? ? ? ? ?? memset(foo,0,sizeof(*foo));

?

? ? ? ? ?? memcpy(&foo->dst,sa,salen);

? ? ? ? ?? return foo;

}

如果由于一些限制不能使用select(2)poll(2),可以運(yùn)行兩個(gè)應(yīng)用程序的實(shí)例,一個(gè)用于AF_INET socket,另一個(gè)用于AF_INET6,這樣就可以同時(shí)處理IPv4節(jié)點(diǎn)和IPv6節(jié)點(diǎn)。

?

?

?

?

?

?

結(jié)論及尚存在的問題

IPv6的主要優(yōu)勢(shì)體現(xiàn)在以下幾方面:擴(kuò)大地址空間、提高網(wǎng)絡(luò)的整體吞吐量、改善服務(wù)質(zhì)量(QoS)、安全性有更好的保證、支持即插即用和移動(dòng)性、更好實(shí)現(xiàn)多播功能。顯然,IPv6的優(yōu)勢(shì)能夠直接或間接地解決IPv4存在的諸多問題。其中最突出的是IPv6大大地?cái)U(kuò)大了地址空間,恢復(fù)了原來(lái)因地址受限而失去的端到端連接功能,為互聯(lián)網(wǎng)的普及與深化發(fā)展提供了基本條件。當(dāng)然,IPv6并非十全十美、一勞永逸,不可能解決所有問題。IPv6只能在發(fā)展中不斷完善,也不可能在一夜之間發(fā)生,過渡需要時(shí)間和成本,但從長(zhǎng)遠(yuǎn)看,IPv6有利于互聯(lián)網(wǎng)的持續(xù)和長(zhǎng)久發(fā)展。

由于IPv6相關(guān)技術(shù)目前還不是很成熟,其應(yīng)用范圍還不是很廣,很多方面還只是停留在研究與實(shí)驗(yàn)階段,因此本畢業(yè)論文的討論范圍也涉及不深,只作淺層次的探索討論;在程序移植性方面也不是做的很好,本設(shè)計(jì)的代碼是在Linux平臺(tái)下調(diào)試運(yùn)行的,因此在跨平臺(tái)方面還有待提高。

在這次畢業(yè)設(shè)計(jì)的過程中,我查閱了大量相關(guān)的書籍與相關(guān)知識(shí),通過自學(xué)與老師的指導(dǎo),我對(duì)畢業(yè)設(shè)計(jì)的要求與內(nèi)容有了深刻的了解,并通過自己的理解與分析思考,圓滿完成了畢業(yè)論文。在完成畢業(yè)設(shè)計(jì)的過程中,我對(duì)“一份耕耘,一份收獲”有了深刻的認(rèn)識(shí),只要你付出了,你就一定會(huì)有收獲。在畢業(yè)設(shè)計(jì)期間,我認(rèn)真查閱資料,虛心請(qǐng)教老師與同學(xué),并仔細(xì)弄清相關(guān)理論知識(shí),理清思路,詳細(xì)構(gòu)思規(guī)劃,很快就有了一個(gè)大致的框架,在后期通過修改與完善,我的論文完成了。在此過程中,我付出了努力,而我也取得了收獲。我學(xué)到了很多以前課堂上沒學(xué)到的知識(shí),在IPv6相關(guān)知識(shí)方面有了更深的理解,并在此過程中,我的查閱資料能力、整體構(gòu)思規(guī)劃能力、解決問題的能力也得到了提高,我從中受益甚大。

?

?

?

大學(xué)生活即將畫上記號(hào),而于我的人生卻只是一個(gè)逗號(hào),我將面對(duì)又一次征程的開始。四年的求學(xué)生涯在師長(zhǎng)、親友的大力支持下,走得辛苦卻也收獲滿囊,在論文即將付梓之際,思緒萬(wàn)千,心情久久不能平靜。偉人、名人為我所崇拜,可是我更急切地要把我的敬意和贊美獻(xiàn)給我的大學(xué)老師們,是你們的無(wú)私教導(dǎo)讓我從稚嫩走向成熟,而我也從中學(xué)到各種知識(shí)與智慧,讓我可以更容易地面對(duì)社會(huì)生活。在這里,我要特別感謝我的指導(dǎo)老師羅海天老師,他給了我很大的幫助,幫我解決了畢業(yè)設(shè)計(jì)中遇到的很多問題。從論文題目的選定到論文寫作的指導(dǎo),經(jīng)由他悉心的點(diǎn)撥,再經(jīng)思考后的領(lǐng)悟,常常讓我有山重水復(fù)疑無(wú)路,柳暗花明又一村的感覺。授人以魚不如授人以漁,置身其間,耳濡目染,潛移默化,使我不僅接受了全新的思想觀念,樹立了宏偉的學(xué)術(shù)目標(biāo),領(lǐng)會(huì)了基本的思考方式,從羅老師的身上,我受益良多。

感謝我的爸爸媽媽,焉得諼草,言樹之背,養(yǎng)育之恩,無(wú)以回報(bào),你們永遠(yuǎn)健康快樂是我最大的心愿。在論文即將完成之際,我的心情無(wú)法平靜,從開始進(jìn)入課題到論文的順利完成,有多少可敬的師長(zhǎng)、同學(xué)、朋友給了我無(wú)言的幫助,在這里請(qǐng)接受我誠(chéng)摯謝意!同時(shí)也感謝學(xué)院為我提供良好的做畢業(yè)設(shè)計(jì)的環(huán)境。最后再一次感謝所有在畢業(yè)設(shè)計(jì)中曾經(jīng)幫助過我的良師益友和同學(xué),以及在設(shè)計(jì)中被我引用或參考的論著的作者。

本畢業(yè)設(shè)計(jì)是我學(xué)習(xí)生涯的最后一份答卷,也是我作為學(xué)生交給母校最后的一份答卷,盡管傾注了我數(shù)月來(lái)的心血和汗水,卻由于自己的基礎(chǔ)知識(shí)不夠扎實(shí),能力確實(shí)有限,多多少少存在著這樣那樣的缺陷。然而,畢竟已經(jīng)盡力,已無(wú)憾矣!

?

?

?

?

?

附錄

程序1 client-gethostby.c:TCP客戶端實(shí)例——通過host/port與服務(wù)端通信,并從服務(wù)端接收信息。該程序不支持IPv6。

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <string.h>

#include <stdlib.h>

#include <arpa/inet.h>

?

int main_P((int,char **));

?

int main(argc,argv)

? ? ? ? int agrc;

? ? ? ? char **argv;

{

? ? ? ? struct hostent *hp;

? ? ? ? struct servent *sp;

? ? ? ? unsigned long lport;

? ? ? ? u_int16_t? port;

? ? ? ? char *ep;

? ? ? ? struct sockaddr_in dst ;

? ? ? ? int dstlen ;ssize_t l ;

? ? ? ? int s;

? ? ? ? char hbuf[INET_ADDRSTRLEN];

? ? ? ? char buf[1024];

?

/*檢查參數(shù)個(gè)數(shù)*/

? ? ?? if(argc!=3){

? ? ? ?? fprintf(stderr,”usage:test host port/n”);

? ? ? ?? exit(1);

? ? ?? }

? ? ?? /*把主機(jī)名解釋為IP*/

? ? ?? hp=gethostbyname(argv[1]);

? ? ?? if(!hp){

? ? ? ?? fprintf(stderr,”%s:%s/n”,argv[1],hstrerror(h_errno));

? ? ? ?? exit(1);

? ? ? }

? ? ? if(hp->h_length!=sizeof(dst.sin_addr)){

? ? ? ?? fprintf(stderr,”%s:unexpected address length/n”,argv[1]);

? ? ? ?? exit(1);

? ? ? }

? ? ? /*解析端口號(hào)*/

? ? ? sp=getservbyname(argv[2],”tcp”);

? ? ? if(sp){

? ? ? ?? port=sp-s_port& 0xffff;

? ? ? }else{

? ? ? ? ?? ep=NULL;errno=0;

? ? ? ? ?? lport=strtoul(argv[2],&ep,10);

? ? ? ? ?? if(!*argv[2] || errno || !ep || *ep){

? ? ? ? ? ?? fprintf(stderr,”%s:no such service/n”,agrv[2]);

? ? ? ? ? ?? exit(1);

? ? ? ? ? ?}

? ? ? ? ?? if(lport & ~0xffff){

? ? ? ? ? ?? fprintf(stderr,”%s:out of range/n”,argv[2]);

? ? ? ? ? ?? exit(1);

? ? ? ? ?? }

? ? ? ? ?? port=htons(lport & 0xffff);/*將主機(jī)的無(wú)符號(hào)短整數(shù)型數(shù)轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序12 34—>34 12*/

? ? ?? }

? ? ?? endservent() ;

?

? ? ?? /*只嘗試第一個(gè)地址*/

? ? ?? memset(&dst,0,sizeof(dst)) ;

? ? ?? dst.sin_family=AF_INET ;

? ? ?? /*Linux/Solaris系統(tǒng)不需要下面的一行*/

? ? ?? dst.sin_len=sizeof(struct sockaddr_in) ;

? ? ?? memcpy(&dst.sin_addr,hp->h_addr,sizeof(dst.sin_addr)) ;

? ? ?? dst.sin_port=port ;

? ? ?? dstlen=sizeof(struct sockaddr_in) ;

?

? ? ?? s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

? ? ?? if(s<0){

? ? ? ? ? perror(“socket”);

? ? ? ? ? exit(1);

? ? ?? }

?

? ? ?? inet_ntop(AF_INET,hp->h_addr,hbuf,sizeof(hbuf));

? ? ?? fprintf(stderr,”trying %s port %u/n”,,hbuf,ntohs(port));

?

? ? ?? if(connect(s,(struct sockaddr *)&dst,dstlen)<0){

? ? ? ? ? perror(“connect”);

? ? ? ? ? exit(1);

? ? ?? }

? ? ?? while((l=read(s,buf,sizeof(buf))>0)

? ? ? ? ? ?? write(STDOUT_FILENO,buf,l);close(s);

? ? ? ? ? ?? exit(0);

?? ? ? }

?

程序2 client-getaddrinfo.c:在程序6-1的基礎(chǔ)上使程序?qū)崿F(xiàn)地址族無(wú)關(guān)。

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet.h>

#include <netdb.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <string.h>

?

int main_P((int,char **));

?

int main(argc,argv)

? ? ? ? int agrc;

? ? ? ? char **argv;

{

? ? ? ? struct addrinfo hints,*res,*res0;

? ? ? ? ssize_t l;

? ? ? ? int s;

? ? ? ? char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];

? ? ? ? char buf[1024] ;

? ? ? ? int error ;

?

/*檢查參數(shù)個(gè)數(shù)*/

? ? ?? if(argc!=3){

? ? ? ? ?fprintf(stderr,”usage:test host port/n”);

? ? ? ?? exit(1);

? ? ?? }

/*把地址/端口號(hào)轉(zhuǎn)換為sockaddr,基于getaddrinfo(3)返回的結(jié)果,程序代碼運(yùn)行于數(shù)據(jù)驅(qū)動(dòng)模式*/

? ? ?? memset(&hints,0,sizeof(hints)) ;

? ? ?? hints.ai_socktype=SOCK_STREAM;

? ? ?? error=getaddinfo(argv[1],argv[2],&hints,&res0);

? ? ?? if(error){

? ? ? ? ? fprintf(stderr,”%s %s/n”,argv[1],argv[1],gai_strerror(error));continue;

? ? ? ? ? exit(1);

/*嘗試所有的sockaddr直到通信成功*/

? ? ?? for(res=res0;res;res=res->ai_next){

? ? ? ? ?? error=getnameinfo(res->ai_aiaddr,res->ai_addrlen,hbuf,sizeof(hbuf),sbuf,

sizeof(sbuf),NI_NUMERICHOST | NI_NUMERICSERV);

?

? ? ? ? ?? if(error){

? ? ? ? ? ? ? fprintf(stderr,”%s%s:%s/n”,arg[1],argv[1],gai_sterror(error));

? ? ? ? ? ? ? continue;

? ? ? ? ?? }

? ? ? ? ?? fprintf(stderror,”trying %s port %s/n”,hbuf,sbuf);

?

? ? ? ? ?? s=socket(res->ai_family,res->ai_socktype,res->ai_protocol);

? ? ? ? ?? if(s<0)

? ? ? ? ?? continue;

?

? ? ? ? ?? if(connect(s,res-ai_addr,res-ai_addrlen)>0){

? ? ? ? ? ? ?? close(s);

? ? ? ? ? ? ?? s=-1;

? ? ? ? ? ? ?? continue;

? ? ? ? ?? }

?

? ? ? ? ?? while((l=read(s,buf,sizeof(buf)))<0)

? ? ? ? ? ? ? ? write(STDOUT_FILENO,buf,l);close(s);

? ? ? ? ? ? exit(0);

? ? ? ? ? ? }

? ? ? ? ? ? fprintf(stderr,”test:no destination to connect to/n”);

? ? ? ? ? ? exit(1);

? ? ? ? ? ? }

?

程序3 server-single.c 一個(gè)獨(dú)立的TCP服務(wù)器偵聽一個(gè)IPv4端口

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <string.h>

#include <stdlib.h>

#include <arpa/inet.h>

?

int main_P((int,char **));

?

int main(argc,argv)

? ? ? ? int agrc;

? ? ? ? char **argv;

{

? ? ? ? struct servent *sp;

? ? ? ? unsigned long lport;

? ? ? ? u_int16_t? port;

? ? ? ? char *ep;

? ? ? ? struct sockaddr_in serv ;

? ? ? ? int servlen ;struct sockaddr_in from;

? ? ? ? socklen_t fromlen;

? ? ? ? int s;

? ? ? ? int ls;

? ? ? ? char hbuf[INET_ADDRSTRLEN];

?

if(argc!=2){

? ? ? ? ?? fprintf(stderr,”usage:test port/n”);

? ? ? ? ?? exit(1);

? ? ?? }

? ? ?? sp=getservbyname(argv[1],”tcp”);

? ? ?? if(sp)

? ? ? ?? ? port=sp->s_port & 0xffff;

? ? ?? else{

? ? ? ? ?? ep=NULL;errno=0;

? ? ? ? ?? lport=strtoul(argv[1],&ep,10) ;

? ? ? ? ?? if(!*argv[1] || errno || !ep || *ep){

? ? ? ? ? ? ? frpintf(stderr,”%s: no such service/n”,argv[1]);

? ? ? ? ? ? ? exit(1);

? ? ? ?? ? }

? ? ? ? ?? if(lport & ~0xffff){

? ? ? ? ? ? ? fprintf(stderr,”%s: out of range/n”,argv[1]);

? ? ? ? ? ? ? exit(1);

? ? ? ? ?? }

? ? ? ? ? port=htons(lport &0xffff);

? ? ?? }

? ? ?? endservent() ;

?

? ? ?? memset(&serv,0,sizeof(serv)) ;

? ? ?? serv.sin_family=AF_INET ;

? ? ?? /*Linux/Solaris系統(tǒng)不需要下面的一行*/

? ? ?? serv.sin_len=sizeof(struct sockaddr_in) ;

? ? ?? serv.sin_port=port;

? ? ?? servlen=sizeof(struct sockaddr_in);

? ? ?? s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

? ? ?? if(s<0){

? ? ? ? ? perror(“socket”);

? ? ? ? ? exit(1);

? ? ?? }

?

? ? ?? if(bind(s,(struct sockaddr *)&serv,servlen)<0){

? ? ? ? ? perror(“bind”);

exit(1);

? ? ?? }

? ? ?? if(listen(s,5)<0{

perror(“l(fā)isten

exit(1);

? ? ?? }

?

? ? ?? while(l){

? ? ? ? ? fromlen=sizeof(from);

? ? ? ? ? ls=accept(s,(struct sockaddr *)&from,&fromlen);

? ? ? ? ? if(ls<0)? continue;

? ? ? ? ? if(from.sin_family!=AF_INET || fromlen!=sizeof(struct sockaddr_in)){

exit(1);

? ? ?? }

?

? ? ?? ? ?if(inet_ntop(AF_INET, &from.sin_addr,hbuf,sizeof(hbuf))= =NULL){

exit(1);

? ? ?? }

?

? ? ? ?? write(ls,”hello”,6);

? ? ? ?? write(ls,hbuf,strlen(hbuf));

? ? ? ?? write(ls,”/n”,l);

? ? ? ?? close(ls);

? ? ?? }

}

?

程序4 server-getaddrinfo.c 在程序6-3的基礎(chǔ)上使程序地址族無(wú)關(guān)

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <string.h>

#include <stdlib.h>

#include <arpa/inet.h>

?

#define MAXSOCK 20

?

int main_P((int,char **));

?

int main(argc,argv)

? ? ? ? int agrc;

? ? ? ? char **argv;

{

? ? ? ? struct addrinfo hints,*res,*res0;

? ? ? ? int error;

? ? ? ? struct sockaddr_storage from;

? ? ? ? socklen_t fromlen;

? ? ? ? int ls;

? ? ? ? int s[MAXSOCK];

? ? ? ? int smax;

? ? ? ? int sockmax;

? ? ? ? fd_set rfd,rfd0;

? ? ? ? int n;

? ? ? ? int i;

? ? ? ? char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];

#ifdef IPV6_V6ONLY

? ? ? ? const int on=1 ;

#endif

?if(argc!=2){

? ? ?? ? fprintf(stderr,”usage:test port/n”);

? ?? ? ? exit(1);

? ? ?? }

?

memset(&hints,0,sizeof(hints)) ;

? ? ?? hints.ai_socktype=SOCK_STREAM;

? ? ? ?hints.ai_flags=AI_PASSIVE;

? ? ?? error=getaddinfo(NULL,argv[1],&hints,&res0);

? ? ?? if(error){

? ? ? ? ? fprintf(stderr,”%s %s/n”,argv[1], gai_strerror(error));

? ? ? ? ? exit(1);

}

?

smax=0;

sockmax=-1;

for(res=res0;res &&smax rMAXSOCK; res=res->ai_next){

? ?? s[smax]=socket(res-ai_family,res-ai_socktype,res->ai_protocol);

?? if(s[smax]<0? continue;

?

/*避免FD_SET溢出*/

if(s[smax]=FD_SETSIZE){

?? close(s[smax]);

?? s[smax]=-1;

?? continue;

}

#ifdef IPV6_V6ONLY

? ? ?? if(res->ai_family= =AF_INET6 &&

setsockopt(s[smax],IPPROTO_IPV6,IPV6_V6ONLY,&on,

sizeof(on))<0){

?? perror(“bind”);

?? s[smax]=-1;

?? continue;

}

#endif

? ? ? ??

? ? ? ? ? ? if(bind(s[smax],res-ai_addr,res-ai_addrlen) 0){

? ? ? ? ? ? ? ? ? close(s[smax]);

? ? ? ? ? ? ? ? ? s[smax]=-1;

? ? ? ? ? ? ? ? ? continue;

? ? ? ?? }

? ? ? ?? if(listen(s[smax],5) 0){

? ? ? ? ? ? ? ? ? close(s[smax]);

? ? ? ? ? ? ? ? ? s[smax]=-1;

? ? ? ? ? ? ? ? ? continue;

? ? ? ? ?}

?

? ? ? ?? error=getnameinfo(res-ai_addr,res-ai_addrlen,hbuf,sizeof(hbuf),sbuf,sizeof(sbuf),

? ? ? ? ? ? ? ? ? NI_NUMERICHOST | NI_NUMERICSERV);

? ? ? ?? if(error){

fprintf(stderr,”test:%s/n”,gai_strerror(error));

? ? ? ? ? ? exit(1);

? ? ? ?? }

? ? ? ?? fprintf(stderr,”listen to %s %s /n”,hbuf,sbuf);

?

? ? ? ?? if(s[smax]>sockmax)

? ? ? ? ? ?sockmax=s[smax];

? ? ? ?? samx++;

? ? }

?

? ? if(smax= =0){

? ? ? ?? fprintf(stderr,”test:no socket to listen to/n”);

? ? ? ?? exit(1);

? ? }

?

? ? FD_ZERO(&rfd0);

? ? for(i=0;i<smax;i++)

? ? ? ?? FD_SET(s[i],&rfd0);

? ?? while(l){

? ? ? ?? rfd=rfd0;

? ? ? ? ?n=select(sockmax+1,&frd,NULL,NULL,NULL);

? ? ? ?? if(n<0){

? ? ? ? ?? perror(“select”);

? ? ? ? ?? exit(1);

? ? ? ?? }

? ? ? ?? fro(i=0;i<smax;i++){

? ? ? ? ?? if(FD_ISSET(s[i],&rfd)){

? ? ? ? ? ? ?? fromlen=sizeof(from);

? ? ? ? ? ? ?? ls=accept(s[i],(struct sockaddr *)&from &fromlen);

? ? ? ? ? ? if(ls<0)?? continue;

? ? ? ? ? ? write(ls,”hello/n”,6);

? ? ? ? ? ? close(ls);

? ? ? ? ? }

? ? ? }

?? }

}

?

轉(zhuǎn)載于:https://www.cnblogs.com/javaexam2/archive/2010/07/08/2632989.html

總結(jié)

以上是生活随笔為你收集整理的IPv6套接字编程介绍的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 成人午夜影视 | 黄色a区 | 精品国产999久久久免费 | 国产69视频在线观看 | 成人午夜一区 | 亚洲精品综合在线 | 日韩三级欧美 | 国产激情一区二区三区 | 黄色片网战| 国产在线中文 | 亚洲精品久久久蜜桃网尤妮丝 | 日本视频在线观看免费 | 99re视频在线观看 | 白丝一区 | 日本免费成人 | 国产尻逼视频 | 精品视频无码一区二区三区 | 国产又粗又大又爽视频 | 18禁肉肉无遮挡无码网站 | 免费a级片视频 | 国产黄在线免费观看 | 亚洲成人视屏 | 国产精品熟妇一区二区三区四区 | av片在线播放 | 日本久久久久 | 亚洲经典一区二区 | 一区在线视频 | 91亚洲网 | 欧美丰满熟妇xxxx | 黄wwwww | 天天做天天爱天天做 | 动漫一区二区三区 | 女女百合高h喷汁呻吟玩具 www.亚洲一区 | 97色伦97色伦国产欧美空 | 国产三区在线视频 | 国产一区二区三区在线观看视频 | 中文字幕女优 | 女同av在线播放 | 人人澡人人干 | 巨乳美女动漫 | 99久久99久久精品国产片桃花 | 男人操女人的视频 | 国产欧美日韩精品一区 | 免费视频亚洲 | 不用播放器av | 亚洲成人伦理 | 国产精品综合一区二区 | 少妇2做爰hd韩国电影 | 国产成人免费看一级大黄 | 一区二区三区人妻 | 久久影院午夜理论片无码 | 一级片在线观看视频 | 国产成人福利 | 亚洲九九视频 | 久久成人在线视频 | 激情五月婷婷小说 | 日韩怡春院 | 久草视频精品在线 | 欧美日韩一区二区三 | 国产人妻久久精品一区二区三区 | 国产精九九网站漫画 | 欧美激情h| 日韩短视频 | 97香蕉碰碰人妻国产欧美 | 91成人国产综合久久精品 | 欧美欧美欧美 | 国产精品二 | 日韩毛毛片 | 麻豆精品免费视频 | 国产1区2区3区4区 | 波多野结衣黄色 | 国产乱子伦农村叉叉叉 | 青青艹在线观看 | www.一区二区.com | a天堂视频 | 久久精品人人爽 | 美国黄色一级视频 | 亚洲一卡二卡 | 91丨九色| 亚洲呦呦| 欧美另类videosbestsex日本 | 中文字幕日韩一区二区三区不卡 | 久久久久久亚洲av无码专区 | 丰满人妻一区二区三区四区 | 国产精品丝袜黑色高跟 | 日本69视频 | 中文字幕丰满人伦在线 | av观看国产| 自拍偷拍第5页 | 在线观看中文字幕av | 男人日女人b视频 | 六月天综合网 | 一个综合色 | 欧美性生话 | 精品孕妇一区二区三区 | 国产精品jizz在线观看无码 | 免费日韩成人 | 国产精品久久777777 | 日韩在线一卡 |