能,getpeername()可直接获取对端地址,但仅适用于已连接的socket(如connect()后的客户端或accept()返回的服务端socket),未连接时调用会因enotconn失败;udp需先connect()才生效,且应配合sockaddr_storage与getnameinfo()统一处理ipv4/ipv6。

getpeername() 能否直接获取对端地址?
能,但必须在连接建立后调用,且仅适用于已连接的 socket(如 connect() 成功后的客户端,或 accept() 返回的服务端 socket)。未连接的 socket(如刚 socket() 创建但没 connect() 的)调用会失败,返回 ENOTCONN 错误。
常见错误是:在 listen() 后、accept() 前对监听 socket 调用 getpeername() —— 这必然失败,因为监听 socket 本身不对应任何具体对端。
- 服务端应只对
accept()返回的 client socket 调用 - 客户端应在
connect()返回成功后立即调用 - 调用前需确保
sockaddr_storage结构体大小参数传准确(如用sizeof(addr),而非硬编码)
IPv4 和 IPv6 地址如何统一处理?
用 sockaddr_storage 配合 getnameinfo() 是最稳妥的方式。它能自动适配 AF_INET 和 AF_INET6,避免手动判断地址族、拆解 sockaddr_in/sockaddr_in6 字段出错。
直接 cast 到 sockaddr_in 并取 sin_addr 和 sin_port 只适用于 IPv4;若 socket 实际连的是 IPv6(哪怕走 IPv4-mapped IPv6 地址),就会读错字段、得到乱码或 0。
-
sockaddr_storage足够大,兼容所有地址族 -
getnameinfo()的NI_NUMERICHOST+NI_NUMERICSERV标志确保输出纯数字 IP 和端口字符串,不触发 DNS 查询 - 注意
getnameinfo()返回的 port 是网络字节序,需用ntohs()转换(getnameinfo()内部已处理,无需额外转换)
代码示例:安全获取对端 IP 和端口
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(addr);
if (getpeername(sockfd, (struct sockaddr*)&addr, &addrlen) == 0) {
char ip_str[INET6_ADDRSTRLEN];
char port_str[16];
if (getnameinfo((struct sockaddr*)&addr, addrlen,
ip_str, sizeof(ip_str),
port_str, sizeof(port_str),
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
printf("Peer: %s:%s\n", ip_str, port_str); // 如 "192.168.1.100:54321"
}
}
关键点:不用 inet_ntop() 手动转换,也不用 ntohs() 处理端口 —— getnameinfo() 已封装这些细节。若只想要端口号整数,可把 port_str 用 atoi() 转,但字符串形式更安全(避免溢出或无效输入)。
UDP socket 能不能用 getpeername()?
不能,除非该 UDP socket 已调用 connect()。UDP 本身无连接概念,getpeername() 依赖内核维护的“已知对端”状态,只有 connect() 后才会设置。未 connect 的 UDP socket 调用会返回 ENOTCONN。
如果只是想获取 recvfrom() 收到数据报的来源地址,应检查 recvfrom() 的 addr 参数输出,而不是依赖 getpeername()。
- UDP 客户端若固定与某服务通信,建议
connect()一次,后续可用send()/recv()简化调用,且支持getpeername() - UDP 服务器通常不
connect(),因此不能用getpeername()获取客户端地址——必须靠recvfrom()的输出参数
sockaddr_in,上线后遇到 IPv6 就崩;还有人给无连接 UDP socket 调 getpeername() 却不检查返回值,导致逻辑跳过或崩溃。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











