C++中的IPv6网络措施设计
副标题#e#
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;//地点所有的接口索引
}
#p#副标题#e#
三.独立于协议的地点及名称理会
由此可见在寻址时,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平台中。
<一>客户机措施设计
#p#分页标题#e#
对付客户机来说,不管是成立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.应用示例
#p#分页标题#e#
#include "stdafx.h"
#include "IPv6.h"
int main(int argc, char* argv[])
{
CIPv6 m_ipv6;
m_ipv6.CreateServer(); //回收默认建设处事器,
//假如你乐成安装了IPv6可以利用正常利用
return 0;
}