1. 创建一个数据报插口
1 9 - 2 0 s o c k e t
函数创建了一个UDP 插口,并且给进程返回一个保存在变量
s o c k f d
中的描
述符。差错处理函数
e r r _ s y s
在[Stevens 1992]的附录 B . 2中给出。它接收任意数量的参数,
并用
v s p r i n t f
对它们格式化,将系统调用产生的
e r r n o
值对应的U n i x 错误信息打印出来,
并中断进程。
我们在不同的地方使用术语插口: ( 1 )为4 . 2 B S D 开发的程序用来访问网络协议的
A P I 通常叫插口A P I或者就叫插口接口; (2) socket是插口A P I 中的一个函数的名字;
( 3 )我们把调用s o c k e t创建的端点叫做一个插口,如评注“创建一个数据报插口”。
但是这里还有一些地方也使用术语插口: (4) socket函数的返回值叫一个插口描
述符或者就叫一个插口; ( 5 ) 在内核中的伯克利联网协议实现叫插口实现,相比较其
他系统如:系统V的流实现。( 6 )一个I P 地址和一个端口号的组合叫一个插口, I P 地址
和端口号对叫一个插口对。所幸的是引用哪一种术语是很明显的。
2. 将服务器地址放到结构
s o c k a d d r _ i n
中
2 1 - 2 4
在一个互联网插口地址结构中存放日期 /时间服务器的I P地址( 1 4 0 . 2 5 2 . 1 . 3 2 ) 和端口号
( 1 3 ) 。大多数T C P / I P 实现都提供标准的日期 /时间服务器,它的端口号为 13 [Stevens 1994,图
1- 9 ]。我们对服务器主机的选择是随意的
—
直接选择了提供此服务的本地主机 (图1- 1 7 ) 。
函数i n e t _ a d d r 将一个点分十进制表示的 I P地址的A S C I I字符串转换成网络字节序的 3 2
b i t 二进制整数。( I n t e r n e t 协议族的网络字节序是高字节在后 )。函数h t o n s把一个主机字节序
的短整数(可能是低字节在后)转换成网络字节序(高字节在后)。在S p a r c 这种系统中,整数是高
字节在后的格式, h t o n s典型地是一个什么也不做的宏。但是在低字节在后的 8 0 3 8 6 上的
B S D / 3 8 6 系统中, h t o n s可能是一个宏或者是一个函数,来完成一个 16 bit整数中的两个字节
的交换。
3. 发送数据报给服务器
2 5 - 2 7 程序调用s e n d t o 发送一个1 5 0 字节的数据报给服务器。因为是运行时栈中分配的未初
始化数组,1 5 0字节的缓存内容是不确定的。但没有关系,因为服务器根本就不看它收到的报
文的内容。当服务器收到一个报文时,就发送一个应答给客户端。应答中包含服务器以可读
格式表示的当前时间和日期。
我们选择的1 5 0 字节的客户数据报是随意的。我们有意选择一个报文长度在 1 0 0 ~ 2 0 8 之间
的值,来说明在本章的后面要提到的 m b u f 链表的使用。为了避免拥塞,在以太网中,我们希
望长度要小于1 4 7 2 。
4. 读取从服务器返回的数据报
2 8 - 3 2 程序通过调用r e c v f r o m 来读取从服务器发回的数据报。 U n i x 服务器典型地发回一
个如下格式的2 6 字节字符串
Sat Dec 11 11:28:05 1993\r\n
\ r
是一个A S C I I 回车符,\ n 是A S C I I 换行符。我们的程序将回车符替换成一个空字节,然后
调用p r i n t f输出结果。
在本章和下一章我们在分析函数 s o c k e t 、s e n d t o 和r e c v f r o m 的实现时,要进入这个
例子的一些细节部分。
第1章 概 述计计5