文章目录

  • 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:417A2001:DB8:0:23:8:800:200C:417A

3.2 0位压缩表示法

在某些情况下,一个IPv6地址中问可能包含很长的一段0,可以把连续的一段0压缩为“::"。但为保证地址解析的唯一性,地址中”:"只能出现一次,例如:
FF01:0:0:0:0:0:0:1101FF01: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
    使用gethostbynamegethostbyaddr
  • ipv6
    getaddrinfogetnameinfo

例:

getaddrinfo(ipaddr, port, &hints, &res);

5. TCP_Server_IPv6.cpp

使用cmdipconfig查看自己主机的ipv6地址:

IPV6 TCP wireshark网络_ipv4移植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. 启动服务端和客户端

  • 先启动服务端
  • 再启动客户端
  • 运行结果如下,通讯成功

IPV6 TCP wireshark网络_网络编程_02