windows vc 通用socket 支持ipv6 客户端及服务端

本文介绍了一个兼容IPv4与IPv6的网络编程示例,包括客户端和服务端的实现方法。客户端通过TCP连接到指定的IPv6地址和端口,并发送消息。服务端监听指定的IPv6地址和端口,接收客户端的消息并回应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

兼容ipv4和ipv6

一、客户端

#include <iostream>
#include <string>
#include <errno.h>
#include <time.h>

#include <WinSock2.h>
#include <WS2tcpip.h>//getaddrinfo inet_ntop

#pragma comment(lib, "ws2_32.lib")

#define  close closesocket

using namespace std;

//进行tcp连接
int tcp_connect(const char* host, const char* service)
{
	int sockfd, ret;
	struct addrinfo hints, * res, * ressave;
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_IP;

    //通过ip和端口初始化地址信息,兼容ipv4和ipv6
	if (0 != (ret = getaddrinfo(host, service, &hints, &res)))
	{
		cout << "getaddrinfo error: " << gai_strerror(ret) << endl;
		return -1;
	}

	ressave = res;
	while (NULL != res)
	{
		cout << host << "---" << service << endl;
		if (-1 == (sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)))
		{
			cout << "create socket error: " << strerror(errno) << endl;
			res = res->ai_next;
			continue;
		}

		int size = 0x1UL << 26;   // 32M
		if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(size)) < 0) {
			cout << "call tcp_connect snd error" << endl;
			res = res->ai_next;
			continue;
		}
		if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(size)) < 0) {
			cout << "call tcp_connect rcv error" << endl;
			res = res->ai_next;
			continue;
		}
		if (-1 == connect(sockfd, res->ai_addr, res->ai_addrlen))
		{
			cout << "connect error: " << strerror(errno) << endl;
			close(sockfd);
			res = res->ai_next;
			continue;
		}

		printf("connect sucess.\n");
		break;
	}

	freeaddrinfo(ressave);

	if (NULL == res)
		return -1;

	return sockfd;
}

int main()
{
	//初始化WinSock
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
	{
		return 0;
	}

	int sockfd, n;
	char buff[128];
	struct sockaddr_storage cliaddr; 

    //要连接的服务端地址和端口
	char* ip = (char*)"fe80::b090:dc48:48fd:fa63";
	char* port = (char*)"5555";
	cout << ip << "---" << port << endl;

	sockfd = tcp_connect(ip, port);
	if (sockfd < 0)
	{
		cout << "call tcp_connect error" << endl;
		return -1;
	}

	int ret = send(sockfd, ip, strlen(ip), 0);
	memset(buff, 0, sizeof(buff));
	while ((n = recv(sockfd, buff, sizeof(buff) - 1, 0) > 0))
	{
		cout << buff << endl;
		memset(buff, 0, sizeof(buff));
	}
	close(sockfd);

	return 0;
}

二、服务端

调用方法:

#include <iostream>
#include <string>
#include <errno.h>
#include <time.h>

#include <WinSock2.h>
#include <WS2tcpip.h>//getaddrinfo inet_ntop

#pragma comment(lib, "ws2_32.lib")

using namespace std;

int tcp_listen(const char* host, const char* service, const int listen_num = 5)
{
	int listenfd, ret;
	const char on = 1;
	struct addrinfo hints, * res, * ressave;
	memset(&hints, 0, sizeof(hints));
	hints.ai_flags = AI_PASSIVE;
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;//SOCK_DGRAM;//SOCK_STREAM;
	hints.ai_protocol = IPPROTO_IP;

	if (0 != (ret = getaddrinfo(host, service, &hints, &res)))
	{
		cout << "getaddrinfo error: " << gai_strerrorA(ret) << endl;
		return -1;
	}

	ressave = res;
	while (NULL != res)
	{
		if (-1 == (listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)))
		{
			cout << "create socket error: " << strerror(errno) << endl;
			res = res->ai_next;
			continue;
		}

		if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
		{
			cout << "setsockopt error: " << strerror(errno) << endl;
			closesocket(listenfd);
			res = res->ai_next;
			continue;
		}

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

		if (-1 == bind(listenfd, res->ai_addr, res->ai_addrlen))
		{
			cout << "bind error: " << strerror(errno) << endl;
			closesocket(listenfd);
			res = res->ai_next;
			continue;
		}

		if (-1 == listen(listenfd, listen_num))
		{
			cout << "listen error: " << strerror(errno) << endl;
			closesocket(listenfd);
			res = res->ai_next;
			continue;
		}

		break;
	}

	freeaddrinfo(ressave);

	if (NULL == res)
		return -1;

	return listenfd;
}

int get_addrinfo(const struct sockaddr* addr, string& ip, uint16_t& port)
{
	void* numeric_addr = NULL;
	char addr_buff[INET6_ADDRSTRLEN];

	int len = sizeof(sockaddr_in);;
	char* b = (char*)addr;

	unsigned char str16;
	memset(&str16, 0, 1);
	for (int i = 0; i < len; i++)
	{
		memcpy(&str16, b + i, 1);
		printf("%02x ", str16);
	}
	printf("\n");

	if (AF_INET == addr->sa_family)
	{
		len = sizeof(sockaddr_in);
		numeric_addr = &((struct sockaddr_in*)addr)->sin_addr;
		port = ntohs(((struct sockaddr_in*)addr)->sin_port);

		b = (char*)((struct sockaddr_in*)addr);
	}
	else if (AF_INET6 == addr->sa_family)
	{
		len = sizeof(sockaddr_in6);
		numeric_addr = &((struct sockaddr_in6*)addr)->sin6_addr;
		port = ntohs(((struct sockaddr_in6*)addr)->sin6_port);

		b = (char*)((struct sockaddr_in6*)addr);
	}
	else
	{
		return -1;
	}

	memset(&str16, 0, 1);
	for (int i = 0; i < len; i++)
	{
		memcpy(&str16, b + i, 1);
		printf("%02x ", str16);
	}
	printf("\n");

	//printf("family:%d\n", addr->sa_family);
	if (NULL != inet_ntop(addr->sa_family, numeric_addr, addr_buff, sizeof(addr_buff)))
		ip = addr_buff;
	else
		return -1;

	return 0;
}

inline std::string get_remote_addr(int sock) {
	struct sockaddr_storage addr;
	socklen_t len = sizeof(addr);

	if (!getpeername(sock, (struct sockaddr*)&addr, &len)) {
		char ipstr[NI_MAXHOST];

		if (!getnameinfo((struct sockaddr*)&addr, len, ipstr, sizeof(ipstr),
			nullptr, 0, NI_NUMERICHOST)) {
			return ipstr;
		}
	}

	return std::string();
}

int main(int argc, char* argv[])
{
	int ret = 0;
	int listenfd, connfd;
	struct sockaddr_storage cliaddr;
	socklen_t len = sizeof(cliaddr);
	time_t now;
	char buff[128];

	//监听的ip和端口
    //char* ip = NULL;//ip为NULL时,监听本机所有地址
	char* ip = (char*)"fe80::b090:dc48:48fd:fa63";
    char* port = (char*)"5555";
    listenfd = tcp_listen(ip, port);
	
	if (listenfd < 0)
	{
		cout << "call tcp_listen error" << endl;
		return -1;
	}

	while (true)
	{
		connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &len);

		int size = 0x1UL << 26;   // 32M
		ret = setsockopt(connfd, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(size));
		if (ret < 0) {
			cout << "client error!"<< endl;
		}
		string ip = "";
		uint16_t port = 0;
		get_addrinfo((struct sockaddr*)&cliaddr, ip, port);
		cout << "client " << ip << "|" << port << " login" << endl;

		auto clientip = get_remote_addr(connfd);
		cout << "clientip " << clientip << endl;

		now = time(NULL);
		snprintf(buff, sizeof(buff) - 1, "%.24s", ctime(&now));
		send(connfd, buff, strlen(buff), 0);
		closesocket(connfd);
	}

	closesocket(listenfd);
	return 0;
}





#ifdef WIN32
class WSInit
{
public:
	WSInit()
	{
		WSADATA wsadata;
		WSAStartup(MAKEWORD(2, 2), &wsadata);
	}

	~WSInit() { WSACleanup(); }
};

static WSInit wsinit_;
#endif

三、使用

在vs中新建项目,修改客户端和服务端地址和端口为你自己的,编译运行即可

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值