文章目录
- 1. IPv6简介
- 2. IPv6的长度
- 3. IPv6的表示方法
- 3.1 冒号分十六进制表示法
- 3.2 0位压缩表示法
- 3.3 内嵌IPv4地址表示法
- 4. socket 下ipv4到ipv6的移植
- 4.1 地址结构
- 4.2 地址赋值
- 4.3 socket创建
- 4.4 网络序转为字符串ip地址
- 4.5 字符串ip地址转为网络序
- 4.6 主机名和地址的转换
- 5. TCP_Server_IPv6.cpp
- 6. TCP_Client_IPv6.cpp
- 7. 启动服务端和客户端
1. IPv6简介
IPv6(Internet Protocol Version 6)是IETF(互联网工程任务组,Internet Engineering Task Force)设计的用于替代现行版本IP协议(IPv4)的下一代IP协议,号称可以为全世界的每一粒沙子编上一个网址。IPv6不仅能解决IPv4地址资源紧缺的问题,而且也解决了多种接入设备连入互联网的障碍。
2. IPv6的长度
IPV4地址长度32位(2^32-1)
IPv6地址长度128位(2^128)
3. IPv6的表示方法
3.1 冒号分十六进制表示法
格式为X:X:X:X:X:X:X:X,其中每个X表示地址中的16位,以十六进制表示,例如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
这种表示法中,每个X的前导0是可以省略的,例如:2001:0DB8:0000:0023:0008:0800:200C:417A
→2001:DB8:0:23:8:800:200C:417A
3.2 0位压缩表示法
在某些情况下,一个IPv6地址中问可能包含很长的一段0,可以把连续的一段0压缩为“::"。但为保证地址解析的唯一性,地址中”:"只能出现一次,例如:FF01:0:0:0:0:0:0:1101
→FF01:1101
0:0:0:0:0:0:0:1
→::1
0:0:0:0:0:0:0:0
→::
3.3 内嵌IPv4地址表示法
为了实现IPv4-IPv6互通,IPv4地址会嵌入IPv6地址中,此时地址常表示为:X:X:X:X:X:X:d.d.d.d,前96b采用冒分十六进制表示,而最后32b地址则使用IPv4的点分十进制表示,例如::192.168.0.1与
:FFFF:192.168.0.1就是两个典型的例子。
4. socket 下ipv4到ipv6的移植
4.1 地址结构
- ipv4地址结构:(16字节)
struct sockaddr_in
{
short sin_family; // 2 AF_INET
unsigned short sin_port; // 2
struct in_addr sin_addr; // 4
char sin_zero[8]; // 8填充
}
- ipv6地址结构:(28字节)
#include <ws2tcpip.h> // sockaddr_in6头文件
struct sockaddr_in6
{
short sin6_family; // 2 AF_INET6
u_short sin6_port; // 2
u_long sin6_flowinfo; // 4
struct in6_addr sin6_addr; // 16
u_long sin6_scope_id; // 4
}
4.2 地址赋值
addr.sin_family = AF_INET; // ipv4
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(6000);
addr.sin6_family = AF_INET6; // ipv6
addr.sin6_addr = in6addr_any;
addr.sin6_port = htons(6000);
注意:
INADDR_ANY是主机字节序,而in6addr_any为网络字节序
4.3 socket创建
int socket(int domain, int type, int protocol);
差别在于ipv4时,第一个参数为AF_INET,而ipv6时,第一个参数为AF_INET6
socket(AF_INET, SOCK_STREAM, 0); // ipv4
socket(AF_INET6, SOCK_STREAM, 0); // ipv6
4.4 网络序转为字符串ip地址
// ipv4
char* inet_ntoa(struct in_addr in);
// ipv6
char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len);
// 例如
inet_ntop(AF_INET6, &addr.sin6_addr, strip, 100);
4.5 字符串ip地址转为网络序
// ipv4
in_addr_t inet_addr(const char* strptr);
// ipv6
int inet_pton(int af, const char* src, void *dst);
// 例如
inet_pton(AF_INET, "fe80:ce6:3cc:f93a:4203%5", &addr6.sin6_addr);
4.6 主机名和地址的转换
- ipv4
使用gethostbyname
和gethostbyaddr
- ipv6
getaddrinfo
和getnameinfo
例:
getaddrinfo(ipaddr, port, &hints, &res);
5. TCP_Server_IPv6.cpp
使用cmd
的ipconfig
查看自己主机的ipv6地址:
#include<winsock2.h>//winsock的头文件
#include<ws2tcpip.h>//sockaddr_in6的头文件
#include<iostream>
using namespace std;
//指定动态库的lib文件
#pragma comment(lib,"ws2_32.lib")
//TCP服务端IPv6版
int main()
{
//初始化winsock2.2相关的动态库
WSADATA wd;//获取socket相关信息
if (WSAStartup(MAKEWORD(2, 2), &wd) != 0)//0表示成功
{
cout << "WSAStartup error:" << WSAGetLastError() << endl;
return 0;
}
//1.创建TCP socket , 流式套接字 , AF_INET改为AF_INET6
SOCKET s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
cout << "socket error:" << WSAGetLastError() << endl;
return 0;
}
//2.绑定socket到一个IP地址和端口, sockaddr_in改为sockaddr_in6
sockaddr_in6 addr;//不建议使用sockaddr,建议用sockaddr_in
memset(&addr, 0, sizeof(sockaddr_in6)); //重点,否则就10049错误
addr.sin6_port = htons(8000);//网络字节序
addr.sin6_family = AF_INET6; //地址族AF_INET改为AF_INET6
//addr.sin6_addr = in6addr_any;// 把INADDR_ANY替换为in6addr_any,表示绑定任意ip
//addr.sin6_addr = inet_addr("127.0.0.1");//绑定指定地址, ipv4
inet_pton(AF_INET6, "fe80::8055:681d:2c08:d728%18", &addr.sin6_addr);//绑定指定地址, ipv6
int len = sizeof(sockaddr_in6);//地址结构大小改变 sizeof(sockaddr_in6)
if (bind(s, (sockaddr *)&addr, len) == SOCKET_ERROR)
{
cout << "bind error:" << WSAGetLastError() << endl;
return 0;
}
//3.监听, 5代表正在等待完成相应的TCP三路握手过程的队列长度
if (listen(s, 5) == SOCKET_ERROR)
{
cout << "listen error:" << WSAGetLastError() << endl;
return 0;
}
//4.接受客户端请求,并且返回和客户端通讯的套接字,sockaddr_in改为sockaddr_in6
sockaddr_in6 addrClient;// 保存客户端IP地址端口
memset(&addrClient, 0, sizeof(sockaddr_in6));
len = sizeof(sockaddr_in6);//地址结构大小改变 sizeof(sockaddr_in6)
SOCKET c = accept(s, (sockaddr*)&addrClient, &len);//成功返回套接字
if (c == INVALID_SOCKET)
{
cout << "accept error:" << WSAGetLastError() << endl;
return 0;
}
//5.发送,接受消息
int ret = 0;
do
{
//向客户端发送数据,不能用监听套接字s,而应该用accept返回的套接字c
ret = send(c, "I am Server!", strlen("I am Server!"), 0);//把flag置0
//接受客户端的消息
char buf[64] = { '\0' };
ret = recv(c, buf, 64, 0);//把flag置0
// inet_ntoa(addrClient.sin_addr) 改为 inet_ntop
char ipbuf[100] = { 0 };
inet_ntop(AF_INET6, (LPVOID)&addrClient.sin6_addr, ipbuf, 100);
cout << "recv " << ipbuf<< ": " << buf << endl;// inet_ntoa转换为IP字符串
} while (ret != SOCKET_ERROR && ret != 0);//对方关闭,返回0 ,错误返回SOCKET_ERROR
//6.关闭套接字
closesocket(s);
//清理winsock环境
WSACleanup();
return 0;
}
6. TCP_Client_IPv6.cpp
#include<winsock2.h>//winsock的头文件
#include<ws2tcpip.h>//sockaddr_in6的头文件
#include<iostream>
using namespace std;
//指定动态库的lib文件
#pragma comment(lib,"ws2_32.lib")
//TCP客户端
int main()
{
//初始化winsock2.2相关的动态库
WSADATA wd;//获取socket相关信息
if (WSAStartup(MAKEWORD(2, 2), &wd) != 0)//0表示成功
{
cout << "WSAStartup error:" << WSAGetLastError() << endl;
return 0;
}
//1.创建TCP socket , 流式套接字, AF_INET改为AF_INET6
SOCKET s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
cout << "socket error:" << WSAGetLastError() << endl;
return 0;
}
//2.链接服务端
sockaddr_in6 addr;//不建议使用sockaddr,建议用sockaddr_in
memset(&addr, 0, sizeof(sockaddr_in6)); //重点,否则就10049错误
addr.sin6_port = htons(8000);//网络字节序
//addr.sin6_addr = inet_addr("127.0.0.1");//绑定指定地址, ipv4
inet_pton(AF_INET6, "fe80::8055:681d:2c08:d728%18", &addr.sin6_addr);//绑定指定地址, ipv6
addr.sin6_family = AF_INET6; //地址族
int len = sizeof(sockaddr_in6);//结构大小改变sizeof(sockaddr_in6)
if (connect(s, (sockaddr*)&addr, len) == SOCKET_ERROR)
{
cout << "connect error:" << WSAGetLastError() << endl;
return 0;
}
//3.接受发送消息
int ret = 0;
do
{
//接受客户端的消息
char buf[64] = { '\0' };
ret = recv(s, buf, 64, 0);//把flag置0
// inet_ntoa(addrClient.sin_addr) 改为 inet_ntop
char ipbuf[100] = { 0 };
inet_ntop(AF_INET6, (LPVOID)&addr.sin6_addr, ipbuf, 100);
cout << "recv " << ipbuf << ": " << buf << endl;// inet_ntoa转换为IP字符串
//发送
ret = send(s, "I am Client!", strlen("I am Client!"), 0);
Sleep(1000);
} while (ret != SOCKET_ERROR && ret != 0);
//4.关闭套接字
closesocket(s);
//清理winsock环境
WSACleanup();
return 0;
}
7. 启动服务端和客户端
- 先启动服务端
- 再启动客户端
- 运行结果如下,通讯成功