winsock2:服务器端代码调用`accept()`后如何获取已连接客户端的ipv4/ipv6地址
Posted
技术标签:
【中文标题】winsock2:服务器端代码调用`accept()`后如何获取已连接客户端的ipv4/ipv6地址【英文标题】:winsock2: How to get the ipv4/ipv6 address of a connected client after server side code calls `accept()` 【发布时间】:2021-12-11 00:03:42 【问题描述】:此站点上还有其他类似的问题,但它们要么与 winsock2 无关,要么仅适用于 ipv4 地址空间。 Visual Studio 2019 的默认编译器在使用 ntoa
函数时会产生错误,因此需要 ipv4 和 ipv6 解决方案。
我曾经为 Linux 系统编写过代码来执行此操作,但是我目前正在工作并且无法访问该代码。它可能会或可能不会“复制和粘贴”到带有winsock2 的Windows 环境中。 (编辑:我当然会在今晚晚些时候添加该代码,但当然它可能没有用。)
以下包含一个示例,但是这是客户端代码的示例,不是服务器端代码。
https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedInternet3c.html
这里,getaddrinfo()
函数用于获取包含匹配 ipv4 和 ipv6 地址的结构。要获取此信息,需要与 DNS 进行一些交互,在这种情况下不需要。
我有一些服务器代码调用accept()
(在绑定和监听之后)接受客户端连接。我希望能够将客户端 IP 地址和端口打印到标准输出。
这个网站上最相关的问题是here。但是答案使用ntoa
,并且仅与 ipv4 兼容。
到目前为止我所拥有的:
到目前为止,我的草图如下:
SOCKET acceptSocket = INVALID_SOCKET;
SOCKADDR_IN addr; // both of these are NOT like standard unix sockets
// I don't know how they differ and if they can be used with standard
// unix like function calls (eg: inet_ntop)
int addrlen = sizeof addr;
acceptSocket = accept(listenSocket, (SOCKADDR*)&addr, &addrlen);
if(acceptSocket == INVALID_SOCKET)
// some stuff
else
const std::size_t addrbuflen = INET6_ADDRSRTLEN;
char addrbuf[addrbuflen] = '\0'
inet_ntop(AF_INET, (void*)addr.sin_addr, (PSTR)addrbuf, addrbuflen);
// above line does not compile and mixes unix style function calls
// with winsock2 structures
std::cout << addrbuf << ':' << addr.sin_port << std::endl;
getpeername()
int ret = getpeername(acceptSocket, addrbuf, &addrbuflen);
// addrbuf cannot convert from char[65] to sockaddr*
if(ret == ???)
// TODO
【问题讨论】:
但接受直接返回这样的地址。在什么问题/问题上? @RbMmaccept()
返回某种类型的套接字结构,而不是 ascii 字符串
是的,当然是socket结构。那又怎样?
SOCKADDR_IN addr
这里你猜地址是 ipv4。你硬编码这个。如果更通用 - 使用SOCKADDR_INET addr
。看起来你混淆了地址和它的字符串表示。地址可以通过WSAAddressToString
转换为字符串。
@RbMm 对不起,我完全不知道你在说什么。是的,我使用 ipv4 结构来保存地址。我问这个的唯一原因是因为 VS2019 不支持 ntoa
。该解决方案不必实际接受 ipv6 地址,只需使用 inet_ntop
或等效函数即可。
【参考方案1】:
您需要访问SOCKADDR
。这实际上是一个有区别的工会。第一个字段告诉您它是 IPv4 (==AF_INET
) 还是 IPv6 (==AF_INET6
) 地址。根据您将addr
指针转换为struct sockaddr_in*
或struct sockaddr_in6*
,然后从相关字段中读取IP 地址。
【讨论】:
什么时候接受已经返回这个? 查看我添加的最后一段,尽管在这里查看文档docs.microsoft.com/en-us/windows/win32/api/winsock2/…,但我不确定如何使用此功能 不,accept
给你一个套接字,它是一个类似于文件描述符的本地结构。 getPeerName
在socket代表的连接的另一端查找机器的详细信息,这就是你想要的。
@PaulJohnson - getpeername
与 accept
具有相同的输出 - 因为这个调用是毫无意义的
啊,抱歉,我错过了accept
中的输出 sockaddr。我会再试一次。【参考方案2】:
vs2019中的C++代码sn-p:
char* CPortListener::get_ip_str(struct sockaddr* sa, char* s, size_t maxlen)
switch (sa->sa_family)
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr),
s, maxlen);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr),
s, maxlen);
break;
default:
strncpy(s, "Unknown AF", maxlen);
return NULL;
return s;
Example:
...
char s[INET6_ADDRSTRLEN];
sockaddr_storage ca;
socklen_t al = sizeof(ca);
SOCKET recv = accept(sd, (sockaddr*)&ca, &al);
pObj->m_ip = get_ip_str(((sockaddr*)&ca),s,sizeof(s));
【讨论】:
以上是关于winsock2:服务器端代码调用`accept()`后如何获取已连接客户端的ipv4/ipv6地址的主要内容,如果未能解决你的问题,请参考以下文章
TSINGSEE青犀视频编译Winsock2 websocket服务端连接异常断开问题排查
Netty——NIO(Selector处理accept事件)代码示例
Netty——NIO(Selector处理accept事件)代码示例