C++ UDP兼容IPv4与IPv6

IPv4和IPv6两套不同的API,应该默认使用IPv4,可选择IPv6
为IPv4和IPv6绑定不同的socket,利用epoll和iocp的多路复用同时监听

linux下 AF_INET6默认同时使用IPv4,表示为::ffff:x.x.x.x
https://tools.ietf.org/html/rfc3493#page-22
5.3 IPV6_V6ONLY option for AF_INET6 Sockets

This socket option restricts AF_INET6 sockets to IPv6 communications
only. As stated in section ❤️.7 Compatibility with IPv4 Nodes>,
AF_INET6 sockets may be used for both IPv4 and IPv6 communications.
Some applications may want to restrict their use of an AF_INET6
socket to IPv6 communications only. For these applications the
IPV6_V6ONLY socket option is defined. When this option is turned on,
the socket can be used to send and receive IPv6 packets only. This
is an IPPROTO_IPV6 level option. This option takes an int value.
This is a boolean option. By default this option is turned off.

Here is an example of setting this option:

 //linux下开启仅IPv6模式
 int ipv6only = 1;
 if (setsockopt(m_nListenFd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)) != 0) {
 	cout << "set ipv6only failed!";
 }

 //windows下开启仅IPv6兼IPv4模式
 int ipv6only = 0;
 if (setsockopt(m_nListenFd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)) != 0) {
 	cout << "set ipv6only failed!";
 }

Note - This option has no effect on the use of IPv4 Mapped addresses
which enter a node as a valid IPv6 addresses for IPv6 communications
as defined by Stateless IP/ICMP Translation Algorithm (SIIT) [5].

An example use of this option is to allow two versions of the same
server process to run on the same port, one providing service over
IPv6, the other providing the same service over IPv4.

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <thread>
#include <netdb.h>
#include <string.h>
using namespace std;

int wPort = 22345;

void listen4()
{
	addrinfo hints;
	addrinfo* m_ListenAddr;
	bzero(&hints, sizeof(addrinfo));
	hints.ai_flags = AI_PASSIVE;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_family = AF_INET;
	int m_nListenFd;
	if (0 != getaddrinfo(NULL, std::to_string(wPort).c_str(), &hints, &m_ListenAddr))
	{
		cout << "[init] getaddrinfo failed";
	}
	//创建socket
	m_nListenFd = socket(m_ListenAddr->ai_family, m_ListenAddr->ai_socktype,
		m_ListenAddr->ai_protocol);
	if (-1 == m_nListenFd)
	{
		cout << "[init] create socket error!";
		//return false;
	}

	uint32_t nReuse = 1;
	//设定地址复用
	if (-1 == setsockopt(m_nListenFd, SOL_SOCKET, SO_REUSEADDR, &nReuse, sizeof(uint32_t)))
	{
		cout << "[connect] reuse addr failed";
	}

	//设定端口复用
	if (-1 == setsockopt(m_nListenFd, SOL_SOCKET, SO_REUSEPORT, &nReuse, sizeof(uint32_t)))
	{
		cout << "[connect] reuse port failed";
	}

	//绑定IP地址端口信息
	if (bind(m_nListenFd, m_ListenAddr->ai_addr, m_ListenAddr->ai_addrlen) < 0)
	{
		cout << "[init] bind socket error!";
		//return false;
	}



	while (1)
	{
		sockaddr* sock = new sockaddr();
		socklen_t len = sizeof(sockaddr_in);
		char buf[1024];
		int res = recvfrom(m_nListenFd, buf, 1024, 0, sock, &len);
		cout << "recv :" << buf << endl;
		if (AF_INET == sock->sa_family)
		{
			char Buf[INET_ADDRSTRLEN];
			int Port = ntohs(((sockaddr_in*)sock)->sin_port);
			inet_ntop(sock->sa_family, &((sockaddr_in*)sock)->sin_addr, Buf, INET_ADDRSTRLEN);
			cout << Buf << ":" << Port << endl;
		}
	}
}

void listen6()
{
	addrinfo hints;
	addrinfo* m_ListenAddr;
	bzero(&hints, sizeof(addrinfo));
	hints.ai_flags = AI_PASSIVE;
	hints.ai_socktype = SOCK_DGRAM;
	hints.ai_family = AF_INET6;
	int m_nListenFd;
	if (0 != getaddrinfo(NULL, std::to_string(wPort).c_str(), &hints, &m_ListenAddr))
	{
		cout << "[init] getaddrinfo failed";
	}
	//创建socket
	m_nListenFd = socket(m_ListenAddr->ai_family, m_ListenAddr->ai_socktype,
		m_ListenAddr->ai_protocol);
	if (-1 == m_nListenFd)
	{
		cout << "[init] create socket error!";
		//return false;
	}

	uint32_t nReuse = 1;
	//设定地址复用
	if (-1 == setsockopt(m_nListenFd, SOL_SOCKET, SO_REUSEADDR, &nReuse, sizeof(uint32_t)))
	{
		cout << "[connect] reuse addr failed";
	}

	//设定端口复用
	if (-1 == setsockopt(m_nListenFd, SOL_SOCKET, SO_REUSEPORT, &nReuse, sizeof(uint32_t)))
	{
		cout << "[connect] reuse port failed";
	}

	int ipv6only = 1;
	if (setsockopt(m_nListenFd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ipv6only, sizeof(ipv6only)) != 0) {
		cout << "set ipv6only failed!";
	}

	//绑定IP地址端口信息
	if (bind(m_nListenFd, m_ListenAddr->ai_addr, m_ListenAddr->ai_addrlen) < 0)
	{
		cout << "[init] bind socket error!";
		//return false;
	}



	while (1)
	{
		sockaddr* sock = new sockaddr();
		socklen_t len = sizeof(sockaddr_in6);
		char buf[1024];
		int res = recvfrom(m_nListenFd, buf, 1024, 0, sock, &len);
		cout  << "recv :" << buf << endl;
		
		if (AF_INET6 == sock->sa_family)
		{
			char Buf[INET6_ADDRSTRLEN];
			int Port = ntohs(((sockaddr_in6*)sock)->sin6_port);
			if (NULL != inet_ntop(sock->sa_family, &((sockaddr_in6*)sock)->sin6_addr,
				Buf, INET6_ADDRSTRLEN))
			{
				cout << Buf << ":" << Port << endl;
			}

		}
	}
}


void xsend()
{
	int lfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	sockaddr_in* sockaddr1 = new sockaddr_in();
	sockaddr1->sin_family = AF_INET;

	sockaddr1->sin_port = htons(wPort);
	inet_pton(AF_INET, "127.0.0.1", &sockaddr1->sin_addr);
	socklen_t len = sizeof(sockaddr_in6);
	char buf[1024];
	cin >> buf;
	cout << sendto(lfd, buf, 1024, 0, (sockaddr*)sockaddr1, 16) << endl;
}

void xsend6()
{
	int lfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	sockaddr_in6* sockaddr1 = new sockaddr_in6();
	sockaddr1->sin6_family = AF_INET6;

	sockaddr1->sin6_port = htons(wPort);
	inet_pton(AF_INET6, "::1", &sockaddr1->sin6_addr);
	socklen_t len = sizeof(sockaddr_in6);
	char buf[1024];
	cin >> buf;
	cout << sendto(lfd, buf, 1024, 0, (sockaddr*)sockaddr1, sizeof(sockaddr_in6)) << endl;
}

int main()
{
	thread t1(listen4);
	t1.detach();
	thread t2(listen6);
	t2.detach();

	while (1)
	{
		xsend();
		xsend6();
	}
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值