【问题标题】:Linux: sockaddr_storage how to initialize it?Linux:sockaddr_storage 怎么初始化呢?
【发布时间】:2018-06-05 19:17:23
【问题描述】:

我有一个基于 TCP 的程序,它同时支持 IPv4IPv6。所以我在代码中使用了“sockaddr_storage”。

client 端,我需要将客户端 TCP 端口固定到特定端口,因此我需要将套接字绑定到该地址。

struct sockaddr_storage local_addrs; //for local address

if (sc->domain == AF_INET) {
    (*(struct sockaddr_in*)&local_addrs).sin_family = AF_INET;
    (*(struct sockaddr_in*)&local_addrs).sin_addr.s_addr = inet_addr(INADDR_ANY);
    (*(struct sockaddr_in*)&local_addrs).sin_port = htons(tcp_port);
}
else{
    (*(struct sockaddr_in6*)&local_addrs).sin6_family = AF_INET6; 
    (*(struct sockaddr_in6*)&local_addrs).sin_addr.s_addr = inet_addr(IN6ADDR_ANY_INIT);
    (*(struct sockaddr_in6*)&local_addrs).sin6_port = htons(tcp_port);
}

local_addr_size = sizeof(local_addrs);
if (( ret = bind(sockfd, (struct sockaddr *)&local_addrs, local_addr_size)) < 0 ) {
    ....//error
}

如何初始化struct sockaddr_storage local_addrs?我需要为这个结构分配内存吗?

顺便说一句,下面的行还不行。仍在尝试找出如何将客户端 sokcet 绑定到任何可能的 IPv6 地址。

(*(struct sockaddr_in6*)&local_addrs).sin_addr.s_addr = inet_addr(IN6ADDR_ANY_INIT);

【问题讨论】:

  • 为什么要绑定 client 套接字?这对于 TCP 来说是非常不寻常的。
  • 正如我在问题中所说,我想将本地 TCP 端口绑定到特定端口,以进行测试。 :)
  • 这里的sc-&gt;domain 是什么?

标签: c linux sockets tcp


【解决方案1】:

您显示的代码基本上是正确的方法,因为您需要将sockaddr_storage 类型转换为您要填充的特定sockaddr_... 类型。

但是,在sockaddr_in6 的情况下,IN6ADDR_ANY_INIT 部分是错误的。改用这个:

(*(struct sockaddr_in6*)&local_addrs).sin6_addr = in6addr_any;

IN6ADDR_ANY_INIT 是一个只能在编译时用于静态声明的宏,例如:

struct sockaddr_in6 in6 = {AF_INET6, port, 0, IN6ADDR_ANY_INIT, 0};

struct in6_addr addr = IN6ADDR_ANY_INIT;

IN6ADDR_ANY_INIT不能用于运行时的赋值,例如:

struct sockaddr_in6 in6;
in6.sin6_addr = IN6ADDR_ANY_INIT; // ERROR

struct in6_addr addr;
addr = IN6ADDR_ANY_INIT; // ERROR

另一方面,in6addr_any 是一个全局变量,可以在运行时用于赋值。

不,您不需要malloc sockaddr_storage 结构。

话虽如此,我建议使用一些局部变量来使代码更易于阅读:

if (sc->domain == AF_INET) {
    struct sockaddr_in *in4 = (struct sockaddr_in*) &local_addrs;
    in4->sin_family = AF_INET;
    in4->sin_addr.s_addr = INADDR_ANY; // <-- inet_addr() is not needed for INADDR_ANY
    in4->sin_port = htons(tcp_port);
}
else {
    struct sockaddr_in6 *in6 = (struct sockaddr_in6*) &local_addrs;
    in6->sin6_family = AF_INET6;
    in6->sin6_addr = in6addr_any;
    in6->sin6_port = htons(tcp_port);
    /* note: sockaddr_in6 also has sin6_flowinfo and sin6_scope_id
       fields that you may have to fill, too...
    in6->sin6_flowinfo = ...;
    in6->sin6_scope_id = ...;
    */
}

或者,改用union

union sockaddr_types {
    struct sockaddr_storage storage;
    struct sockaddr addr; 
    struct sockaddr_in in4;
    struct sockaddr_in6 in6;
};

union sockaddr_types local_addrs;

if (sc->domain == AF_INET) {
    local_addrs.in4.sin_family = AF_INET;
    local_addrs.in4.sin_addr.s_addr = INADDR_ANY;
    local_addrs.in4.sin_port = htons(tcp_port);
}
else {
    local_addrs.in6.sin6_family = AF_INET6;
    local_addrs.in6.sin6_addr = in6addr_any;
    local_addrs.in6.sin6_port = htons(tcp_port);
    /*
    local_addrs.in6.sin6_flowinfo = ...;
    local_addrs.in6.sin6_scope_id = ...;
    */
}

if ((ret = bind(sockfd, &local_addrs.addr, sizeof(local_addrs))) < 0) {
    //error...
}

无论哪种方式,您还应该考虑将整个 sockaddr_storage 清零以在填充您需要的内容之前预填充未使用的字段:

memset(&local_addrs, 0, sizeof(local_addrs));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-12
    • 2012-02-01
    • 1970-01-01
    相关资源
    最近更新 更多