兼容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中新建项目,修改客户端和服务端地址和端口为你自己的,编译运行即可