C++中的IPv6网络程序设计
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中表示。
. 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 类
#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 // 数据缓冲区
{
public:
// 创建TCP 服务器
int CreateServer(char *Port = DEFAULT_PORT,char *Address = NULL);
void Usage(char *ProgName);//用户信息提示
LPSTR DecodeError(int ErrorCode);//获取错误信息
CIPv6();
virtual ~CIPv6();
// IPv61.cpp: implementation of the CIPv6 class.
//
//
#include "IPv61.h"
{
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");
//使用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;
}
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;
}
{
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);
}
{
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 "IPv6.h"
int main(int argc, char* argv[])
{
CIPv6 m_ipv6;
m_ipv6.CreateServer(); //采用默认创建服务器,
//如果你成功安装了IPv6可以使用正常使用
return 0;
}
C++中的IPv6网络程序设计相关推荐
- java面试题32:Java网络程序设计中,下列正确的描述是()
java面试题32:Java网络程序设计中,下列正确的描述是() A:Java网络编程API建立在Socket基础之上 B:Java网络接口只支持tcP以及其上层协议 C:Java网络接口只支持UDP ...
- 《部署IPv6网络(修订版)》一2.4 邻居发现协议
本节书摘来自异步社区<部署IPv6网络(修订版)>一书中的第2章,第2.4节,作者[美]Ciprian Popoviciu , Eric Levy-Abegnoli , Patrick G ...
- 全球IPv6网络6月6日正式启动
全球范围内的世界互联网协议版本6(IPv6)昨日正式启动.据中广网报道,在非营利组织国际互联网协会(Internet Society)发起下,包括互联网服务供应商AT&T.康卡斯特(Comca ...
- 怎样解决IPv4网络和IPv6网络的共存互通?
现在,业界一致认为IPv6技术是解决IP地址短缺问题的唯一可行的根本办法.但由于IPv6与IPv4技术不兼容,且现有的IPv4设备和用户数量庞大,在网络演进过程中,需要解决异构网络的共存互通问题. 解 ...
- 《Cisco IPv6网络实现技术(修订版)》一2.6 配置练习:使用Cisco路由器配置一个IPv6网络...
本节书摘来自异步社区<Cisco IPv6网络实现技术(修订版)>一书中的第2章,第2.6节,作者[加]Régis Desmeules,更多章节内容可以访问云栖社区"异步社区&q ...
- IPv6网络协议的安全疑云
随着互联网的更新,我们迎来了IPv6网络协议的使用,在我们不断赞扬IPv6网络协议的诸多优点后,我们也同样对它的一些漏洞和问题产生了担心.计算机网络协议版本6(IPv6) 并不提高企业的Web安全性, ...
- [渝粤教育] 西南科技大学 网络程序设计 在线考试复习资料
网络程序设计--在线考试复习资料 一.单选题 1.Sng函数的作用是( ). A.将字符串转换为日期 B.将字符串转换为长整型 C.将字符串转换为布尔型 D.将数值转换为单精度型 2.数据库常用的函数 ...
- 服务器放行6in4协议,最简单的接入IPv6网络的方法 – 6in4隧道
很多大学的校园网启用了IPv6,并且建设了很多IPv6专享的资源.毕业离校,由于现在的网络运营商绝大部分尚未支持IPv6,所以那些资源也就离我们远去了.机缘巧合之下,我知道了,原来我们可以这样接入IP ...
- 通过IPv4网络访问IPv6网络 ISATAP隧道配置方法
实验14: IPv4和IPv6地址转化 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office ...
最新文章
- android ui篇
- 判断手机浏览器还是微信浏览器(PHP)
- Python模块整理(四):线程模块threading
- 将Object structure IBASE从UI上隐藏
- c++ explicit关键字_聊一聊 C++的特性 explicit 匿名空间
- mysql bigint转string_无语了,直到今天,我才揪出MySQL磁盘消耗迅猛的“真凶”!...
- seqlist插入java_大话数据结构(五)(java程序)——顺序存储结构的插入与删除...
- 化学实验中计算机技术的应用,数字化实验在化学教学中的应用
- 中小型企业网络配置、基于企业网络方案的设计与实施
- 软件生成问候图片_设计师注意啦!欧美风双色图片一键生成的软件来啦!
- centos服务器搭建代理
- Excel文件转换为XML以及Linux文件编码格式转换
- 杭州电商java招聘_2021年IUU旅行 JAVA开发工程师J11179招聘-IUU旅行 JAVA开发工程师J11179招聘求职信息-拉勾招聘...
- prometheus grafana sql 常用函数参数
- 知识图谱问答 | (3) 关系分类概述
- 魔方(10)金字塔魔方、金字塔二重奏魔方
- “武大樱花绽放视频”动手实操教程--Python
- 小红书引流软件有哪些
- 马云的SNS梦美好却残酷
- Prism4学习笔记(七):State-Based Navigation QuickStart