通常我们在Linux下使用的都是IPv4的方式来进行网络编程通信,本次介绍Linux下IPv6方式通信方式。
IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址。由于IPv4最大的问题在于网络地址资源不足,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍。
IPv6地址由被划分为8个16位块的128位组成。 然后将每个块转换为由冒号符号分隔的4位十六进制数字。
例如,下面给出的是以二进制格式表示并被划分为八个16位块的128位IPv6地址:
0010000000000001 0000000000000000 0011001000111000 1101111111100001 0000000001100011 0000000000000000 000000000000000
每个块被转换为十六进制并由":"符号分隔,即使在转换为十六进制格式后,IPv6地址仍然很长。
2001:0000:3238:DFE1:0063:0000:0000:FEFB
地址的使用规则如下:
规则1:丢弃前导零:
在块5,0063中,可以省略前导的两个0,例如(第五块):
2001:0000:3238:DFE1:63:0000:0000:FEFB
规则2:如果两个或多个块包含连续零,则省略它们并用双冒号"::"替换:
例如(第6和第7块):
2001:0000:3238:DFE1:63::FEFB
连续的零块只能被::替换一次。如果地址中仍有零块,它们可以缩小到一个零:
例如(第二块):
2001:0:3238:DFE1:63::FEFB
确定开发板运行Linux内核是否支持Ipv6协议栈,可通过对网卡添加Ipv6地址是否被允许来判断。当对网卡添加IPv6地址不被允许时,会出现“ifconfig: socket: Address family not supported by protocol”这个错误信息。如下图所示。
以Linux 4.9.16内核版本为例,可以2种方法来使内核支持Ipv6协议栈:
1)重新配置内核,编译镜像,使之支持IPv6;
2)编译IPv6协议栈模块,利用insmod方式加载模块。
运行命令“make ARCH=arm menuconfig”,依次在内核配置单选择Networking support -> Networking options -> The IPv6 protocol
由于需要将IPv6协议栈编译进内核,所以选择“*”的方式。
以AM5728开发板添加IPv6配置为例:
网卡eth0没有配置ipv6地址之前:
为网卡eth0配置ipv6地址后:
如上图所示,我们使用命令“ifconfig eth0 add 1::1”添加了Ipv6地址。inet6中有两个ipv6地址,这两个地址是有区别的,就是尾部的Global和Link两个关键字。要想使用ipv6进行网络通信,不能使用Link标识的inet6字段,因为该地址私有,供给内核使用。所以需要我们使用手动添加相应的ipv6地址且标识为Global inet6地址来进行通信。如果网卡没有该标识的inet6地址,可手动添加。
局域网内以PC机和AM5728开发板为例。
AM5728开发板 Linux下ping一下网卡的私有Ipv6地址,验证Ipv6协议栈的支持。
打开自己的PC机,进行开发板与PC机之间的ipv6通信测试。windows下查看ipv6地址:
windows下通过ipconfig看到的ipv6地址如下:
fe80::44ac:4b43:a82e:e5dd%18
%后面是本ipv6地址对应的网络接口的index,windows术语叫scope id,可理解为一个接口序号。
在开发板侧ping PC机的ipv6地址。
直接使用“ping6 fe80::34c7:7536:f8f1:4de6
”命令测试也可以。
注意:
如果在同一网段中的不同机器上进行通信连接,需注意防火墙是否关闭,针对inet6地址的防火墙它有自己的防火墙名为ip6tables,关闭即可,负责客户端进行连接是会出现connect:: Permission denied错误,或者在ip6tables防火墙配置中配置对应的端口放行即可,在udt封装的协议中,如果防火墙未关闭,会出现Connection setup failure: connection time out.
查看防火墙状态:service ip6tables status
关闭防火墙:service ip6tables stop
参考:https://blog.csdn.net/zhengxianghdu/article/details/14106167
准备两台Linux开发板,进行配置。
1)两台linux用网线直接相连
2)手动配置两台linux的ipv6地址为:
ifconfig eth0 add 2001:da8:e000::1:1:1
ifconfig eth0 add 2001:da8:e000::1:1:2
1)server端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
struct sockaddr_in6 s_addr;
struct sockaddr_in6 c_addr;
int sock;
socklen_t addr_len;
int len;
char buff[128];
char buf_ip[128];
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(errno);
} else
printf("create socket.\n\r");
memset(&s_addr, 0, sizeof(struct sockaddr_in6));
s_addr.sin6_family = AF_INET6;
if (argv[2])
s_addr.sin6_port = htons(atoi(argv[2]));
else
s_addr.sin6_port = htons(4444);
if (argv[1])
inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
else
s_addr.sin6_addr = in6addr_any;
if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1) {
perror("bind");
exit(errno);
} else
printf("bind address to socket.\n\r");
addr_len = sizeof(c_addr);
while (1) {
len = recvfrom(sock, buff, sizeof(buff) - 1, 0,
(struct sockaddr *) &c_addr, &addr_len);
if (len < 0) {
perror("recvfrom");
exit(errno);
}
buff[len] = '\0';
printf("receive from %s: buffer:%s\n\r",
inet_ntop(AF_INET6, &c_addr.sin6_addr, buf_ip, sizeof(buf_ip)),
buff);
}
return 0;
}
2)client端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
struct sockaddr_in6 s_addr;
int sock;
int addr_len;
int len;
char buff[128];
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(errno);
} else
printf("create socket.\n\r");
s_addr.sin6_family = AF_INET6;
if (argv[2])
s_addr.sin6_port = htons(atoi(argv[2]));
else
s_addr.sin6_port = htons(4444);
if (argv[1])
inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
else {
printf("usage:./command ip port\n");
exit(0);
}
addr_len = sizeof(s_addr);
strcpy(buff, "hello i'm here");
len = sendto(sock, buff, strlen(buff), 0,
(struct sockaddr *) &s_addr, addr_len);
if (len < 0) {
printf("\n\rsend error.\n\r");
return 3;
}
printf("send success.\n\r");
return 0;
}
1)server端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUF 1024
int main(int argc, char **argv)
{
int sockfd, new_fd;
socklen_t len;
/* struct sockaddr_in my_addr, their_addr; */ // IPv4
struct sockaddr_in6 my_addr, their_addr; // IPv6
unsigned int myport, lisnum;
char buf[MAXBUF + 1];
if (argv[1])
myport = atoi(argv[1]);
else
myport = 7838;
if (argv[2])
lisnum = atoi(argv[2]);
else
lisnum = 2;
/* if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { */ // IPv4
if ((sockfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { // IPv6
perror("socket");
exit(1);
} else
printf("socket created\n");
bzero(&my_addr, sizeof(my_addr));
/* my_addr.sin_family = PF_INET; */ // IPv4
my_addr.sin6_family = PF_INET6; // IPv6
/* my_addr.sin_port = htons(myport); */ // IPv4
my_addr.sin6_port = htons(myport); // IPv6
if (argv[3])
/* my_addr.sin_addr.s_addr = inet_addr(argv[3]); */ // IPv4
inet_pton(AF_INET6, argv[3], &my_addr.sin6_addr); // IPv6
else
/* my_addr.sin_addr.s_addr = INADDR_ANY; */ // IPv4
my_addr.sin6_addr = in6addr_any; // IPv6
/* if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) */ // IPv4
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_in6)) // IPv6
== -1) {
perror("bind");
exit(1);
} else
printf("binded\n");
if (listen(sockfd, lisnum) == -1) {
perror("listen");
exit(1);
} else
printf("begin listen\n");
while (1) {
len = sizeof(struct sockaddr);
if ((new_fd =
accept(sockfd, (struct sockaddr *) &their_addr,
&len)) == -1) {
perror("accept");
exit(errno);
} else
printf("server: got connection from %s, port %d, socket %d\n",
/* inet_ntoa(their_addr.sin_addr), */ // IPv4
inet_ntop(AF_INET6, &their_addr.sin6_addr, buf, sizeof(buf)), // IPv6
/* ntohs(their_addr.sin_port), new_fd); */ // IPv4
their_addr.sin6_port, new_fd); // IPv6
bzero(buf, MAXBUF + 1);
strcpy(buf, "This is sever to client!\n");
len = send(new_fd, buf, strlen(buf), 0);
if (len < 0) {
printf
("message '%s' error number:%d,error mes:'%s'\n", buf, errno, strerror(errno));
} else
printf("message'%s'success length: %d\n", buf, len);
bzero(buf, MAXBUF + 1);
len = recv(new_fd, buf, MAXBUF, 0);
if (len > 0)
printf("receive message:'%s',length:%d\n", buf, len);
else
printf
("message error number:%d,error mes:'%s'\n", errno, strerror(errno));
}
close(sockfd);
return 0;
}
2) client端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXBUF 1024
int main(int argc, char **argv)
{
int sockfd, len;
/* struct sockaddr_in dest; */ // IPv4
struct sockaddr_in6 dest; // IPv6
char buffer[MAXBUF + 1];
if (argc != 3) {
printf
("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
argv[0], argv[0]);
exit(0);
}
/* socket for tcp */
/* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4
if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) { // IPv6
perror("Socket");
exit(errno);
}
printf("socket created\n");
/* 初始化服务器端(对方)的地址和端口信息 */
bzero(&dest, sizeof(dest));
/* dest.sin_family = AF_INET; */ // IPv4
dest.sin6_family = AF_INET6; // IPv6
/* dest.sin_port = htons(atoi(argv[2])); */ // IPv4
dest.sin6_port = htons(atoi(argv[2])); // IPv6
/* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4
if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) { // IPv6
perror(argv[1]);
exit(errno);
}
printf("address created\n");
/* 连接服务器 */
if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
perror("Connect ");
exit(errno);
}
printf("server connected\n");
/* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
bzero(buffer, MAXBUF + 1);
/* 接收服务器来的消息 */
len = recv(sockfd, buffer, MAXBUF, 0);
if (len > 0)
printf("接收消息成功:'%s',共%d个字节的数据\n",
buffer, len);
else
printf
("消息接收失败!错误代码是%d,错误信息是'%s'\n",
errno, strerror(errno));
bzero(buffer, MAXBUF + 1);
strcpy(buffer, "这是客户端发给服务器端的消息\n");
/* 发消息给服务器 */
len = send(sockfd, buffer, strlen(buffer), 0);
if (len < 0)
printf
("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
buffer, errno, strerror(errno));
else
printf("消息'%s'发送成功,共发送了%d个字节!\n",
buffer, len);
/* 关闭连接 */
close(sockfd);
return 0;
}
1) https://blog.csdn.net/qq_26105397/article/details/80929087
ipv6网络通信方式
2) https://blog.csdn.net/zhengxianghdu/article/details/17392849
Linux系统中添加IPv6模块(TQ2440开发板)
3) https://www.cnblogs.com/lfxiao/p/10102294.html
局域网内使用ipv6 通信
4) https://blog.csdn.net/zhengxianghdu/article/details/14106167
简单的IPv6 UDP/TCP socket编程
适用于:Debian / Ubuntu
开启IPv6功能
有些镜像是禁用了IPv6功能的,因此需要开启。首先查看一下是否被禁用了:
root@HIzcND1000044:~# sysctl -a | grep ipv6.*disable
sysctl: reading key "net.ipv6.conf.all.stable_secret"
net.ipv6.conf.all.disable_ipv6 = 1
sysctl: reading key "net.ipv6.conf.default.stable_secret"
net.ipv6.conf.default.disable_ipv6 = 1
sysctl: reading key "net.ipv6.conf.eth0.stable_secret"
net.ipv6.conf.eth0.disable_ipv6 = 1
sysctl: reading key "net.ipv6.conf.lo.stable_secret"
net.ipv6.conf.lo.disable_ipv6 = 1
disable=1说明被禁用了,因此需要去修改,配置文件为/etc/sysctl.conf:
vi /etc/sysctl.conf
#把ipv6 disable的参数都改为0,然后保存,重载服务后生效
[root@hSxavP1000044 ~]# sysctl -p
root@HIzcND1000044:~# sysctl -p
vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time = 120
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0
设置网卡,启用DHCP
nano /etc/network/interfaces
添加一行iface eth0 inet6 dhcp,举例示意:
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet static
address 172.16.2.X
netmask 255.240.0.0
gateway 172.16.2.1
iface eth0 inet6 dhcp
重启网络,检查是否正常
service networking restart