如何在C中比较套接字地址?

时间:2013-03-17 13:37:28

标签: c sockets

我的意思是,当我检查两个struct sockaddr是否具有相同的IP地址和端口号时,我应该比较struct sockaddr的哪些字段?那么sockaddr_in呢?

我可以将sockaddr_in投射到sockaddr,并将其与真实sockaddr进行比较吗?

2 个答案:

答案 0 :(得分:11)

首先,您需要检查系列(IPv4,IPv6或其他)。然后你可以将每个sockaddr转换为适当的“派生”类型,如sockaddr_in。了解Apple如何在此处执行此操作:http://www.opensource.apple.com/source/postfix/postfix-197/postfix/src/util/sock_addr.c

答案 1 :(得分:0)

首先,当您处理时,您需要使用struct sockaddr_storage,因为struct sockaddr仅对指针是安全的,否则您将遇到大小和对齐问题。

其次,struct sockaddr是C中传递的“基类”。第一个成员是sa_family_t sa_family;(尽管,因为这个struct早于结构成员位于不同的名称空间中,每个“子类”使用唯一的前缀(我遇到过至少40个子类))。

第三 - 虽然您可能会认为每个struct,但事实证明,类的大小因内核/库版本而异。因此,您总是必须通过sizeof()实际的struct sockaddr_FOO才能。例如,struct sockaddr_in6的旧版本没有sin6_scope_id成员。

你应该将它包装在struct中以获得理智(并提供各种辅助函数):

struct SocketAddress
{
    struct sockaddr_storage addr;
    socklen_t addr_len;
};

然后,您的比较代码如下:

// returns < 0 if (left < right)
// returns > 0 if (left > right)
// returns 0 if (left == right)
// Note that in general, "less" and "greater" are not particularly
// meaningful, but this does provide a strict weak ordering since
// you probably need one.
int socket_cmp(struct SocketAddress *left, struct SocketAddress *right)
{
    socklen_t min_addr_len = MIN(left->addr_len, right->addr_len;
    // If head matches, longer is greater.
    int default_rv = right->addr_len - left->addr_len;
    int rv = memcmp(left, right, min_addr_len);
    return rv ?: default_rv;
}

但是等等!虽然上面的代码足够小,如果你小心,小心仍然需要很多细节。例如:

  • 是否所有套接字地址都会在程序的单次运行中生成,或者是否会从某些外部介质中读取其中一些?对于后一种情况,您需要规范化sin6_scope_id等案例。

  • 您是否必须在同一程序中处理IPv4和IPv4-on-IPv6地址(映射为ffff::1.2.3.4)?最简单的方法是在程序中专门处理IPv6,因为相关功能也接受IPv4地址。为了获得最佳可移植性,请务必禁用(使用setsockoptIPV6_V6ONLY标志。或者,您可以启用该标志以帮助确保IPv6地址永远不会包含IPv4地址。 (请注意,localhost的查找不同 - 但这是所有域查找的一般问题,可能会返回多个结果。)

  • 您需要确保所有struct padding都归零。