📙
Beej's Guide to Network Programming 简体中文版
  • 簡介
  • 联络译者
  • 译者鸣谢
  • 1. 前言
    • 1.1. 本书的读者
    • 1.2. 平台与编译器
    • 1.3. 官方网页与书本
    • 1.4. Solaris/SunOS 程序员要注意的事情
    • 1.5. Windows 程序员要注意的事情
    • 1.6. 来信原则
    • 1.7. 镜像站台(Mirroring)
    • 1.8. 译者该注意的
    • 1.9. 版权与出版(Copyright and Distribution)
  • 2. 何谓 Socket
    • 2.1. 两种 Internet Sockets
    • 2.2. 底层漫谈与网路理论
  • 3. IP address丶结构与数据转换
    • 3.1. IPv4 与 IPv6
      • 31.1. Sub network (子网)
      • 3.1.2. Port Number(连接埠号码)
    • 3.2. Byte Order(字节的顺序)
    • 3.3. 数据结构
    • 3.4. IP address,Part II
      • 3.4.1. Private Network
  • 4. 从 IPv4 移植为 IPv6
  • 5. System call 或 Bust
    • 5.1. getaddrinfo()-准备开始!
    • 5.2. socket()-取得 File Descriptor!
    • 5.3. bind()- 我在哪個 port?
    • 5.4. connect(),嘿!你好。
    • 5.5. listen()-有人会调用我吗?
    • 5.6. accept()-"谢谢你调用 port 3490"
    • 5.7. send() 与 recv()- 宝贝,跟我说说话!
    • 5.8. sendto() 与 recvfrom()- 用 DGRAM 风格跟我说说话
    • 5.9. close() 与 shutdown()-从我面前消失吧!
    • 5.10. getpeername()-你是谁?
    • 5.11. gethostname()-我是誰?
  • 06-Client-Server 基础
    • 6.1. 简易的 Stream Server
    • 6.2. 简易的 Stream Client
    • 6.3. Datagram Sockets
  • 07-高等技术
    • 7.1. Blocking(阻塞)
    • 7.2. select()-同步 I/O 多工
    • 7.3. 不完整传送的後续处理
    • 7.4. Serialization:如何封装数据
    • 7.5. 数据封装
    • 7.6. 广播数据包:Hello World!
  • 8. 常见的问题
  • 9. Man 使用手册
    • 9.1. accept()
    • 9.2. bind()
    • 9.3. connect()
    • close()
    • getaddrinfo(), freeaddrinfo(), gai_strerror()
    • gethostname()
    • gethostbyname(), gethostbyaddr()
    • getnameinfo()
    • getpeername()
    • errno
    • fcntl()
    • htons(), htonl(), ntohs(), ntohl()
    • inet_ntoa(), inet_aton(), inet_addr
    • inet_ntop(), inet_pton()
    • listen()
    • perror(), strerror()
    • poll()
    • recv(), recvfrom()
    • select()
    • setsockopt(), getsockopt()
    • send(), sendto()
    • shutdown()
    • socket()
    • struct sockaddr and pals
  • 10-参考文献
    • 10.1. 书籍
    • 10.2. 网站参考资料
    • 10.3. RFC
  • 11. 原着鸣谢
Powered by GitBook
On this page
  • 函式原型
  • 說明
  • 範例
  • 參考
Edit on GitHub
  1. 9. Man 使用手册

struct sockaddr and pals

處理 internet addresses 的資料結構

函式原型

#include <netinet/in.h>

// 全部指向 socket address structures 的指標,通常會在各種函式或 system calls 使用以前,
// 轉型為對相對應型別的指標:

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes 的 protocol address
};

// IPv4 AF_INET sockets:

struct sockaddr_in {
    short            sin_family;   // 例如:AF_INET, AF_INET6
    unsigned short   sin_port;     // 例如:htons(3490)
    struct in_addr   sin_addr;     // 參考下列的 struct in_addr
    char             sin_zero[8];  // 若你想要的話,將這個設定為零
};

struct in_addr {
    unsigned long s_addr;          // 用 inet_pton() 載入
};


// IPv6 AF_INET6 sockets:

struct sockaddr_in6 {
    u_int16_t       sin6_family;   // address family, AF_INET6
    u_int16_t       sin6_port;     // port number, Network Byte Order
    u_int32_t       sin6_flowinfo; // IPv6 flow 資訊
    struct in6_addr sin6_addr;     // IPv6 address
    u_int32_t       sin6_scope_id; // Scope ID
};

struct in6_addr {
    unsigned char   s6_addr[16];   // 用 inet_pton() 載入
};


// 承載 socket address 的 structure,要足以承載
// struct sockaddr_in 或 struct sockaddr_in6 data:

struct sockaddr_storage {
    sa_family_t  ss_family;     // address family

    // 這個全部都是填充的內容,依據實作而定,請忽略它:
    char      __ss_pad1[_SS_PAD1SIZE];
    int64_t   __ss_align;
    char      __ss_pad2[_SS_PAD2SIZE];
};

說明

對於 internet address 全部的 syscalls 及函式都有基本的資料結構,通常你會用 getaddinfo() 填好這些資料結構,然後當你需要時,就可以讀取它們。

印象中,struct sockaddr_in 與 struct sockaddr_in6 會共用相同的 struct sockaddr 起始資料結構,而你能自由地將一種型別轉型為另一種型別,除非你到了宇宙的盡頭,不然是不會有任何問題的。

宇宙的盡頭這件事只是開個玩笑 ... 若當你將 struct sockaddr_in 轉型為 struct sockaddr 時已經到了宇宙的盡頭,那我保證這必定純屬巧合,所以你根本不用擔心這件事啦。

恩,還要記得一點,如果一個函式允許你代入一個 struct sockaddr 參數時,你就可以放心地將你的 struct sockaddr_in、struct sockaddr_in6 或 struct sockadd_storage 轉型成這個型別。

struct sockaddr_in 是 IPv4 addresses(例如:"192.0.2.10")在用的資料結構,它承載了一個 address family (AF_INET):在 sin_port 有一個 port,而在 sin_addr 有一個 IPv4 address in sin_addr。

還有 struct sockadd_in 中的 sin_zero 欄位,有些人認為這個一定要設定成零,但有些人認為不用設定任何值(Linux 文件一點也沒有提過這件事),而且設定成零似乎沒有實際的用途。不過如果你喜歡,還是可以用 memset() 將它設定為零。

目前 struct in_addr 在不同的系統上是個怪東西,有時候它是瘋狂的大總匯(crazy union),有各種 #define 與一堆鬼東西。不過你該做的只是用這個 structure 的 s_addr 欄位,因為多數的系統只會實作這個欄位。

struct sockadd_in6 與 struct in6_addr 不僅都適用於 IPv6,而且都非常相似。

當你試著寫與 IP 版本無關的程式時,struct sockaddr_storage 是你可以傳遞給 accept() 或 recvfrom() 的資料結構,而且你不需知道新的 address 是走 IPv4 或 IPv6 協定。資料結構 struct sockaddr_storage 不像原本小小的 struct sockaddr,而是大到足以承載兩種型別。

範例

// IPv4:

struct sockaddr_in ip4addr;
int s;

ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(3490);
inet_pton(AF_INET, "10.0.0.1", &ip4addr.sin_addr);

s = socket(PF_INET, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&ip4addr, sizeof ip4addr);
// IPv6:

struct sockaddr_in6 ip6addr;
int s;

ip6addr.sin6_family = AF_INET6;
ip6addr.sin6_port = htons(4950);
inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &ip6addr.sin6_addr);

s = socket(PF_INET6, SOCK_STREAM, 0);
bind(s, (struct sockaddr*)&ip6addr, sizeof ip6addr);

參考

accept(), bind(), connect(), inet_aton(), inet_ntoa()

Previoussocket()Next10-参考文献

Last updated 2 years ago