如今,随着网络的日益普及,IPv4地址数量相对于与日俱增的网络设备而言,简直太稀缺了,已经远远无法满足日常需求,因此出现了IPv6协议,并开始逐渐向IPv6进行过渡。在如今的过渡阶段中,网络环境中同时存在IPv4网络和IPv6网络,因此开发软件时通常要求支持双栈的功能。
同时监控双栈(IPv4栈、IPv6栈)下的指定端口号实现上也比较简单,分别创建两个套接字:
只是这里面有一个需要注意的地方:
创建两个UDP套接字,分别监听IPv4,IPv6端口,不存在特别的问题。但是TCP却不同,如果先创建并绑定IPv4的端口,则另一个套接字无法直接绑定IPv6的相同端口。会提示如下信息:
bind failed: : Address already in use
此时需要设置IPv6套接字的高级选项(IPPROTO_IPV6)方可以进行绑定和监听:
static const int on = 1;
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,(const void *)&on, sizeof(on))
UDP无需此项参数设置,具体原因未知。
完整代码实现如下(包括udp, tcp监控双栈代码):
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
int creatSocketUdp(char *ip, short port)
{
static const int on = 1;
int fd;
struct sockaddr_in server;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0))<0){
perror("creatSocketUdp_v6 create socket failed: ");
return -1;
}else if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))<0){
perror("creatSocketUdp_v6 set REUSEADDR failed: ");
return -1;
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(fd, (struct sockaddr *)&server, sizeof(server));
if (ret != 0){
perror("creatSocketUdp_v6 bind failed: ");
return -1;
}
return fd;
}
int creatSocketUdp_v6(char *ip, short port)
{
static const int on = 1;
int fd;
struct sockaddr_in6 server;
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0))<0){
perror("creatSocketUdp_v6 create socket failed: ");
return -1;
}else if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))<0){
perror("creatSocketUdp_v6 set REUSEADDR failed: ");
return -1;
/* }else if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,(const void *)&on, sizeof(on))<0){
perror("creatSocketUdp_v6 set REUSEADDR failed: ");
return -1; */
}
bzero(&server, sizeof(server));
server.sin6_family = AF_INET6;
server.sin6_addr = in6addr_any;
server.sin6_port = htons(port);
int ret = bind(fd, (struct sockaddr *)&server, sizeof(server));
if (ret != 0){
perror("creatSocketUdp_v6 bind failed: ");
return -1;
}
return fd;
}
int creatSocketTcp(char *ip, short port)
{
static const int on = 1;
int fd;
struct sockaddr_in server;
if ((fd = socket(AF_INET, SOCK_STREAM, 0))<0){
perror("creatSocketTcp create socket failed: ");
return -1;
}else if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))<0){
perror("creatSocketTcp set REUSEADDR failed: ");
return -1;
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(fd, (struct sockaddr *)&server, sizeof(server));
if (ret != 0){
perror("creatSocketTcp bind failed: ");
return -1;
}
if (listen(fd, 5)<0){
perror("creatSocketTcp listen failed: ");
return -1;
}
return fd;
}
int creatSocketTcp_v6(char *ip, short port)
{
static const int on = 1;
int fd;
struct sockaddr_in6 server;
if ((fd = socket(AF_INET6, SOCK_STREAM, 0))<0){
perror("creatSocketTcp_v6 create socket failed: ");
return -1;
}else if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))<0){
perror("creatSocketTcp_v6 set REUSEADDR failed: ");
return -1;
}else if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,(const void *)&on, sizeof(on))<0){
perror("creatSocketTcp_v6 set REUSEADDR failed: ");
return -1;
}
bzero(&server, sizeof(server));
server.sin6_family = AF_INET6;
server.sin6_addr = in6addr_any;
server.sin6_port = htons(port);
int ret = bind(fd, (struct sockaddr *)&server, sizeof(server));
if (ret != 0){
perror("creatSocketTcp_v6 bind failed: ");
return -1;
}
if (listen(fd, 5)<0){
perror("creatSocketTcp_v6 listen failed: ");
return -1;
}
return fd;
}
void main(int argc, char** argv)
{
char buf[1024] = {0};
int buflen = 0;
int clientLen = 0;
struct sockaddr client;
int ret = 0;
int fd ;
int fd6;
if (argc != 2){
printf("Usage: ./a.out tcp|udp\n");
return ;
}else if(strstr(argv[1],"udp")){
fd = creatSocketUdp(NULL, 8080);
if(fd < 0){
printf("%s:%d error\n", __func__, __LINE__);
return ;
}
fd6 = creatSocketUdp_v6(NULL, 8080);
if(fd < 0){
printf("%s:%d error\n", __func__, __LINE__);
return ;
}
}else if(strstr(argv[1],"tcp")){
fd = creatSocketTcp(NULL, 8080);
if(fd < 0){
printf("%s:%d error\n", __func__, __LINE__);
return ;
}
fd6 = creatSocketTcp_v6(NULL, 8080);
if(fd6 < 0){
printf("%s:%d error\n", __func__, __LINE__);
return ;
}
}else{
printf("Usage: ./a.out tcp|udp\n");
return ;
}
bzero(&client, sizeof(client));
fd_set readfds;
int maxFD = 0;
struct timeval timeVal;
while(1){
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
FD_SET(fd, &readfds);
maxFD = fd6 > fd ? fd6:fd;
timeVal.tv_sec = 1;
timeVal.tv_usec = 0;
ret = select(maxFD, &readfds,NULL,NULL,&timeVal);
if (ret == 0){
printf("【Timeout!!!】\n");
continue;
}else if (ret < 0){
perror("Whack Fucking error??? :");
break;
}else{
if (FD_ISSET(fd, &readfds)){
buflen = recvfrom(fd, buf, 1024, 0, (struct sockaddr *)&client, &clientLen);
if(buflen>0){
printf("【recv packet v4】: %s\n", buf);
sendto(fd, buf, buflen, 0, (struct sockaddr *)&client, clientLen);
}
}else if(FD_ISSET(fd, &readfds)){
buflen = recvfrom(fd, buf, 1024, 0, (struct sockaddr *)&client, &clientLen);
if(buflen>0){
printf("【recv packet v6】: %s\n", buf);
sendto(fd, buf, buflen, 0, (struct sockaddr *)&client, clientLen);
}
}
}
}
}
|