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

你可能感兴趣的:(C/S)