第三章基本套接字编程
概述
本章开始讲解套接字API。我们从套接字地址结构开始讲解,本书中几乎每个例子都用到它们。这些结构可以在两个方向上传递:从进程到内核和从内核到进程。
地址转换函数在地址的文本表达和它们存放在套接字地址结构中的二进制值之间进行转换。多数现存的IPv4代码使用inet_addr和inet_ntoa这两个函数,不过两个新函数inet_pton和inet_ntop同时适用于IPv4和IPv6两种代码。
这些地址转换函数存在的一个问题是它们与所转换的地址类型协议相关,要考虑究竟是IPv4地址还是IPv6地址。为克服这个问题,我们开发了一组名字以sock_开头的函数,它们以协议无关方式使用套接字地址结构。
套接字地址结构
大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。每个协议族都定义它自己的套接字地址结构。这些结构的名字均以sockaddr_开头,并以对应每个协议族的唯一后缀结尾。
IPv4套接字地址结构
IPv4套接字地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在<netinet/in.h>头文件中。图3-1给出了它的POSIX定义。
![](https://i-blog.csdnimg.cn/blog_migrate/920471a2239e6c5ff720b85eca9ef99d.png)
利用图3-1所示的例子,我们对套接字地址结构做几点一般性的说明。
长度字段sin_len是为增加对OSI协议的支持而随4.3BSD-Reno添加的(见图1-15)。在此之前,第一个成员是sin_family,它是一个无符号短整数(unsigned short)。并不是所有的厂家都支持套接字地址结构的长度字段,而且POSIX规范也不要求有这个成员。该成员的数据类型uint8_t是典型的,符合POSIX的系统都提供这种形式的数据类型(见图3-2)。
正是因为有了长度字段,才简化了长度可变套接字地址结构的处理。
即使有长度字段,我们也无须设置和检查它,除非涉及路由套接字(见第18章)。它是由处理来自不同协议族的套接字地址结构的例程(例如路由表处理代码)在内核中使用的。
POSIX规范只需要这个结构中的3个字段:sin_family、sin_addr和sin_port。对于符合POSIX的实现来说,定义额外的结构字段是可以接受的,这对于网际套接字地址结构来说也是正常的。几乎所有的实现都增加了sin_zero字段,所以所有的套接字地址结构大小都至少是16字节。
我们给出了字段s_addr、sin__family和sin_port的POSIX数据类型。in_addr_t数据类型必须是一个至少32位的无符号整数类型,in_port_t必须是一个至少16位的无符号整数类型,而sa_family_t可以是任何无符号整数类型。在支持长度字段的实现中,sa_family_t通常是一个8位的无符号整数,而在不支持长度字段的实现中,它则是一个16位的无符号整数。图3-2列出了POSIX定义的这些数据类型以及后面将会遇到的其他POSIX数据类型。
![](https://i-blog.csdnimg.cn/blog_migrate/fdd656201e0e82229e71ae4f6cf4db0a.png)
我们还将遇到数据类型u_char、u_short、u_int和u_long,它们都是无符号的。POSIX规范定义这些类型时特地标记它们已过时,仅是为向后兼容才提供的。
IPv4地址和TCP或UDP端口号在套接字地址结构中总是以网络字节序来存储。
32位IPv4地址存在两种不同的访问方法。举例来说,如果serv定义为某个网际套接字地址结构,那么serv.sin_addr将按in_addr结构引用其中的32位IPv4地址,而serv.sin_addr.s_addr将按in_addr_t(通常是一个无符号的32位整数)引用同-个32位IPv4地址。因此,我们必须正确地使用IPv4地址,尤其是在将它作为函数的参数时,因为编译器对传递结构和传递整数的处理是完全不同的。
sin_zero字段未曾使用,不过在填写这种套接字地址结构时,我们总是把该字段置为0。按照惯例,我们总是在填写前把整个结构置为o,而不是单单把sin_zero字段置为0。
套接字地址结构仅在给定主机上使用:虽然结构中的某些字段(例如IP地址和端口号)用在不同主机之间的通信中,但是结构本身并不在主机之间传递。
通用套接字地址结构P72