程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 實例解析IPv6環境下的網絡編程

實例解析IPv6環境下的網絡編程

編輯:關於VC++

自IPv4誕生至今已有20多年了,目前它雖仍因互聯網的成功而風光無限,但是如同“Internet正在成為其自身巨大成功的受害者”一樣,目前IPv4地址的極度匮乏注定它將被歷史所淘汰。而IPv6—IPv4的繼承人,具有地址空間巨大,支持QOS等許多優良特性,在不久的將來會迅速的普及,但IPv6的出現將對目前網絡編程方式產生一定的影響。

本文將就IPv6環境下的網絡編程方式進行實例解析。

最終效果:

實例解析IPv6環境下的網絡編程—配置篇

目前我們所用的IP協議是v4版本的, 比如192.168.0.1。它是在1981年由RFC791標准化的。而ipv6是IP協議的新版本,標准化工作始於1991年,主要部分在1996年完成。它的地址長度為128位。比如3ffe:b00:c18:1::10。

要進行IPv6編程,我們首先要自己搭建一個IPv6的實驗環境。在這裡我將簡單介紹windows平台與linux平台的配置方法。由於只是講解一下實驗環境的搭建,所以其他的細節將不做詳細說明。

在window2K下安裝Microsoft IPv6 Technology Preview for Windows 2000

1.下載tpipv6-001205-SP3-IE6。可以從http://hs247.com下載,也可從微軟官方下載。

2.在tpipv6-001205-SP3-IE6\setup目錄下雙擊hotfix.exe。

3.重啟後在控制面板->網絡和撥號連接->本地連接->右鍵打開屬性對話框->安裝->協議->添加ipv6協議。

效果如圖1。

圖1

在windows XP下安裝ipv6

XP本身已經自帶ipv6的功能了,所以不需要另外安裝。只不過默認是不啟用的。如果要啟用Xp下的ipv6只要在命令行方式下輸入ipv6 install就行了。同win2k一樣啟用/關閉IPv6功能是net start tcpip6和net stop tcpip6。

地址配置的舉例

在裝完IPv6協議棧後我們給機器配個地址,然後ping6一下試試。

添加地址的命令是:ipv6 adu ifindex/address

刪除地址的命令是:ipv6 adu ifindex/address lifetime 0(即將生命期設為0便可)

ping在ipv6環境下變成了ping6 address

實例解析IPv6環境下的網絡編程—實例篇

IPv6程序相對於IPv4改動並不大。其中主要的改動就是地址結構與地址解析函數。在RFC中詳細說明了socket api 為適應IPv6所做的改動。而且windows平台與Linux平台在實現上也幾乎是一樣的。只不過頭文件與支持程度等有所不同罷了(具體請參見RFC 2553與RFC 2292)。如讀者有興趣的話可以找RFC來看看,在這裡就不再詳細說明,只講最簡單的原理與例子,同時例出各主要socket api,如何使用,可以參考MSDN或是Linux中的MAN。

地址表示

IPv4使用32bits的地址表示,並有sockaddr_in和in_addr等結構應用於API中,而IPv6 使用128 bits 地址,也定義了本身的地址結構sockaddr_in6和in6_addr。

struct sockaddr_in {
  u_char  sin_len;
  u_char  sin_family;
  u_short  sin_port;
  struct  in_addr sin_addr;
  char    sin_zero[8];
};
struct in_addr {
    u_int32_t s_addr;
}
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;
}
struct in6_addr {
u_int8_t  __u6_addr8[16];
}

IP V4/IP V6專用函數

IPv6 API中一部分沿用了IPv4 API, 也新增了一些IPv6專用API,為使得程序具有更大的通用性,盡量避免使用IPv4專用函數,這些函數如下:

IP V4專用 是對應的IP v4/v6通用函數 功能說明 inet_aton( ) inet_ntop( ) 字符串地址轉為IP地址 inet_ntoa( ) inet_pton( ) IP地址轉為字符串地址 gethostbyname( ) Getipnodebyname( ) 由名字獲得IP地址 gethostbyaddr( ) struct hostent *getipnodebyaddr( ) IP地址獲得名字   getaddrinfo( ) 獲得全部地址信息   getnameinfo( ) 獲得全部名字信息 未發生變化的函數 功能說明 socket( ) 建立Socket bind( ) Socket與地址綁定 send( ) 發送數據(TCP) sendto( ) 發送數據(UDP) receive( ) 接收數據(TCP) recv( ) 接收數據(UDP) accept( ) 接收連接 listen( ) 網絡監聽

如上表格所示,IP V4專用函數在IP V6環境下已經不能使用,他們一般有一個對應的IP V4/V6通用函數,但是在使用通用函數的時候需要一個協議類型參數(AF_INET/AF_INET6)。另外還增加了兩個功能強大的函數getaddrinfo( )和getnameinfo( ),幾乎可以完成所有的地址和名字轉化的功能。

Windows平台IPv6程序一例

下面我將給出windows平台下的一個例子。

系統平台:windows 2000 professional SP3+tpipv6-001205-SP3-IE6+VC6

說明:在本程序中只采用一個SOCKET,用WSAAsyncSelect來完成收與發的處理,為了方便起見,采用UDP方式。同時只專注IPv6的寫法,不考慮函IPv4/IPv6兼容。如何編寫IP無關的程序,有機會再另行撰文。

注:由於windows2000只是提供了IPv6的技術預覽版,所以在有些地方支持上並不好。比如win2k在recvfrom的實現上好像有點問題,而linux中同樣的代碼卻沒問題。兩者在recv的實現上都很正常。所以如果要開發程序最好是在window xp或是linux上進行。

下面是程序中關鍵的幾步:

1.頭文件的引入

#include <winsock2.h>
#include <ws2tcpip.h>
#include "tpipv6.h" // Form IPv6 Tech Preview.(可以從tpipv6-001205-SP3-IE6安裝後所在目錄裡找到)
#pragma comment(lib,"ws2_32")//引入庫函數。

2.建SOCKET

UpdateData(TRUE);
memset(&hints,0,sizeof(hints));
//hints.ai_family=PF_UNSPEC;//這樣可以由系統自己進行判斷,這裡我們直接指定是IPV6
hints.ai_family=AF_INET6;//指定用IPV6協議
hints.ai_socktype=SOCK_DGRAM;
hints.ai_protocol=IPPROTO_UDP;//用UDP
hints.ai_flags=AI_NUMERICHOST;//IP用數字表示
rc=getaddrinfo((LPCTSTR )m_send,"2000",&hints,&res);//解析地址
if(rc!=0)
{
  AfxMessageBox("fail");
  return;
}
s_send=socket(res->ai_family,res->ai_socktype,res->ai_protocol);//建socket
if(s_send==INVALID_SOCKET)
{
  AfxMessageBox("建本機發socket失敗");return ;}
else
{
  AfxMessageBox("建本機發socket成功");
  //綁定本地監聽端口bind
  if(bind (s_send,res->ai_addr,res->ai_addrlen)==SOCKET_ERROR)
    AfxMessageBox("bind失敗");
  else
    {AfxMessageBox("bind成功");GetDlgItem(IDOK)->EnableWindow (TRUE);  }
}
if ( WSAAsyncSelect( s_send, m_hWnd, UM_PACKET, FD_READ ) == SOCKET_ERROR )//異步選擇
{
  MessageBox( "WSAAsyncSelect failed" );
  closesocket( s_send );
  return;
}

3.發送

memset(&hints,0,sizeof(hints));
hints.ai_family=AF_INET6;
hints.ai_socktype=SOCK_DGRAM;
hints.ai_protocol=IPPROTO_UDP;//這裡也可以改為0,讓系統自己選
hints.ai_flags=AI_NUMERICHOST;
rc=getaddrinfo((LPCTSTR )m_receive,"2000",&hints,&res);//解析對方收地址
if(rc!=0)
{
  AfxMessageBox("fail");
  return;
}
rc=sendto(s_send,m_sendtxt,m_sendtxt.GetLength(),0,res->ai_addr,res->ai_addrlen);
if (rc== SOCKET_ERROR)
{
  AfxMessageBox("發送失敗");
  closesocket(s_send);
}
else
{
  AfxMessageBox("發送成功");
}

4.異步觸發事件

LRESULT CMy6dDlg::OnPacket( WPARAM wParam, LPARAM lParam )
{
  char sBuf[128];
  sockaddr_in6 sa;
  int iAddrLen=sizeof( sa );
  int a=sizeof(sockaddr);
  ZeroMemory( &sa, sizeof( sa ) );
  memset(&sBuf,0,sizeof(sBuf));
  int iLen = recv(s_send, sBuf, sizeof(sBuf),0);//for win2k use recv
  if(iLen<0)
  {
    fprintf(stderr,"recvfrom failed with error %d: %s\n",)WSAGetLastError(), DecodeError(WSAGetLastError()));
    return 0;
  }
  else
  {
  sBuf[iLen] = 0;
  CString sText;
  char lpszAddressString[1000];
  DWORD dwAddressStringLength=1000;
  rc=WSAAddressToString((sockaddr*)&sa,sizeof(sa),NULL,lpszAddressString,&dwAddressStringLength );
  sText.Format( "recved [%s]", sBuf);//,sa.sin6_family==AF_INET6,lpszAddressString);//for win2k
    m_reclist.AddString( sText );
  return 0;
  }
}

(全文完)

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved