JavaScript is required to for searching.
跳过导航链接
退出打印视图
编程接口指南     Oracle Solaris 10 1/13 Information Library (简体中文)
search filter icon
search icon

文档信息

前言

1.  内存和 CPU 管理

2.  用于 Solaris Cluster 的远程共享内存 API

3.  会话描述协议 API

4.  进程调度程序

5.  地址组 API

6.  输入/输出接口

7.  进程间通信

8.  套接字接口

SunOS 4 二进制兼容性

套接字概述

套接字库

套接字类型

接口组

套接字基础知识

创建套接字

绑定本地名称

建立连接

连接错误

数据传输

关闭套接字

连接流套接字

输入/输出多路复用

数据报套接字

标准例程

主机和服务名称

主机名-hostent

网络名称-netent

协议名-protoent

服务名-servent

其他例程

客户机/服务器程序

套接字和服务器

套接字和客户机

无连接服务器

高级套接字主题

带外数据

非阻塞套接字

异步套接字 I/O

中断驱动套接字 I/O

信号和进程组 ID

选择特定的协议

地址绑定

套接字选项

inetd 守护进程

广播及确定网络配置

使用多播

发送 IPv4 多播数据报

接收 IPv4 多播数据报

发送 IPv6 多播数据报

接收 IPv6 多播数据报

流控制传输协议

SCTP 栈实现

SCTP 套接字接口

sctp_bindx()

sctp_opt_info()

sctp_recvmsg()

sctp_sendmsg()

sctp_send()

分叉关联

sctp_getpaddrs()

sctp_freepaddrs()

sctp_getladdrs()

sctp_freeladdrs()

SCTP 用法代码示例

9.  使用 XTI 和 TLI 编程

10.  包过滤钩子

11.  传输选择和名称到地址映射

12.  实时编程和管理

13.  Solaris ABI 和 ABI 工具

A.  UNIX 域套接字

索引

客户机/服务器程序

分布式应用程序的最常见形式为客户机/服务器模型。在此方案中,客户机进程从服务器进程请求服务。

备用方案是可以删除暂停服务器进程的服务服务器。Internet 服务守护进程 inetd(1M) 便是一个示例。inetd(1M) 可以侦听启动时通过读取配置文件而确定的不同端口。在 inetd(1M) 服务端口上请求连接时,inetd(1M) 会产生相应的服务器以便为客户机提供服务。客户机并不知道中间服务器已参与连接。inetd 守护进程中更详细地介绍了 inetd(1M)

套接字和服务器

大多数服务器都是通过已知 Internet 端口号或 UNIX 系列名称进行访问的。服务 rlogin 便是已知的 UNIX 系列名称。示例 8-7 中给出了远程登录服务器的主循环。

服务器将从其调用者的控制终端分离出来,除非服务器在 DEBUG 模式下运行。

(void) close(0);
(void) close(1);
(void) close(2);
(void) open("/", O_RDONLY);
(void) dup2(0, 1);
(void) dup2(0, 2);
setsid();

进行分离可防止服务器从控制终端的进程组接收信号。服务器从控制终端分离之后,便不能将错误报告发送到终端。分离的服务器必须使用 syslog(3C) 记录错误。

服务器通过调用 getaddrinfo(3SOCKET) 获取其服务定义。

bzero(&hints, sizeof (hints));
hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(NULL, "rlogin", &hints, &api);

api 中返回的结果包含程序侦听服务请求所用的 Internet 端口。/usr/include/netinet/in.h 中定义了某些标准端口号。

然后,服务器创建套接字并侦听服务请求。bind(3SOCKET) 例程可以确保服务器在预期位置进行侦听。由于远程登录服务器会侦听受限的端口号,因此该服务器将以超级用户身份运行。服务器的主体是以下循环。

示例 8-7 服务器主循环

/* Wait for a connection request. */
for (;;) {
    faddrlen = sizeof (faddr);
    new_sock = accept(sock, (struct sockaddr *)api->ai_addr,
            api->ai_addrlen)
    if (new_sock == -1) {
        if (errno != EINTR && errno != ECONNABORTED) {
            perror("rlogind: accept");
        }
        continue;
    }
    if (fork() == 0) {
        close (sock);
        doit (new_sock, &faddr);
    }
    close (new_sock);
}
/*NOTREACHED*/

accept(3SOCKET) 将阻止消息,直到客户机请求服务为止。此外,如果 accept 被某信号(如 SIGCHLD)中断,则 accept(3SOCKET) 会返回故障指示。如果发生错误,则会检查来自 accept(3SOCKET) 的返回值,并使用 syslog(3C) 记录错误。

然后,服务器派生一个子进程,并调用远程登录协议处理的主体。父进程用于对连接请求进行排队的套接字将在子进程中关闭。accept(3SOCKET) 所创建的套接字将在父进程中关闭。将客户机的地址传递到服务器应用程序的 doit() 例程,此例程用来验证客户机。

套接字和客户机

本节介绍客户机进程所执行的步骤。与在服务器中相同,第一步是查找远程登录的服务定义。

bzero(&hints, sizeof (hints));
hints.ai_flags = AI_ALL|AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;

error = getaddrinfo(hostname, servicename, &hints, &res);
if (error != 0) {
    (void) fprintf(stderr, "getaddrinfo: %s for host %s service %s\n",
        gai_strerror(error), hostname, servicename);
    return (-1);
}

getaddrinfo(3SOCKET) 返回 res 中的地址列表头。 通过创建套接字,并尝试连接列表中返回的每个地址直到一个地址有效,即可找到所需的地址。

for (aip = res; aip != NULL; aip = aip->ai_next) {
    /*
     * Open socket.  The address type depends on what
     * getaddrinfo() gave us.
     */
    sock = socket(aip->ai_family, aip->ai_socktype,
          aip->ai_protocol);
    if (sock == -1) {
        perror("socket");
        freeaddrinfo(res);
        return (-1);
    }

    /* Connect to the host. */
    if (connect(sock, aip->ai_addr, aip->ai_addrlen) == -1) {
        perror("connect");
        (void) close(sock);
        sock = -1;
        continue;
    }
    break;
}

套接字已经创建并已连接到所需的服务。connect(3SOCKET) 例程隐式绑定 sock,因为 sock 未绑定。

无连接服务器

某些服务使用数据报套接字。rwho(1) 服务提供了有关连接到局域网的主机的状态信息。应避免运行 in.rwhod(1M),因为 in.rwho 会导致网络通信流量过大。rwho 服务将信息广播到所有连接到特定网络的主机。rwho 服务是数据报套接字用法示例。

运行 rwho(1) 服务器的主机上的用户可以使用 ruptime(1) 获取其他主机的当前状态。以下示例给出了典型输出。

示例 8-8 ruptime(1) 程序的输出

itchy up 9:45, 5 users, load 1.15, 1.39, 1.31
scratchy up 2+12:04, 8 users, load 4.67, 5.13, 4.59
click up 10:10, 0 users, load 0.27, 0.15, 0.14
clack up 2+06:28, 9 users, load 1.04, 1.20, 1.65
ezekiel up 25+09:48, 0 users, load 1.49, 1.43, 1.41
dandy 5+00:05, 0 users, load 1.51, 1.54, 1.56
peninsula down 0:24
wood down 17:04
carpediem down 16:09
chances up 2+15:57, 3 users, load 1.52, 1.81, 1.86

在每台主机上,rwho(1) 服务器进程定期广播状态信息,并接收状态信息。此外,服务器还更新数据库。将对此数据库进行解释以了解每台主机的状态。仅通过本地网络及其广播功能连接的服务器将自主运行。

使用广播时效率非常低,因为广播会生成过多网络通信流量。除非广泛且频繁地使用该服务,否则所带来的简单性相对定期广播的开销而言,得不偿失。

以下示例给出了简化的 rwho(1) 服务器版本。样例代码接收网络中其他主机广播的状态信息,并提供运行此样例代码的主机的状态。第一项任务在程序的主循环中完成:检查在 rwho(1) 端口接收到的包,以确保这些包由其他 rwho(1) 服务器进程发送并标记有到达时间。然后,这些包使用主机的状态更新文件。如果长时间没有收到来自主机的消息,则数据库例程认为此主机已关闭并记录此信息。由于主机正常运行时服务器可能会关闭,因此该应用程序容易出现错误。

示例 8-9 rwho(1) 服务器

main()
{
    ...
    sp = getservbyname("who", "udp");
    net = getnetbyname("localnet");
    sin.sin6_addr = inet_makeaddr(net->n_net, in6addr_any);
    sin.sin6_port = sp->s_port;
    ...
    s = socket(AF_INET6, SOCK_DGRAM, 0);
    ...
    on = 1;
    if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof on)
            == -1) {
        syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
        exit(1);
    }
    bind(s, (struct sockaddr *) &sin, sizeof sin);
    ...
    signal(SIGALRM, onalrm);
    onalrm();
    while(1) {
        struct whod wd;
        int cc, whod, len = sizeof from;
        cc = recvfrom(s, (char *) &wd, sizeof(struct whod), 0,
            (struct sockaddr *) &from, &len);
        if (cc <= 0) {
            if (cc == -1 && errno != EINTR)
                syslog(LOG_ERR, "rwhod: recv: %m");
            continue;
        }
        if (from.sin6_port != sp->s_port) {
            syslog(LOG_ERR, "rwhod: %d: bad from port",
                ntohs(from.sin6_port));
            continue;
        }
        ...
        if (!verify( wd.wd_hostname)) {
            syslog(LOG_ERR, "rwhod: bad host name from %x",
                ntohl(from.sin6_addr.s6_addr));
            continue;
        }
        (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
        whod = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666);
        ...
        (void) time(&wd.wd_recvtime);
        (void) write(whod, (char *) &wd, cc);
        (void) close(whod);
    }
    exit(0);
}

第二项服务器任务是提供其主机状态。这要求定期获取系统状态信息,将其打包在消息中,并在本地网络上广播,以使其他 rwho(1) 服务器收到这些信息。此任务由计时器运行,并由信号触发。

状态信息将在本地网络上广播。对于不支持广播的网络,使用多播。