3.3. 数据结构
很好,终於讲到这里了,该是谈谈编程的时间了。在本节,我会介绍 socket 接口的各种数据型别,因为它们有些会不太好理解。
首先是最简单的:socket descriptor,型别如下:
就是一般的 int。
从这里开始会有点不好理解,所以不用问太多,直接读过就好。
我的第一个 StructTM -struct addrinfo,这个数据结构是最近的发明,用来准备之後要用的 socket 地址数据结构,也用在主机名(host name)及服务名(service name)的查询。当我们之後开始实际应用时,才会开始觉得比较靠谱,现在只需要知道你在建立连接调用时会用到这个数据结构。
你可以载入这个数据结构,然後调用 getaddrinfo()。它会返回一个指针,这个指针指向一个新的链表,这个链表有一些数据结构,而数据结构的内容记载了你所需的东西。
你可以在 ai_family 栏位中设定强制使用 IPv4 或 IPv6,或者将它设定为 AF_UNSPEC,AF_UNSPEC 很酷,因为这样你的程序就可以不用管 IP 的版本。
要注意的是,这是个链表:ai_next 是指向下一个成员(element),可能会有多个结果让你选择。我会直接用它提供的第一个结果,不过你可能会有不同的个人考量;先生!我不是万事通。
你会在 struct addrinfo 中看到 ai_addr 栏位是一个指向 struct sockaddr 的指针。这是我们开始要了解 IP 地址结构中有哪些细节的地方。有时候,你需要的是调用 getaddrinfo() 帮你填好 struct addrinfo。然而,你必须查看这些数据结构,并将值取出,所以我在这边会进行说明。
[还有,在发明 struct addrinfo 以前的代码都要手动填写这些数据的每个栏位,所以你会看到很多 IPv4 的代码真的用很原始的方式去做这件事。你知道的,本教程在旧版也是这样做]。
有些 structs 是 IPv4,而有些是 IPv6,有些两者都是。我会特别注明清楚它们属於哪一种。
总之,struct sockaddr 记录了很多 sockets 类型的 socket 的地址资料。
sa_family 可以是任何东西,不过在这份教程中我们会用到的是 AF_INET[IPv4]或 AF_INET6[IPv6]。sa_data 包含一个 socket 的目地地址与 port number。这样很不方便,因为你不会想要手动的将地址封装到 sa_data 里。
为了处理 struct sockaddr,程序设计师建立了对等平行的数据结构:struct sockaddr_in["in"是代表"internet"]用在 IPv4。
而这有个重点:指向 struct sockaddr_in 的指针可以转型(cast)为指向 struct sockaddr 的指针,反之亦然。所以即使 connect() 需要一个 struct sockaddr *,你也可以用 struct sockaddr_in,并在最後的时候对它做型别转换!
这个数据结构让它很容易可以参考(reference)socket 地址的成员。要注意的是 sin_zero[这是用来将数据结构补足符合 struct sockaddr 的长度],应该要使用 memset()函数将 sin_zero 整个清为零。还有,sin_family 是对应到 struct sockaddr 中的 sa_family,并应该设定为"AF_INET"。最後,sin_port 必须是 Network Byte Order[利用 htons()]。
让我们再更深入点!你可以在 sruct in_addr 里看到 sin_addr 栏位。
那是什麽?
好,别太激动,不过它是其中一个最恐怖的 union:
哇!好耶,它以前是 union,不过这个包袱现在似乎已经不见了。因此,若你已将 ina 宣告为 struct sockaddr_in 的型别时,那麽 ina.sin_addr.s_addr 会参考到 4-byte 的 IP address(以 Network Byte Order)。要注意的是,如果你的系统仍然在 struct in_addr 使用超恐怖的 union,你依然可以像我上面说的,精确地参考到 4-byte 的 IP address[这是因为 #define]。
那麽 IPv6 会怎样呢?
IPv6 也有提供类似的 struct,比如:
要注意到 IPv6 协议有一个 IPv6 address 与一个 port number,就像 IPv4 协议有一个 IPv4 address 与 port number 一样。
我现在还不会介绍 IPv6 的流量资料,或是 Scope ID 栏位 … 这只是一份入门教程嘛 :-)
最後要强调的一点,这个简单的 struct sockaddr_storage 是设计用来足够储存 IPv4 与 IPv6 structures 的 structure。[你看看,对於某些 calls,你有时无法事先知道它是否会使用 IPv4 或 IPv6 address 来填好你的 struct sockaddr。所以你用这个平行的 structure 来传递,它除了比较大以外,也很类似 struct sockaddr ,因而可以将它转型为你所需的型别]。
重点是你可以在 ss_family 栏位看到地址家族(address family),检查它是 AF_INET 或 AF_INET6(是 IPv4 或 IPv6)。之後如果你愿意的话,你就可以将它转型为 sockaddr_in 或 struct sockaddr_in6。
Last updated