C++中的IPv6网络程序设计

IPv4 最初是由美国国防部开发的用于网际互联(IP)协议,后来它不仅发展了TCP,而且还进一步发展了IPv4(IP 协议4.0版)。IPv4现在已经广泛应用于Internet网络中,同时也应用于大多数计算机系统,局域网和广域网中。然而,随着Internet 中的计算机数量突飞猛涨,IPv4 的局限性越发明显:
1.IPv4地址数目面临耗尽,日近紧张;
2.IPv4寻址并非完全分等级,这使得Internet 枢纽路由器必须维持大量的路由表,负担过重。
3.IPv4的地址必须被静态分配或通过配置协议(如:DHCP)进行分配。IPv6的开发目标之一就是将提供更为简便的配置方案。
于是IPv6(6.0版本)应运而生。在Window系统中,Windows XP 提供了IPv6的developer-release版本;Windows 2000也可在http://www.microsoft.com/ipv6 下载 IPv6协议预览。下图在本人计算机上成功安装的示例图:
图-1 IPV6 安装示例
 
 
一.IPv4地址及其寻址 
1.IPv4地址
IPv4地址(常称IP地址)用一个32位数表示;通常表示位十进制格式,地址的每8位字节被表示转为一个十进制的数值,并由句点分隔,如:192.168.0.1;IPv4地址 通常分为A、B、C、D、E 五类。
    2.IPv4寻址
在Winsock 中,通过SOCKADDR_IN 结构来指定IPv4的地址和服务断口信息:
struct sockaddr_in {
short sin_family ;//必须为AF_INET,表示使用IPv4地址簇
           u_short sin_port; //TCP/UDP 端口
struct in_addr sin_addr;// IP地址(以网络字节顺序排列, 4个字节)
char sin_zero[8];//填充项
}  
 
二.IPv6地址及其寻址
  1.IPv6地址   
    IPv6地址与IPv4地址的显著的不同是128位,长度是IPv4地址的4倍。IPv6地址由16位字节分段表示,显示为冒号分隔的十六进制:
       21DA:00D3:0000:2F3A:B234:ED12:9C5A:DAC3
                                                 IPv6地址的分配
分配
 地址前缀
 
保留地址0000 0000
为NSAP预留0000 0001
可聚合的全球单播地址001
链接-本地单播地址1111 1110 10
站点-本地单播地址1111 1110 11
多播地址1111 1111

 
    2. IPv6的寻址
 Winsock中,寻址使用一下结构:
  struct  sockaddr_in6{
short sin6_family;// 地址簇:AF_INET6
u_short sin6_port;//端口号
u_long sin6_flowinfo;//连接标记通信量
struct in6_addr sin6_addr;//16字节结构的IPv6 地址
u_long sin6_scope_id;//地址所有的接口索引
 

 
三.独立于协议的地址及名称解析
由此可见在寻址时,IPv4使用16字节的SOCK_ADDR_IN 结构,IPv6则使用28 字节的SOCK_ADDR_IN6 结构。为了解决这个问题,IPv6中引入了新的寻址函数。 [Page]
1.getaddrinfo(),它提供独立于协议的名称解析:
int getaddrinfo(  
         const char *FAR *nodename,
         const  char FAR* servname,
         const struct addrinfo FAR *hins,
         struct addrinfo FAR *FAR *res
); 
   
l       第一参数:nodename,以空字节结束的主机名或文字地址
l       第二参数:servname,包含端口或服务名(如:FTP,TELNET)的以空字节结束的字符串
l       第三个参数:hins 是一个结构(addrinfo),包含名称解析的执行方式选项
l       第四个参数:res ,用于返回 addrinfo 结构的一个或多个链表
结构addrinfo 的定义:
struct    addrinfo{
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
size_t ai_addrlen;
char *ai_cannoname;
struct sockaddr *ai_addr;
struct addrinfo *ai_next;
}
l       ai_flags 选值:AI_PASSIVE:可以用来获取能够传递给bind函数的地址,此时nodename应设置为NULL,servname为欲绑定的端口;AI _CANONNAME 表示nodename 是主机名;AI_NUMBERICHOST 表示, nodename 是一个文字字符串地址(如:“192.168.0.1”)    
l       ai_family 选值:AI_INET或PF_INET(IPv4地址簇);AI_INET6或PF_INET6(IPv6地址簇);AI_UNSPEC(未指定,可能是IPv4或IPv6 地址簇)
l       ai_socktype选值:SOCK_DGRAM(UDP类型套接字);SOCK_STREAM (TCP类  型套接字)
l       ai_protocol 选值:IPPROTO_TCP (TCP/IP协议)
如果函数解析成功,解析后的地址将通过res返回。如果名称被解析为多个地址,则返回一个由ai_next 字段形成的链表。每个由名称解析的地址在ai_addr中表示,长度在ai_addrlen中表示。
2.getnameinfo()函数与getaddrinfo()相对应,功能相反。
.      int getnameinfo(
                const struct sockaddr FAR *sa,
                socklen_t salen, [Page]
                char FAR *host,
              DWORD hostlen,
              char FAR *serv,
              DWORD servlen,
              Int flags); 
   以上参数的含义比较明显,不再一一说明。
  3.释放函数: freeaddrinfo(res);
四、兼容IPv4和IPv6的网络程序设计
兼容IPv4和IPv6的网络程序,显然涉及到两个部分:客户机和服务器。
在Windows 网络编程中,Winsock是一种标准的API(应用程序接口),Winsock2版本已经发展成独立于协议的的接口,被广泛应用于Windows平台中。
<一>客户机程序设计
对于客户机来说,不管是建立TCP/UDP 连接,它都应知道服务器的主机名或IP 地址,同时将服务器地址解析为IPv4或IPv6地址都可以,一般可以考虑一下步骤:
SOCKET s;
struct addrinfo,hints,*res=NULL;
char *szRemoteAddress;//主机名或IP 地址
char *szRemotePort;//端口号
int rc;
1.用getaddrinfo() 函数解析地址。hins结构中 使用AF_UNSPEC标志,便可以获得地址簇类型(IPv4或IPv6)。
memset(&hintas,0,sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
rc=getaddrinfo(szRemoteAdddress,szRemotePort,&hints,&res);
if(rc==WSANO_DATA)
{// 无法解析,出错
}
用返回的addrinfo结构中的ai_family,ai_socketype,ai_protocol字段来创建套接字。
s=socket(res->ai_family,ai_socktype,res->protocol);
if(s==INVALID_SOCKET)
{//创建套接字失败
}
2.使用返回的addrinfo结构中的ai_addr来调用其他函数(connect(),send()等).。
      rc==connect(s,res->ai_addr,res->addrlen);
if(rc==SOCKET_ERROR)
{//连接失败;
}
。。。//完成其他编程
<二>服务器程序设计
服务器程序设计,应考虑到IPv4和IPv6 都具有各自的堆栈;因此如果服务器希望能同时接受IPv4和IPv6的连接,就必须能同时创建IPv4和IPv6套接字;一般可以考虑一下步骤:
        SOCKET socklisten[2];//监听Socket变量
char *szPort=”8080”;//监听端口
struct addinfo hints,*res=NULL,*ptr=NULL;
int rc,i=0;
1.  调用getaddrinfo()函数,该结构包含AI_PASSIVE,AF_UNSPEC标志,以及所需的套接字类型、协议及所需的本地端口(用来监听和接受数据等)。函数将返回的两个addrinfo结构,分别可用于IPv4和IPv6监听地址: [Page]
memset(&hints,0,sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
hints.ai_flags=AI_PASSIVE;
rc=getaddinfo(NULL,szPort,&hints,&res);
if(rc!=0){//失败处理;}
ptr=res;
     2. 用返回的addrinfo结构中的ai_family,ai_socketype,ai_protocol字段来创建套接字后;便可以使用addrinfo结构中的ai_addr 和ar_addrlen 字段调用绑定函数bind()。
while(ptr)
{
socklisten[i]=socket(ptr->ai_family,ptr->ai_socktype,ptr->ai_protocol);
if(socklisten[i]==INVALID_SOCKET){//创建失败处理;}
rc=bind(socklisten[i],ptr->ai_addr,ptr->ai_addrlen);
if(rc==SOCKET_ERROR){//绑定失败处理}
rc=listen(slisten[i],7)//开始监听
if(rc==SOCKET_ERROR){//监听失败处理}
i++;
ptr=ptr->ai_next;
}
     。。。
//完成其他编程
五、程序实例 
   在这里,给出一个基于IPV6的简单回应(ECHO)服务器程序. 
    1.建立CIPv6 类 
// IPv6.h: 头文件,这里使用到了套接字中的“select I/O模型”
#define WIN32_LEAN_AND_MEAN   
#include <winsock2.h>   
#include <ws2tcpip.h>   
#include <tpipv6.h>    //    IPv6 头文件   
#include <stdlib.h>   
#include <stdio.h>   
#include <string.h>   
#pragma comment(lib, "ws2_32.lib")//套接字库文件   
#define DEFAULT_PORT "7274" // 默认端口
#define BUFFER_SIZE   64   // 数据缓冲区
class CIPv6       
{   
public:   
 // 创建TCP 服务器   
 int CreateServer(char *Port = DEFAULT_PORT,char *Address = NULL);   
 void Usage(char *ProgName);//用户信息提示   
 LPSTR DecodeError(int ErrorCode);//获取错误信息   
 CIPv6();   
 virtual ~CIPv6();   
};
// IPv61.cpp: CIPv6类的实现 .
// IPv61.cpp: implementation of the CIPv6 class.
//
//
#include "stdafx.h"
#include "IPv61.h"
int CIPv6::CreateServer(char *Port, char *Address)   
{    
 char Buffer[BUFFER_SIZE], Hostname[NI_MAXHOST];
 int RetVal, FromLen, AmountRead;   
 SOCKADDR_STORAGE From;   
 WSADATA wsaData;   
 ADDRINFO Hints, *AddrInfo;   
 SOCKET ServSock;   
 fd_set SockSet;   
 // 启动Winsock   
 if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
 {   
  fprintf(stderr, "WSAStartup failed with error %d: %s\n",   
   RetVal, DecodeError(RetVal));   
  WSACleanup();   
  return -1;   
 }   
 if (Port == NULL)
 {   
  Usage("Port Error");   
 }   
 memset(&Hints, 0, sizeof(Hints));   
 Hints.ai_family =AF_INET6;// Family;   
 Hints.ai_socktype =SOCK_STREAM;   
 Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;   
 RetVal = getaddrinfo(Address, Port, &Hints, &AddrInfo);   
 if (RetVal != 0)
 {   
  fprintf(stderr, "getaddrinfo failed with error %d: %s\n", RetVal, gai_strerror(RetVal));   
  WSACleanup();
  return -1;
 }
 // 创建套接字
 ServSock = socket(AddrInfo->ai_family,AddrInfo->ai_socktype, AddrInfo->ai_protocol);
 if (ServSock == INVALID_SOCKET)
 {
  fprintf(stderr, "socket() failed with error %d: %s\n",
   WSAGetLastError(), DecodeError(WSAGetLastError()));
   WSACleanup();
   return -1;
 }
 // 绑定套接字    
 if (bind(ServSock, AddrInfo->ai_addr, AddrInfo->ai_addrlen) == SOCKET_ERROR)    
 {   
  fprintf(stderr,"bind() failed with error %d: %s\n",   
   WSAGetLastError(), DecodeError(WSAGetLastError()));   
  WSACleanup();   
  return -1;        
 }   
 // 侦听   
 if (listen(ServSock, 5) == SOCKET_ERROR)    
 {   
  fprintf(stderr, "listen() failed with error %d: %s\n",   
   WSAGetLastError(), DecodeError(WSAGetLastError()));   
  WSACleanup();   
  return -1;    
 }   
 printf("'Listening' on port %s, protocol %s, protocol family %s\n",   
  Port, \"TCP\",   
  "PF_INET6");   
freeaddrinfo(AddrInfo);   
 //使用select I/O 模型进行收发
 FD_ZERO(&SockSet);   
 while(1)
 {   
  FromLen = sizeof(From);   
  if (FD_ISSET(ServSock, &SockSet)) break;   
  FD_SET(ServSock, &SockSet);
  if (select(0, &SockSet, 0, 0, 0) == SOCKET_ERROR)
  {
   fprintf(stderr, "select() failed with error %d: %s\n",
    WSAGetLastError(), DecodeError(WSAGetLastError()));
   WSACleanup();
   return -1;
  }
 }
 if (FD_ISSET(ServSock, &SockSet))
 {
  FD_CLR(ServSock, &SockSet);
 }
 //接受一个连接
 SOCKET ConnSock;
 ConnSock = accept(ServSock, (LPSOCKADDR)&From, &FromLen);
 if (ConnSock == INVALID_SOCKET)
 {
  fprintf(stderr, "accept() failed with error %d: %s\n",
   WSAGetLastError(), DecodeError(WSAGetLastError()));
   WSACleanup();
   return -1;
 }
 if (getnameinfo((LPSOCKADDR)&From, FromLen, Hostname,
  sizeof(Hostname), NULL, 0, NI_NUMERICHOST) != 0)
  strcpy(Hostname, "<unknown>");
  printf("\nAccepted connection from %s\n", Hostname);
  while(1)
  {
   //等待接受数据
   AmountRead = recv(ConnSock, Buffer, sizeof(Buffer), 0);
   if (AmountRead == SOCKET_ERROR)
   {
    fprintf(stderr, "recv() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError()));
     closesocket(ConnSock);
    break;
   }
   if (AmountRead == 0) {
    printf("Client closed connection\n");
     closesocket(ConnSock);
    break;
   }
printf("Received %d bytes from client: [%.*s]\n",
    AmountRead, AmountRead, Buffer);
   //进行简单ECHO 回应
   printf("Echoing same data back to client\n");
    RetVal = send(ConnSock, Buffer, AmountRead, 0);
   if (RetVal == SOCKET_ERROR)
   {
    fprintf(stderr, "send() failed: error %d: %s\n",
     WSAGetLastError(), DecodeError(WSAGetLastError()));
    closesocket(ConnSock);
    break;
   }
  }
  return 0;
}
void CIPv6::Usage(char *ProgName)
{
 fprintf(stderr, "\nSimple socket sample server program.\n");
  fprintf(stderr, "transport tEither TCP or UDP. (default: %s)\n",
  "TCP");
  fprintf(stderr, "port\t\tPort on which to bind. (default %s)\n",
  DEFAULT_PORT);
 fprintf(stderr, "address\tIP address on which to bind.(default: unspecified address)\n");
  WSACleanup();
 exit(1);
}
LPSTR CIPv6::DecodeError(int ErrorCode)
{
 static char Message[1024];
 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
  FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, ErrorCode,
  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  (LPSTR)Message, 1024, NULL);
 return Message;
}
2.应用示例
#include "stdafx.h"
#include "IPv6.h"    
int main(int argc, char* argv[])    
{
        CIPv6 m_ipv6;    
        m_ipv6.CreateServer(); //采用默认创建服务器,    
        //如果你成功安装了IPv6可以使用正常使用    
        return 0;    
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/433416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python regex_Python 正则表达式

定义在编写处理字符串的程序或网页时&#xff0c;经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。简而言之&#xff1a;正则表达式就是记录文本规则的代码。特点:操作字符串1.更快的方式操作字符串&#xff1b;(表单校验&#xff0c;数据…

python 怎么处理json_Python是怎样处理json模块的

首先&#xff0c;了解下什么是JSON&#xff1f;JSON&#xff1a;JavaScript Object Notation 【JavaScript 对象表示法】JSON 是一种轻量级的数据交换格式&#xff0c;完全独立于任何程序语言的文本格式。一般&#xff0c;后台应用程序将响应数据封装成JSON格式返回。JSON的基本…

UEditor1.2.6.0在.net环境下使用

UEditor1.2.6.0 1.百度百科词条 2.UEditor官方网站 【CKEditorCKFinder的配置实用&#xff0c;可查看博主另一篇文章】 第一次接触UEditor还是在2011年的下半年里&#xff0c;当时由于需要找一款富文本编辑器进行新闻的网站开发&#xff0c;当时UEditor异常较多&#xff0c;就将…

sql 删除数据_从零开始学SQL:是什么、如何安装、基本语法、表格(创建、删除、更新)、数据(插入、删除、更新)...

一、学习知识的黄金圈思维用黄金圈思维分析自己学习SQL&#xff0c;能增加自己的效率和坚持下去的动力。二、SQL基础知识1.数据库概念&#xff1a; database &#xff0c;按照数据结构来组织、存储和管理数据的仓库。2.关系数据库&#xff1a;多张表表之间的关系表&#xff1a;…

ubuntu ip设置

在Debian(ubuntu就是Debian类linux)中网卡的设置可以通过/etc/network/interfaces文件来进行&#xff0c;具体可分为三种不同的配置方式&#xff1a;DHCP自动获取、静态分配IP地址和PPPoE宽带拨号。 具体设置如下&#xff1a; 在进行配置之前&#xff0c;首先进入/etc/networ…

在mysql中你可以为视图创建索引_mysql中创建视图、索引

数据库的三级模式两级映射: 存储文件------>基本表----->视图 内模式 ------->模式 ------>外模式 一、视图 1、什么是视图: 视图是从一个或多个表中导出来的表,是一种虚拟存在的表。 视图就像一个窗口,通过这个窗口可以看到系统专门提供的数据。 这样,用…

mysql 转换编码

ALTER TABLE tablename CONVERT TO CHARACTER SET UTF8;转载于:https://www.cnblogs.com/wuruile/p/3164920.html

嵌入式Linux操作系统的版本查询

嵌入式Linux版本查询&#xff1a; uname -a 显示内核版本 uname -m 显示ARM处理器内核信息 uname -v 显示操作系统的编译时间 linux版本&#xff1a;在linux/utsrelease.h中定义&#xff1a; [cpp] view plaincopy#define UTS_RELEASE "2.6.31-liuqimingxinguodu.com--…

函数使用了堆栈的字节超过_在C语言中如何访问堆栈?

堆栈一般是用来保存变量之类的东西&#xff08;静态变量在内存中&#xff0c;虽然堆栈就是内存的一部分&#xff0c;但为了防止歧义&#xff0c;还是分成两部分来说&#xff09;&#xff0c;一般情况下没必要去故意读取堆栈的值&#xff0c;变量用变量名就可以直接访问&#xf…

怎么解决相位抵消_【录音教程】如何防止话筒拾音的声学相位抵消

设想一下你以单声道的模式为一个吉他歌手做扩声&#xff0c;一支人 声听起来比较古怪&#xff0c;有点“空”&#xff0c;同时有滤波器之类的效果声。为什么会这样&#xff1f;主唱话筒拾取歌手的演唱&#xff0c;同时拾取箱琴的话筒也串入了歌手的声音(见图1)。吉 他话筒离主唱…

win32 ipv6 bind 10014问题

这两天实验了一下&#xff0c;windows xp系统下的 ipv6 udp网络通信。要在windows xp下测试ipv6&#xff0c;首先在命令行窗口下 执行 ipv6 install 和 net start tcpipv6 命令。 在bind本地地址时&#xff0c;发现没法使用 in6addr_any 这通配地址。即下面这种使用方式&a…

string 找出所有数字 index_发现规律,解决整数转罗马数字

嗨&#xff0c;各位&#xff01;我们又准时见面了&#xff0c;即将迎来难得的周末时光&#xff0c;我们今天来一道相对简单的题目逻辑梳理的题目&#xff0c;原定的动态规划的常见题型我们放在周末进行更新。话不多说&#xff0c;我们先看题目&#xff1a; 01 . 题目罗马数字包…

ASP.NET MVC的路由

好久没写博文了&#xff0c;感觉最近好像少了点动力。唉&#xff01;这回就看看这个MVC的路由。 说这个路由机制其实不是MVC里面特有的&#xff0c;ASP.NET里面本身就有的&#xff0c;只不过在WebForm里面一般比较少用&#xff0c;而在MVC里就是把原本的路由扩展了。原本对不知…

win32 ipv6 sendto recvfrom

1. sendto 在windows xp下进行ipv6的udp网络通信时&#xff0c;目标地址应该像如下方式构造 addrinfo hins, *res; memset(&hins, 0, sizeof(hins)); hins.ai_family AF_INET6; hins.ai_socktype SOCK_DGRAM; hins.ai_protocal IPPROTO_UDP; hins.ai_flags AI_NUERICH…

vscode python环境配置_用VScode配置Python开发环境

前言VScode是一个相当优秀的IDE&#xff0c;具备开源、跨平台、模块化、插件丰富、启动时间快、颜值高、可高度定制等等优秀的特质&#xff0c;不愧是微软爸爸的私生子。所以用VScode来编写Python&#xff0c;也是相当的好用的。所以&#xff0c;今天我们就来讲讲&#xff0c;怎…

java情书_Java情书已写好,就差妹子了!

我是Java程序员我用我的方式表达对你的爱&#xff01;我能抽象出整个世界...但是我却不能抽象出你...你肯定是一个单例&#xff0c;因为你是那样的独一无二...所以我的世界并不完整...我可以重载甚至覆盖这个世界里的任何一种方法...但是却不能覆盖对你的思念...也许命中注定了…

JS中的正则表达式小小的技巧---TEST方法的使用

TEST 方法 返回一个 Boolean 值&#xff0c;它指出在被查找的字符串中是否存在模式。 rgexp.test(str) 参数 rgexp 必选项。包含正则表达式模式或可用标志的正则表达式对象。 str 必选项。要在其上测试查找的字符串。 说明 test 方法检查在字符串中是否存在一个模式&#xff0c…

windows下手动配置ipv6地址

在XP,2003等早期版本中&#xff0c;ipv6地址在“网络连接”的属性配置里是无法手工配置的&#xff0c;只能使用netsh配置。配置方法如下&#xff1a;首先&#xff0c;安装IPV6协议&#xff0c;ipv6 install 第二步&#xff0c;查看当前使用的本地连接信息&#xff1a; 如上图&a…

python10的因数_十五道Python小案例,学会这些,Python基础已过关!

十五道Python的小案例&#xff0c;不知道各位都会了多少&#xff01;一、猜数字1到4能组成多少个互不相同且不重复数字的三位数&#xff1f;分别是&#xff1f;分析&#xff1a;可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去 掉不满足条件的排列。程序源代…

java 幽灵引用_Java 幽灵引用的作用

原标题&#xff1a;Java 幽灵引用的作用来源&#xff1a;imzoer&#xff0c;blog.csdn.net/imzoer/article/details/8044900找实习的时候&#xff0c;面试大摩&#xff0c;就遇到了这个问题&#xff0c;当时真不该跟面试官交流这个内容的。垃圾收集过程中&#xff0c;对象的可触…