美文网首页
Linux socket 相关结构体

Linux socket 相关结构体

作者: Linux系统编程 | 来源:发表于2025-07-05 10:12 被阅读0次
// 英特网地址是由IP地址和端口号唯一定义
struct sockaddr {
    sa_family_t sa_family;  /* address family, AF_xxx   */
    char        sa_data[14];    /* 14 bytes of protocol address */
};

// 专门用于 IPv4(Internet Protocol version 4)协议的套接字地址结构体
struct sockaddr_in {
    sa_family_t    sin_family; // 地址族,必须为 AF_INET
    __be16         sin_port;   // 端口号(网络字节序)
    struct in_addr sin_addr;   // IPv4 地址
    unsigned char  sin_zero[8];// 填充字节,保持与 struct sockaddr 大小一致
}; 

// 用于 UNIX 域套接字
struct sockaddr_un {
    __kernel_sa_family_t sun_family; /* AF_UNIX */
    char sun_path[UNIX_PATH_MAX];   /* pathname */
};


struct sockaddr_in6 {
    unsigned short int  sin6_family;    /* AF_INET6 */
    __be16          sin6_port;      /* Transport layer port # */
    __be32          sin6_flowinfo;  /* IPv6 flow information */
    struct in6_addr     sin6_addr;      /* IPv6 address */
    __u32           sin6_scope_id;  /* scope id (new in RFC2553) */
};


// include/uapi/linux/socket.h
typedef __kernel_sa_family_t sa_family_t;

typedef unsigned short __kernel_sa_family_t;

struct sockaddr_in 与 struct sockaddr 的区别, 其中 in 代表的含义是什么?

1. 区别

struct sockaddr

  • 作用:通用的套接字地址结构体,作为所有协议族地址结构体的“基类”。

  • 定义(见 include/linux/socket.hinclude/uapi/linux/socket.h):

    struct sockaddr {
        sa_family_t sa_family;   // 地址族(如 AF_INET、AF_INET6、AF_UNIX 等)
        char        sa_data[14]; // 协议地址数据(内容由具体协议解释)
    };
    
  • 特点

    • 仅用于“通用”接口(如 bind、connect、accept 等),实际使用时通常需要强制类型转换为具体协议的地址结构体。
    • 不能直接表达具体协议的所有字段。

struct sockaddr_in

  • 作用:专门用于 IPv4(Internet Protocol version 4)协议的套接字地址结构体。

  • 定义(见 include/uapi/linux/in.h):

    struct sockaddr_in {
        sa_family_t    sin_family; // 地址族,必须为 AF_INET
        __be16         sin_port;   // 端口号(网络字节序)
        struct in_addr sin_addr;   // IPv4 地址
        unsigned char  sin_zero[8];// 填充字节,保持与 struct sockaddr 大小一致
    };
    
  • 特点

    • 明确表达了 IPv4 的端口和地址字段。
    • 只能用于 IPv4 协议相关的 socket 操作。

2. in 的含义

  • inInternet 的缩写,表示该结构体用于 Internet 协议族(即 IPv4)。
  • 你还会看到类似的命名:
    • sockaddr_in6:用于 IPv6(Internet Protocol version 6)
    • sockaddr_un:用于 UNIX 域套接字(UNIX domain)

3. 总结

  • struct sockaddr 是所有协议族的通用地址结构体,实际用时需强转为具体协议的结构体。
  • struct sockaddr_in 是专门为 IPv4 设计的,in 代表 Internet(即 IPv4)。
  • 你在写 socket 程序时,通常会用 struct sockaddr_in 填写地址,然后通过 (struct sockaddr *) 强转传递给 socket API。

如果写一个 tcp 服务器程序怎么兼容 ipv4 和 ipv6

这是网络编程中非常常见的问题。要让你的 TCP 服务器同时兼容 IPv4 和 IPv6,有两种主流方法:


方法一:分别监听 IPv4 和 IPv6

  1. 创建两个 socket,一个 AF_INET(IPv4),一个 AF_INET6(IPv6)。
  2. 分别 bind() 到对应的地址(如 INADDR_ANYin6addr_any),然后都 listen()
  3. select()/poll()/epoll() 等等待两个 socket 的连接。

优点:最通用,所有平台都支持。
缺点:代码略复杂,需要管理两个 socket。


方法二:只用 IPv6 socket,开启双栈(推荐)

  1. 只创建一个 AF_INET6 socket

  2. bind()in6addr_any(即 ::,所有 IPv6 地址)。

  3. 关闭 IPV6_V6ONLY 选项(默认通常是关闭的),这样该 socket 既能接受 IPv6 也能接受 IPv4 的连接(IPv4-mapped IPv6 address)。

    int off = 0;
    setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
    
  4. 这样,既能接受 IPv6 客户端,也能接受 IPv4 客户端(以 IPv4-mapped IPv6 地址的形式)。

优点:代码简单,只需管理一个 socket。
缺点:部分老系统或特殊配置下可能不支持双栈。


代码示例(方法二,推荐)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main() {
    int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }

    // 关闭 IPV6_V6ONLY,允许 IPv4-mapped IPv6 地址
    int off = 0;
    if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) < 0) {
        perror("setsockopt");
        // 不是致命错误,可以继续
    }

    struct sockaddr_in6 addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin6_family = AF_INET6;
    addr.sin6_addr = in6addr_any; // 监听所有 IPv6 地址(包括 IPv4-mapped)
    addr.sin6_port = htons(12345);

    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    if (listen(sockfd, 10) < 0) {
        perror("listen");
        exit(1);
    }

    printf("Listening on port 12345 (IPv4 & IPv6)...\n");

    while (1) {
        struct sockaddr_storage client_addr;
        socklen_t client_len = sizeof(client_addr);
        int clientfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
        if (clientfd < 0) {
            perror("accept");
            continue;
        }

        char host[NI_MAXHOST], serv[NI_MAXSERV];
        getnameinfo((struct sockaddr *)&client_addr, client_len,
                    host, sizeof(host), serv, sizeof(serv),
                    NI_NUMERICHOST | NI_NUMERICSERV);
        printf("Connection from %s:%s\n", host, serv);

        // ... 处理客户端 ...
        close(clientfd);
    }

    close(sockfd);
    return 0;
}

关键点说明

  • AF_INET6 socket + in6addr_any + 关闭 IPV6_V6ONLY,即可同时接受 IPv4 和 IPv6。
  • accept() 返回的地址类型可能是 IPv4-mapped IPv6(::ffff:a.b.c.d),需要用 getnameinfo() 或手动判断。
  • 某些系统(如部分 BSD)默认 IPV6_V6ONLY 是开启的,需要手动关闭。

参考

相关文章

网友评论

      本文标题:Linux socket 相关结构体

      本文链接:https://www.haomeiwen.com/subject/vefagjtx.html