7.6 辅助函数

  FreeBSD C 语言库包含了许多套接字编程的辅助函数。 例如,在样例客户端中,我们硬性指定了 time.nist.govIP地址。但是我们并非总是知道 IP地址。甚至即使我们知道, 允许用户输入IP地址甚至域名 将使用我们的软件更有弹性。

7.6.1 gethostbyname

  域名是不能直接传送给任何套接字函数的, FreeBSD C 语言库携带了函数 gethostbyname(3)gethostbyname2(3), 声明在netdb.h中。

struct hostent * gethostbyname(const char *name);
struct hostent * gethostbyname2(const char *name, int af);

  这两个函数都返回hostent结构指针, 内含有关域的许多信息。对于我们的情况,结构体中的域 h_addr_list[0]指向长度 h_length字节的地址, 也按网络字节顺序存储。

  这允许我们建立一个要有弹性得多的──也要有用得多的 ──版本的daytime程序:

/*
 * daytime.c
 *
 * G. Adam Stanislav 编程
 * 2001年6月19日
 */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
  register int s;
  register int bytes;
  struct sockaddr_in sa;
  struct hostent *he;
  char buf[BUFSIZ+1];
  char *host;

  if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    return 1;
  }

  bzero(&sa, sizeof sa);

  sa.sin_family = AF_INET;
  sa.sin_port = htons(13);

  host = (argc > 1) ? (char *)argv[1] : "time.nist.gov";

  if ((he = gethostbyname(host)) == NULL) {
    herror(host);
    return 2;
  }

  bcopy(he->h_addr_list[0],&sa.sin_addr, he->h_length);

  if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {
    perror("connect");
    return 3;
  }

  while ((bytes = read(s, buf, BUFSIZ)) > 0)
    write(1, buf, bytes);

  close(s);
  return 0;
}

  现在我们可以在命令行打一个域名(或者一个IP地址, 两种方式都可以),程序将尝试连接daytime服务器。 否则,将仍然缺省为time.nist.gov。然后, 即使在使用缺省值的情形中我们将使用gethostbyname 而不是硬性指定192.43.244.18。这样,即使将来 IP地址变更,我们也能找到。

  由于从我们的本地计算机获取时间几乎不需要时间, 你可以一并运行daytime两次: 第一次从time.nist.gov取得时间, 第二次从你自己的系统取得时间。然后你可以比较结果, 看看你的系统时钟到底怎么样:

% daytime ; daytime localhost


52080 01-06-20 04:02:33 50 0 0 390.2 UTC(NIST) * 
2001-06-20T04:02:35Z
%

  正如你看见的,我的系统比NIST时间快两秒钟。

7.6.2 getservbyname

  有时你不能确定某种服务该用什么端口。 函数getservbyname(3),也声明在 netdb.h中,此时就很上手:

struct servent * getservbyname(const char *name, const char *proto);

  结构体servent包含 s_port,这是正确的端口号, 已经按照网络字节顺序存储。

  假如我们不知道daytime服务的正确端口, 我们可以这样找到:

  struct servent *se;
  ...
  if ((se = getservbyname("daytime", "tcp")) == NULL {
    fprintf(stderr, "Cannot determine which port to use.\n");
    return 7;
  }
  sa.sin_port = se->s_port;

  你通常知道端口。但是如何你正开发一个新协议, 你可能正在一个非正式端口上测试。 有一天,你要注册那个协议和端口 (如果不在别处,至少要在你的 /etc/services里,那是 getservbyname查找的地方)。 上面的代码就不再会返回错误,你就可以使用临时端口号。 一旦你已经将协议列入/etc/services, 你的软件不必重写代码也可以找到端口。

本文档和其它文档可从这里下载:ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读文档,如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.