Linux socket 相關(guān)結(jié)構(gòu)體

// 英特網(wǎng)地址是由IP地址和端口號(hào)唯一定義
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)協(xié)議的套接字地址結(jié)構(gòu)體
struct sockaddr_in {
    sa_family_t    sin_family; // 地址族,必須為 AF_INET
    __be16         sin_port;   // 端口號(hào)(網(wǎng)絡(luò)字節(jié)序)
    struct in_addr sin_addr;   // IPv4 地址
    unsigned char  sin_zero[8];// 填充字節(jié),保持與 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 的區(qū)別, 其中 in 代表的含義是什么?

1. 區(qū)別

struct sockaddr

  • 作用:通用的套接字地址結(jié)構(gòu)體,作為所有協(xié)議族地址結(jié)構(gòu)體的“基類”。

  • 定義(見 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]; // 協(xié)議地址數(shù)據(jù)(內(nèi)容由具體協(xié)議解釋)
    };
    
  • 特點(diǎn)

    • 僅用于“通用”接口(如 bind、connect、accept 等),實(shí)際使用時(shí)通常需要強(qiáng)制類型轉(zhuǎn)換為具體協(xié)議的地址結(jié)構(gòu)體。
    • 不能直接表達(dá)具體協(xié)議的所有字段。

struct sockaddr_in

  • 作用:專門用于 IPv4(Internet Protocol version 4)協(xié)議的套接字地址結(jié)構(gòu)體。

  • 定義(見 include/uapi/linux/in.h):

    struct sockaddr_in {
        sa_family_t    sin_family; // 地址族,必須為 AF_INET
        __be16         sin_port;   // 端口號(hào)(網(wǎng)絡(luò)字節(jié)序)
        struct in_addr sin_addr;   // IPv4 地址
        unsigned char  sin_zero[8];// 填充字節(jié),保持與 struct sockaddr 大小一致
    };
    
  • 特點(diǎn)

    • 明確表達(dá)了 IPv4 的端口和地址字段。
    • 只能用于 IPv4 協(xié)議相關(guān)的 socket 操作。

2. in 的含義

  • inInternet 的縮寫,表示該結(jié)構(gòu)體用于 Internet 協(xié)議族(即 IPv4)。
  • 你還會(huì)看到類似的命名:
    • sockaddr_in6:用于 IPv6(Internet Protocol version 6)
    • sockaddr_un:用于 UNIX 域套接字(UNIX domain)

3. 總結(jié)

  • struct sockaddr 是所有協(xié)議族的通用地址結(jié)構(gòu)體,實(shí)際用時(shí)需強(qiáng)轉(zhuǎn)為具體協(xié)議的結(jié)構(gòu)體。
  • struct sockaddr_in 是專門為 IPv4 設(shè)計(jì)的,in 代表 Internet(即 IPv4)。
  • 你在寫 socket 程序時(shí),通常會(huì)用 struct sockaddr_in 填寫地址,然后通過 (struct sockaddr *) 強(qiáng)轉(zhuǎn)傳遞給 socket API。

如果寫一個(gè) tcp 服務(wù)器程序怎么兼容 ipv4 和 ipv6

這是網(wǎng)絡(luò)編程中非常常見的問題。要讓你的 TCP 服務(wù)器同時(shí)兼容 IPv4 和 IPv6,有兩種主流方法:


方法一:分別監(jiān)聽 IPv4 和 IPv6

  1. 創(chuàng)建兩個(gè) socket,一個(gè) AF_INET(IPv4),一個(gè) AF_INET6(IPv6)。
  2. 分別 bind() 到對(duì)應(yīng)的地址(如 INADDR_ANYin6addr_any),然后都 listen()。
  3. select()/poll()/epoll() 等等待兩個(gè) socket 的連接。

優(yōu)點(diǎn):最通用,所有平臺(tái)都支持。
缺點(diǎn):代碼略復(fù)雜,需要管理兩個(gè) socket。


方法二:只用 IPv6 socket,開啟雙棧(推薦)

  1. 只創(chuàng)建一個(gè) AF_INET6 socket。

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

  3. 關(guān)閉 IPV6_V6ONLY 選項(xiàng)(默認(rèn)通常是關(guān)閉的),這樣該 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 地址的形式)。

優(yōu)點(diǎn):代碼簡(jiǎn)單,只需管理一個(gè) socket。
缺點(diǎn):部分老系統(tǒng)或特殊配置下可能不支持雙棧。


代碼示例(方法二,推薦)

#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);
    }

    // 關(guān)閉 IPV6_V6ONLY,允許 IPv4-mapped IPv6 地址
    int off = 0;
    if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) < 0) {
        perror("setsockopt");
        // 不是致命錯(cuò)誤,可以繼續(xù)
    }

    struct sockaddr_in6 addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin6_family = AF_INET6;
    addr.sin6_addr = in6addr_any; // 監(jiān)聽所有 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;
}

關(guān)鍵點(diǎn)說明

  • AF_INET6 socket + in6addr_any + 關(guān)閉 IPV6_V6ONLY,即可同時(shí)接受 IPv4 和 IPv6。
  • accept() 返回的地址類型可能是 IPv4-mapped IPv6(::ffff:a.b.c.d),需要用 getnameinfo() 或手動(dòng)判斷。
  • 某些系統(tǒng)(如部分 BSD)默認(rèn) IPV6_V6ONLY 是開啟的,需要手動(dòng)關(guān)閉。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 網(wǎng)絡(luò)中進(jìn)程之間如何通信 為了方便大家獲取源代碼,可以移步這里,GitHub源代碼 進(jìn)程通信的概念最初來源于單機(jī)系統(tǒng)...
    batbattle閱讀 14,247評(píng)論 1 5
  • 字符串: strstr(* s,*p) 返回值如果是字符串則是第一次出現(xiàn) p的之后所有的字符串;若返回值為整形,則...
    Edingburgh閱讀 535評(píng)論 0 1
  • https://www.cnblogs.com/skynet/archive/2010/12/12/1903949...
    gykimo閱讀 670評(píng)論 0 0
  • Socket原理 1.什么是TCP/IP、UDP? 2.Socket在哪里呢? 3.Socket是什么呢? 4.你...
    Yong_bcf4閱讀 220評(píng)論 0 0
  • 簡(jiǎn)介 Socket理論 Socket工作流程 核心函數(shù)講解 服務(wù)的如何獲取客戶端的信息 字符串ip和網(wǎng)絡(luò)二進(jìn)制的轉(zhuǎn)...
    第八區(qū)閱讀 4,067評(píng)論 0 4

友情鏈接更多精彩內(nèi)容