摘要:socket api是网络编程的经典选择,可能在许多时候也是不二选择,本文介绍socket套接口中存在的一个知识点:套接口地址结构。socket编程中常存在一个问题,就是填写一个地址结构,然后绑定相应地址,所以对地址结构的理解是基本的要求。
在我第一次写网络程序的时候,或者说我第一次使用socket的时候,深深地感觉到socket中的地址填写有点难写或者有点难受,这个难受在于第一次接触socket本身的生疏,也在于socket地址看上去有点复杂和乱。出于这个原因,Stevens在他们的《UNIX网络编程》中拿一章来说明这个地址结构。
socket接口中地址结构有很多,这是一个基本的事实,但对于多数程序来说可能只会用到其中的少数。这些地址结构存在两类完全不同目的的结构,一类是实际中使用的结构,一类却是只是为参数传递的结构。前者与具体协议有关,不同的地址结构之间的差异很大;而后者就只有两个结构,称为通过结构。
地址结构的内容
我在这里不准备具体介绍哪一个地址结构,而是就地址结构的总体上来说明一下。在地址结构上倾向于为它自身指定一个长度,也就是地址结构往往会有一个长度成员,它指出当前正在使用结构体占有多少字节,但是由于历史原因并不能保证每个实现中都存在这个成员,所以看上去有的这个结构成员可能没有用处──在用户层上这个成员总是很少使用。并且socket api中问题假设用户没有指定这个成员,从而要求把这个长度的值显式地通过参数传递给这些api,如果connect、bind等等。这一点让人难受,有而不用。
在一些地址结构中存在一个未用的扩展区,也就是这个地方现在没有使用,但是在以后的某个时候可能会使用。这样就给人一种感觉,如果有一天这个地方使用了,那么我现在写的程序怎么办,总叫人不放心。可是socket已经产生了几十年,这个预留的东西从来没有使用过,所以历史告诉我们这些东西将来也不会使用。但是出于安全等等各种因素我们需要把这块内存处理为0。
在地址结构存在一个这样的成员它指定相应的地址簇或者说协议簇,它会非常有用,内核会根据这个值来对传入的内容作不同的处理。我们可能已经注意到了,诸如connect、bind这些接口并没有与具体地址结构相关的,所以但是内核必须在某个时候来区分这些地址结构,无论内核在哪里区分它们,区分他们总要一个依据,这个成员就是依据。
地址结构的其它成员就与具体的协议有关了,它们都协议相关的地址结构,各个协议之间可能存在巨大差异,所以我们在这里就不说了。
地址结构的使用
地址结构各有不同,所以用户在使用地址结构的时候必须要知道自己需要使用什么,内核不会为我们做决定,做决定是在用户层上进行的。这就是说,用户需要在使用不同的协议的时候选择一个合适的地址结构并正确的填写其中的成员,接着把填写好的结构交内核,由内核作进一步处理。
由于历史原因,我们可能没有办法知道或者确切了解一个结构成员,所以我们不能一个成员一个成员地来处理,在这里我们使用一个原则:只设置我们感兴趣的成员,把不感兴趣的成员都设置为0。如此就有了一个基本的思路:把整个结构置0,然后把设置我们感兴趣的成员为合适的值。
此外地址结构中大多数成员都使用是网络字节序列,所以用户在设置成员值的时候可能需要把机器字节序列调整为网络字节序列,socket给我们两个工具了(htonl一簇函数),但在网络应用中,可能还需要更强大的工具,此时需要我们手工调整字节序列。
socket套接口地址结构本身可能很复杂,再加上历史原因又加上一些人为的复杂性,所以学习与使用的时候需要小心一些。不过相对socket其它复杂的地方,地址结构可能还是相对简单的。