Linux網路程式設計之IPv6
1.IPv6特點
我們已經學習過了流式套接字(SOCK_STREAM),資料報套接字(SOCK_DGRAM)和原始套接字(SOCK_RAWM),其中原始套接字的功能十分強大,能夠傳送自定義的資料包,偵聽網路上的資料,拒絕服務攻擊,傳送ICMP包等等。但這些協議都是基於IPv4,接下來,我們學習一下IPV6下的套接字程式設計. 下面介紹一個IPV6協議。相對於IPv4,IPv6有如下一些顯著的優勢:
(1)地址容量大大擴充套件,由原來的32位擴充到128位,徹底解決IPv4地址不足的問題;支援分層地址結構,從而更易於定址;擴充套件支援組播和任意播地址,這使得資料包可以傳送給任何一個或一組節點;
(2)大容量的地址空間能夠真正的實現無狀態地址自動配置,使IPv6終端能夠快速連線到網路上,無需人工配置,實現了真正的即插即用;
(3)報頭格式大大簡化,從而有效減少路由器或交換機對報頭的處理開銷,這對設計硬體報頭處理的路由器或交換機十分有利;
(4)加強了對擴充套件報頭和選項部分的支援,這除了讓轉發更為有效外,還對將來網路載入新的應用提供了充分的支援;
(5)流標籤的使用讓我們可以為資料包所屬型別提供個性化的網路服務,並有效保障相關業務的服務質量;
(6)認證與私密性:IPv6把IPSec作為必備協議,保證了網路層端到端通訊的完整性和機密性;
(7)IPv6在行動網路和實時通訊方面有很多改進。特別地,不像IPv4,IPv6具備強大的自動配置能力從而簡化了移動主機和區域網的系統管理。
2. IPv6地址型別
在RFC1884中指出了三種類型的IPv6地址,他們分別佔用不同的地址空間:
* 單點傳送:這種型別的地址是單個介面的地址。傳送到一個單點傳送地址的資訊包只會送到地址為這個地址的介面。
* 任意點傳送:這種型別的地址是一組介面的地址,傳送到一個任意點傳送地址的資訊包只會傳送到這組地址中的一個(根據路由距離的遠近來選擇)
* 多點傳送:這種型別的地址是一組介面的地址,傳送到一個多點傳送地址的資訊包會發送到屬於這個組的全部介面。
其中單播地址又包括:全域性可聚集的單播地址,站點本地地址和鏈路本地地址。3 IPv6地址表示:
對於128位的IPv6地址,考慮到IPv6地址的長度是原來的四倍,RFC1884規定的標準語法建議把IPv6地址的128位(16個位元組)寫成8個16位的無符號整數,每個整數用四個十六進位制位表示,這些數之間用冒號(:)分開,例如:3ffe:3201:1401:1:280:c8ff:fe4d:db39
希望手工管理IPv6地址的難度太大了,DHCP和DNS的必要性在這裡顯得更加明顯。為了簡化IPv6的地址表示,只要保證數值不變,就可以將前面的0省略。
比如:1080:0000:0000:0000:0008:0800:200C:417A
可以簡寫為:1080:0:0:0:8:800:200C:417A
另外,還規定可以用符號::表示一系列的0。那麼上面的地址又可以簡化為:1080::8:800:200C:417A
IPv6地址的字首(FP, Format Prefix)的表示和IPv4地址字首在CIDR中的表示方法類似。比如 0020:0250:f002::/48表示一個字首為48位的網路地址空間。
注意: 0:0:0:0:0:0:0:1 稱為本地迴環地址
鏈路本地地址:同一鏈路的相鄰節點的通訊,鏈路本地地址用於鄰居發現
站點本地地址:在企業內部可以使用的地址,相當於私網地址.
IPv6沒有廣播,它的功能正在被多播代替.
IPv4相容地址: ::a.b.c.d 相容a.b.c.d.
4. IPv6地址分配
RFC1881規定,IPv6地址空間的管理必須符合Internet團體的利益,必須是通過一箇中心權威機構來分配。目前這個權威機構就是IANA(Internet Assigned NumbersAuthority,Internet分配號碼權威機構)。 IANA會根據IAB(Internet ArchitectureBoard)和IEGS的建議來進行IPv6地址的分配。
目前IANA已經委派以下三個地方組織來執行IPv6地址分配的任務:
* 歐洲的RIPE-NCC(www.ripe.net)
* 北美的INTERNIC(www.internic.net)
* 亞太平洋地區的APNIC(www.apnic.net)
5. IPv6地址結構
struct sockaddr_in6{u_char sin6_len;
u_char sin6_family;//協議族
u_int16_t sin6_port;//埠號
u_int32_t sin6_flowinfo;//設定流標記
struct in6_addr sin6_addr;//地址
u_int32_t sin6_scope_id;//設定IPv6地址作用範圍,是可聚集的全球化地址,還是本地站地址,還是鏈路地址
}
struct in6_addr{
u_int8_t __u6_addr8[16];
}
in6addr_any表示本地任意地址.
6. IPv6客戶端與伺服器程式
伺服器:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
/**
IPv6伺服器端與客戶端設計
IPv6的地址結構
struct sockaddr_in6{
u_char sin6_len;
u_char sin6_family;//協議族
u_int16_t sin6_port;//埠號
u_int32_t sin6_flowinfo;//設定流標記
struct in6_addr sin6_addr;//地址
u_int32_t sin6_scope_id;//設定IPv6地址作用範圍,是可聚集的全球化地址,還是本地站地址,還是鏈路地址
}
struct in6_addr{
u_int8_t __u6_addr8[16];
}
**/
#define PORT 8888
#define BACKLOG 10
int main(int argc,char*argv[]){
int s;//伺服器套接字描述符
int sc;//用於客戶端通訊的套接字描述符
int ret;
int size;
struct sockaddr_in6 server_addr,client_addr;
int len=sizeof(struct sockaddr_in6);
//建立套接字描述符
s=socket(AF_INET6,SOCK_STREAM,0);
if(s==-1){
perror("socket error");
return -1;
}
//繫結
bzero(&server_addr,sizeof(server_addr));
server_addr.sin6_family=AF_INET6;
server_addr.sin6_port=htons(PORT);
server_addr.sin6_addr=in6addr_any;//IPv6任意地址
ret=bind(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror("bind error");
return -1;
}
//監聽
ret=listen(s,BACKLOG);
if(ret==-1){
perror("listen error");
return -1;
}
char buffer[1024];
//迴圈伺服器
while(1){
time_t now;
sc=accept(s,(struct sockaddr*)&client_addr,&len);
if(sc==-1){
perror("accept error");
return -1;
}
memset(buffer,0,1024);
inet_ntop(AF_INET6,&client_addr.sin6_addr,buffer,sizeof(buffer));
printf("a client from IP:%s,port %d,socket %d\n",buffer,client_addr.sin6_port,sc);
memset(buffer,0,sizeof(buffer));
size=recv(sc,buffer,1024,0);
if(size>0&&!strncmp(buffer,"TIME",4)){
now=time(NULL);
memset(buffer,0,sizeof(buffer));
sprintf(buffer,"%24s\r\n",ctime(&now));
send(sc,buffer,strlen(buffer),0);
}
close(sc);
}
close(s);
}
客戶端:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUFFERSIZE 1024
int main(int argc,char*argv[]){
int s;
int ret;
int size;
char buffer[BUFFERSIZE];
struct sockaddr_in6 server_addr;
struct sockaddr_in6 client_addr;
//建立套接字
s=socket(AF_INET6,SOCK_STREAM,0);
if(s<0){
perror("socket error");
return -1;
}
//將地址結構繫結到套接字
bzero(&server_addr,sizeof(server_addr));
server_addr.sin6_family=AF_INET6;
server_addr.sin6_port=htons(PORT);
server_addr.sin6_addr=in6addr_any;//任意地址
//連線
ret=connect(s,(struct sockaddr*)&server_addr,sizeof(server_addr));
//傳送請求
memset(buffer,0,sizeof(buffer));
strcpy(buffer,"TIME");
size=send(s,buffer,strlen(buffer),0);
if(size<=0){
perror("send error");
return -1;
}
bzero(buffer,sizeof(buffer));
size=recv(s,buffer,BUFFERSIZE,0);
if(size>0){
write(1,buffer,size);
}
close(s);//關閉套接字描述符
}
執行結果:
./ipv6c
Sun Feb 19 20:21:31 2012
總結:本文主要介紹了IPv6特點以及與IPv4的不同點,最後給出了IPv6伺服器與客戶端的例項.