为什么必须在这段代码中强制转换为 struct in_addr ?

Why does it have to cast to struct in_addr in this code?

TCPStream::TCPStream(int sd, struct sockaddr_in* address) : msd(sd) 
{
    char ip[50];
    inet_ntop(PF_INET, (struct in_addr*)&(address->sin_addr.s_addr), ip, sizeof(ip)-1);
    m_peerIP = ip;
    m_peerPort = ntohs(address->sin_port);
}

为什么必须在这段代码中转换为 struct in_addr

这段代码中的“50”是什么意思?

您不需要转换为 void*,这是第二个参数。

不过,您的代码确实存在一些其他问题。

让我们从 inet_ntop 调用的第一个参数开始:您使用 PF_INET。前缀 PF 代表 Protocol Family。由于您正在获取有关地址的信息,因此您应该使用 AF_INET 符号常量,其中 AF 代表 Address Family.

另一个问题是你做的(不必要的)演员表。如果您遵循这些结构,sockaddr_in 结构是在 <netinet/in.h> 头文件中定义的。成员 sin_addr 的类型为 in_addrin_addr 有一个 s_addr 类型的成员 in_addr_t(相当于 uint32_t)。也就是说,&(address->sin_addr.s_addr) 是一个指向无符号 32 位整数类型的指针。不是 struct in_addr*(即 &address->sin_addr)。

如果我们把所有这些放在一起,正确的调用应该是

inet_ntop(AF_INET, &address->sin_addr.s_addr, ip, sizeof ip);

现在数组的大小50。这有点大,因为 IPv4 地址最多可以有 16 个字符(包括 字符串终止符)。这也恰好是宏INET_ADDRSTRLEN.

的值

所以数组可以定义为

char ip[INET_ADDRSTRLEN];

address->sin_addr 字段是一个 in_addr 结构,它有一个数据字段 s_addr,即 uint32_t。因此,s_addr 字段的地址 碰巧 与其包含的 in_addr 的地址相同。虽然在这种情况下这种转换是 "safe",但它也是 错误的

根据 inet_ntop() 的 Linux 文档:

AF_INET
        `src` points to a `struct in_addr` (in network byte order) which
        is converted to an IPv4 network address in the dotted-decimal
        format, `"ddd.ddd.ddd.ddd"`.  The buffer `dst` must be at least
        `INET_ADDRSTRLEN` bytes long.

如您所见,inet_ntop() 需要一个 in_addr* 指向 IPv4 地址的指针。 address->sin_addr 字段是实际的 in_addr,因此您应该将 address->sin_addr 的地址传递给 inet_ntop(),而不是 address->sin_addr.s_addr.[=31= 的地址]

一个IPv4地址最多只需要15个字符,加上一个空终止符。所以 50 对你的 ip 缓冲区大小来说太过分了。 16INET_ADDRSTRLEN 的定义)就足够了。

正确的代码应该更像这样:

TCPStream::TCPStream(int sd, struct sockaddr_in* address) : msd(sd) 
{
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &(address->sin_addr), ip, sizeof(ip));
    m_peerIP = ip;
    m_peerPort = ntohs(address->sin_port);    
}