我们知道Recvfrom函数具有以下概要
SYNOPSIS
#include <sys/socket.h>
int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
Run Code Online (Sandbox Code Playgroud)
from具有sockaddr的结构。
struct sockaddr {
__uint8_t sa_len; // total length
sa_family_t sa_family; // [XSI] address family
char sa_data[14]; // [XSI] addr value (actually larger)
};
Run Code Online (Sandbox Code Playgroud)
但是sockaddr似乎无法保存IP地址。
不应该使用struct socaddr_in * from
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Run Code Online (Sandbox Code Playgroud)
然后sin_addr将提供IP地址。这是一个有效的假设吗?
出于历史原因,将该from参数定义为sockaddr*支持早于IPv6的旧代码。sock_addr虽然是不可知的,但是它还不足以处理较新的套接字类型。任何具有sockaddr*参数的套接字函数实际上都期望sockaddr适合于所使用套接字类型的基于结构的结构。
如果您是从IPv4套接字读取的,则期望使用sockaddr_in*,例如:
struct sockaddr_in from;
socklen_t len = sizeof(from);
recvfrom(s, ..., (struct sockaddr*)&from, &len);
// use from.sin_addr and from.sin_port as needed...
Run Code Online (Sandbox Code Playgroud)
如果您是从IPv6套接字读取的,则期望使用一个sockaddr_in6*代替,例如:
struct sockaddr_in6 from;
socklen_t len = sizeof(from);
recvfrom(s, ..., (struct sockaddr*)&from, &len);
// use from.sin6_addr and from.sin6_port as needed...
Run Code Online (Sandbox Code Playgroud)
如果要编写支持多种协议的代码,请sockaddr_storage根据需要使用和类型转换,例如:
struct sockaddr_storage from;
socklen_t len = sizeof(from);
recvfrom(s, ..., (struct sockaddr*)&from, &len);
switch (from.ss_family)
{
case AF_INET:
// use ((struct sockaddr_in*)&from) as needed...
break;
case AF_INET6:
// use ((struct sockaddr_in6*)&from) as needed...
break;
...
}
Run Code Online (Sandbox Code Playgroud)
这同样适用于其他sockaddr基于功能,包括connect(),bind(),accept()和sendto()。
您可以按如下方式进行:
struct scokaddr_in A;
char buf[200];
int len;
recvfrom(fd, buf, 200, 0, (struct sockaddr*)&A, &len);
//from ip-address is stored in A.sin_addr...
Run Code Online (Sandbox Code Playgroud)