APUE中的C/S程序

  服务器端的程序名为ruptimed,服务名为ruptime,为客户端提供uptime服务,由于ruptime服务是用户自定义的服务,在调用getaddrinfo时出现如下错误:

1 Servname not supported for ai_socktype

我给getaddrinfo函数提供了主机名和服务名,但需要在系统中登记,即将ruptime和端口号写入/etc/services中(注意用户自定义的端口号不能小于1024,以免和系统已经存在的端口号冲突。程序名和服务名不必相同,服务名更像是程序的别名),将

ruptime 4000/tcp

添加到/etc/services文件末尾,这样就不会出现上面的错误。

所以整个程序的执行过程是:将服务器进程初始化为一个守护进程,程序名为ruptimed,这个程序提供ruptime服务(即调用uptime函数),调用getaddrinfo时,进程去主机host查找“ruptime”在/etc/services对应的端口号,经过hint过滤addrinfo的链表中,选取一个地址,将其和一个套接字绑定,然后监听,以便服务器和客户端建立链接。客户端调用getaddrinfo函数查找服务器上的ruptime服务,建立链接。

注意:addinfo链表中的地址都是网络字节序,所以如果要在本地查看,需要调用ntohs/ntohl函数。

也可以将getaddrinfo函数的第二个参数写成端口号,这时就没有必要在/etc/services文件中登记了,但是hint的ai_flags标志位需要设定为AI_PASSIVE,表示服务器端的绑定监听。

在客户端,如果用户不知道端口号,可以用服务名代替,但是仍然需要在/etc/services文件中登记,不然依然会出现上面的错误,此时端口号不需要和服务器端登记的端口号保持一致(不知原因)。当客户端以端口号的方式连接服务器时,ai_flags不用设为AI_PASSIVE,一般为AI_CANONNAME。

相关代码如下:

客户端:

 1 #include <string.h>
 2 #include <unistd.h>
 3 #include <stdlib.h>
 4 #include <fcntl.h>
 5 #include <errno.h>
 6 #include <netdb.h>
 7 #include <sys/socket.h>
 8 
 9 #define MAXADDRLEN 256
10 #define BUFLEN 128
11 
12 extern int connect_retry(int, const struct sockaddr *, socklen_t);
13 
14 void
15 print_uptime(int sockfd)
16 {
17     int n;
18     char buf[BUFLEN];
19 
20     while((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
21         write(STDOUT_FILENO, buf, n);
22     if(n < 0)
23         printf("recv error");
24 }
25 
26 int
27 main(int argc, char *argv[])
28 {
29     struct addrinfo *ailist, *aip;
30     struct addrinfo hint;
31 
32     int sockfd, err;
33     
34     if(argc != 2) {
35         printf("usage: ruptime hostname");
36         exit(1);
37     }
38 
39     hint.ai_flags = 0;
40     hint.ai_family = 0;
41     hint.ai_socktype = SOCK_STREAM;
42     hint.ai_protocol = 0;
43     hint.ai_addrlen = 0;
44     hint.ai_canonname = NULL;
45     hint.ai_addr = NULL;
46     hint.ai_next = NULL;
47     
48     if((err = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0) {
49         printf("getaddrinfo error: %s\n", gai_strerror(err));
50         exit(1);
51     }
52 
53     for(aip = ailist; aip != NULL; aip = aip->ai_next)
54     {
55         if((sockfd = socket(aip->ai_family, SOCK_STREAM, 0)) < 0)
56             err = errno;
57         if(connect_retry(sockfd, aip->ai_addr, aip->ai_addrlen) < 0)
58             err = errno;
59         else {
60             print_uptime(sockfd);
61             exit(0);
62         }
63     }
64     fprintf(stderr, "can't connect to %s: %s\n", argv[1], strerror(err));
65     exit(1);
66 }

服务器端:

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <syslog.h>
 5 #include <sys/socket.h>
 6 #include <netdb.h>
 7 #include <errno.h>
 8 
 9 #define BUFLEN 128
10 #define QLEN 10
11 
12 #ifndef HOST_NAME_MAX
13 #define HOST_NAME_MAX 256
14 #endif
15 
16 extern int initserver(int, struct sockaddr *, socklen_t , int);
17 
18 void
19 serve(int sockfd)
20 {
21     int clfd;
22     FILE *fp;
23     char buf[BUFLEN];
24 
25     for(;;) {
26         clfd = accept(sockfd, NULL, NULL);
27         if(clfd < 0) {
28             syslog(LOG_ERR, "ruptime: accept error:%s",
29                     strerror(errno));
30             exit(1);
31         }
32         if((fp = popen("/usr/bin/uptime", "r")) == NULL) {
33             sprintf(buf, "error:%s\n", strerror(errno));
34             send(clfd, buf, strlen(buf), 0);
35         } else {
36             while(fgets(buf, BUFLEN, fp) != NULL)
37                 send(clfd, buf, strlen(buf), 0);
38             pclose(fp);
39         }
40         close(clfd);
41     }
42 }
43 
44 int
45 main(int argc, char *argv[])
46 {
47     struct addrinfo *ailist, *aip;
48     struct addrinfo hint;
49 
50     int sockfd, err, n;
51     char *host;
52 
53     if(argc != 1) {
54         printf("usage: ruptimed");
55         exit(1);
56     }
57 
58 #ifdef _SC_HOST_NAME_MAX
59     n = sysconf(_SC_HOST_NAME_MAX);
60     if(n < 0)
61 #endif
62 
63     n = HOST_NAME_MAX;
64     host = malloc(n);
65     if(host == NULL) {
66         perror("malloc error");
67         exit(1);
68     }
69 
70     if(gethostname(host, n) < 0) {
71         perror("gethostname error");
72         exit(1);
73     }
74
75     printf("in main pid: %d\n", getpid());
76 
77     daemonize("ruptimed");
78     hint.ai_flags = AI_CANONNAME;
79     hint.ai_family = 0;
80     hint.ai_socktype = SOCK_STREAM;
81     hint.ai_protocol = 0;
82     hint.ai_addrlen = 0;
83     hint.ai_canonname = NULL;
84     hint.ai_next = NULL;
85     
86     syslog(LOG_DEBUG, "ruptimed pid: %d\n", getpid());
87     if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0) {
88         syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err));
89         exit(1);
90     }
91     for(aip = ailist; aip != NULL; aip = aip->ai_next) {
92         if((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0) {
93             serve(sockfd);
94             exit(0);
95         }
96     }
97     exit(1);
98 }

服务器输出:

1  ./ruptimed 
2 in main pid: 2528
3 return from daemonize pid: 2530

可以看到调用daemonize函数前后,进程已经变化,不再是同一个进程,此时服务器进程成为一个守护进程。第二行由ruptimed程序输出,第三行由daemonize程序输出。

这里将服务器端初始化为一个守护进程,所以第77行daemonize函数之后的代码都不能标准输出到终端,只能调用syslog函数查看,输出如下:

1 Oct  2 12:06:43 elvis ruptimed: ruptimed pid: 2530

用到的makefile如下:

 1 CFLAGS=-I/usr/include -Wall -g
 2 LDFLAGS=-L/usr/local/lib
 3 all:client ruptimed
 4 .PHONY:all
 5 
 6 client:client.o connect_retry.o
 7     gcc client.o connect_retry.o -o client
 8 ruptimed:server.o daemonize.o initserver.o
 9     gcc server.o daemonize.o initserver.o -o ruptimed
10 clean:
11     rm -i *.o
posted @ 2012-10-03 00:38  leealways87  阅读(780)  评论(0编辑  收藏  举报