本篇只记录CSAPP网络编程章节中的函数所属的头文件以及参数,不会有任何额外的知识,因为经过了之前写”信号”笔记的毒打,我深刻的意识到了整本书都是需要记的知识点这个惨痛的事实。另外,本篇的内容与我的博文: Unix网络编程篇有所重复,但我依然会记载Unix网络编程篇出现过并且在CSAPP也出现过的函数与结构,所以无需担心我因偷懒而导致内容不全。
struct in_addr
1 |
|
网络/主机 字节顺序转换
1 |
|
IP地址和点分十进制串转换
1 |
|
struct sockaddr_in
1 |
|
注意,_in后缀是互联网络(internet)的缩写,而不是输入(input)的缩写。
另外,结构体sockaddr_in中的成员sin_zero是用来填充结构体sockaddr_in来使其与结构体sockaddr保持相同大小的。sockaddr_in的大小为2 bytes + 2 bytes + 4 bytes + 8 bytes = 16 bytes,sockaddr的大小为2 bytes + 14 bytes = 16 bytes。
socket()
客户端和服务器使用socket函数来创建一个套接字描述符(socket descriptor)。
1 |
|
connect()
客户端通过调用connect函数来建立和服务器的连接。
1 |
|
bind()
bind函数告诉内核将addr中的服务器套接字地址和套接字描述符sockfd联系起来。参数addrlen就是sizeof(sockaddr_in)。
1 |
|
listen()
服务器调用listen函数告诉内核,描述符是被服务器而不是客户端使用的。
1 |
|
listen函数将sockfd从一个主动套接字转化为一个监听套接字(listening socket),该套接字可以接受来自客户端的连接请求,backlog参数暗示了内核在开始拒绝连接请求之前,队列中要排队的未完成的连接请求的数量。
accept()
服务器通过调用accept函数来等待来自客户端的连接请求。
1 |
|
主机和服务的转换
getaddrinfo()
Linux提供了一些强大的函数(称为getaddrinfo和getnameinfo)实现二进制套接字地址结构和主机名,主机地址,服务名和端口号的字符串表示之间的相互转化。
1 |
|
给定host和service(套接字地址的两个组成部分),getaddrinfo返回result,result一个指向addrinfo结构的链表,其中每个结构指向一个对应于host和service的套接字地址结构。
getaddrinfo的host参数可以是域名,也可以是数字地址(如点分十进制IP地址)。service参数可以是服务名(如http),也可以是十进制端口号。如果不想把主机名转换成地址,可以把host参数设置为NULL.对service来说也是一样。但是必须指定两者中至少一个。
可选的参数hints是一个getaddrinfo结构,它提供对getaddrinfo返回的套接字地址列表的更好的控制。如果要传递hints参数,只能设置下列字段: ai_family,ai_socktype,ai_protocol和ai_flags字段。其他字段必须设置为0(或NULL)。
- getaddrinfo默认可以返回IPv4和IPv6套接字地址。ai_family设置为AF_INET会将列表限制为IPv4地址;设置为AF_INET6则限制为IPv6地址。
- 对于host关联的每个地址,getaddrinfo函数默认最多返回三个addrinfo结构,每个的ai_socktype字段不同: 一个是连接,一个是数据报,一个是原始套接字。ai_socktype设置为SOCK_STREAM将列表限制为对每个地址最多一个addrinfo结构,该结构的套接字地址可以作为连接的一个端点。
- ai_flags字段是一个位掩码,可以进一步修改默认行为。可以把各种值用OR组合起来得到该掩码。下面是一些有用的值:
- AI_ADDRCONFIG。如果在使用连接,就推荐这个标志。它要求只有当本地主机被配置为IPv4时,getaddrinfo返回IPv4地址。对IPv6也是类似。
- AI_CANONNAME。ai_canonname字段默认为NULL。如果设置了该标志,就是告诉getaddrinfo将列表中第一个addrinfo结构的ai_canonname字段指向host的权威(官方)名字。
- AI_NUMERICSERV。参数service默认可以是服务名或端口号。这个标志强制参数service为端口号。
- AI_PASSIVE。getaddrinfo默认返回套接字地址,客户端可以在调用connect时用作主动套接字。这个标志告诉该函数,返回的套接字地址可能被服务器用作监听套接字。在这种情况下,参数host应该为NULL。得到的套接字地址字段会是通配符地址(wildcard address),告诉内核这个服务器会接受发送到该主机所有IP地址的请求。
1
2
3
4
5
6
7
8
9
10
11// getaddrinfo使用的addrinfo结构
struct addrinfo{
int ai_flags; // Hints argument flags
int ai_family; // First arg to socket function
int ai_socktype; // Second arg to socket function
int ai_protocol; // Third arg to socket function
char *ai_canonname; // Canonical hostname
size_t ai_addrlen; // Size of ai_addr struct
struct sockaddr *ai_addr; // Ptr to socket address structure
struct addrinfo *ai_next; // Ptr to next item in linked list
}
当getaddrinfo创建输出列表中的addrinfo结构时,会填写每个字段,除了ai_flags。ai_addr字段指向一个套接字地址结构,ai_addrlen字段给出这个套接字地址结构的大小,而ai_next字段指向列表中下一个addrinfo结构。其他字段描述这个套接字地址的各种属性。
getaddrinfo一个很好的方面是addrinfo结构中的字段是不透明的,即它们可以直接传递给套接字接口中的函数,应用程序代码无需再做任何处理。这个强大的属性使得我们编写的客户端和服务器能够独立于某个特殊版本的IP协议。
getnameinfo()
getnameinfo函数和getaddrinfo是相反的,将一个套接字地址结构转换成相应的主机和服务名字符串。它是已弃用的gethostbyaddr和getservbyport函数的新的替代品,和以前的那些函数不同,它是可重入和与协议无关的。
1 |
|
参数sa指向大小为salen字节的套接字地址结构,host指向大小为hostlen字节的缓冲区,service指向大小为servlen字节的缓冲区。getnameinfo函数将套接字地址结构sa转换成对应的主机和服务名字符串,并将它们复制到host和service缓冲区。如果getnameinfo返回非零的错误代码,应用程序可以调用gai_strerror把它转化成字符串。
如果不想要主机名,可以把host设置为NULL,hostlen设置为0,对服务字段来说也是一样。不过,两者必须设置其中之一。
参数flags是一个位掩码,能够修改默认的行为。可以把各种值用OR组合起来得到该掩码。下面是两个有用的值。
- NI_NUMERICHOST。getnameinfo默认试图返回host中的域名。设置该标志会使该函数返回一个数字地址字符串。
- NI_NUMERICSERV。getnameinfo默认会检查/etc/services,如果可能,会返回服务名而不是端口号。设置该标志会使该函数跳过查找,简单地返回端口号。
getaddrinfo()和getnameinfo()示例
1 |
|