Below is the code for the server side. Code for client side is in Figure 16.14 in APUE2e, Page 568.
To compile the program, check this post: posix thread相关函数的编译(undefined reference to `pthread_create’)
/**
* apue-chap16: exercise16-3A.c
*
* Description: single thread, multiple endpoint to provide "ruptime" service
*
* Created On: Jan 11, 2012
*
* @author: Huang Zhu
*
* @email: zhuhuang.zp@gmail.com
*/
#include <apueerr.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/resource.h>
#include <sys/select.h>
#define BUFLEN 128
#define QLEN 10
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif
typedef struct Server_FD
{
int fd;
struct addrinfo addr;
} Server_FD;
int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
{
int sockfd, err;
int reuse = 1;
if((sockfd = socket(addr->sa_family, type, 0)) < 0)
return(-1);
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0){
err = errno;
goto errout;
}
if(bind(sockfd, addr, alen) < 0){
err = errno;
goto errout;
}
if(type == SOCK_STREAM || type == SOCK_SEQPACKET){
if(listen(sockfd, qlen) < 0){
err = errno;
goto errout;
}
}
return(sockfd);
errout:
close(sockfd);
errno = err;
return(-1);
}
void daemonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
/*
* Clear file creation mask.
*/
umask(0);
/*
* Get maximum number of file descriptors.
*/
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
err_quit("%s: can't get file limit", cmd);
/*
* Become a session leader to lose controlling TTY.
*/
if ((pid = fork()) < 0)
err_quit("%s: can't fork", cmd);
else if (pid != 0) /* parent */
exit(0);
setsid();
/*
* Ensure future opens won't allocate controlling TTYs.
*/
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGHUP, &sa, NULL) < 0)
err_quit("%s: can't ignore SIGHUP");
if ((pid = fork()) < 0)
err_quit("%s: can't fork", cmd);
else if (pid != 0) /* parent */
exit(0);
/*
* Change the current working directory to the root so
* we won't prevent file systems from being unmounted.
*/
if (chdir("/") < 0)
err_quit("%s: can't change directory to /");
/*
* Close all open file descriptors.
*/
if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = 1024;
for (i = 0; i < rl.rlim_max; i++)
close(i);
/*
* Attach file descriptors 0, 1, and 2 to /dev/null.
*/
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
/*
* Initialize the log file.
*/
openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
fd0, fd1, fd2);
exit(1);
}
}
void serve(fd_set *set, int maxfd, Server_FD *fdarray, int maxindex)
{
int clientfd;
int serverfd;
int ready;
FILE *fp;
char buf[BUFLEN];
int i;
for(;;){
//A socket with pending connect requests will appear to be readable. Use only read sets for select.
if((ready = select(maxfd+1, set, NULL, NULL, NULL)) > 0){
for(i = 0; i < maxindex; i++)
{
serverfd = fdarray[i].fd;
if(FD_ISSET(serverfd, set)){
clientfd = accept(serverfd, NULL, NULL); //we don't care about client's identity
if(clientfd < 0){
syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno));
exit(1);
}
//popen, pclose: apue2e, page 503
if((fp = popen("/usr/bin/uptime", "r")) == NULL){
sprintf(buf, "error: %s\n", strerror(errno));
send(clientfd, buf, strlen(buf), 0);
}else{
while(fgets(buf, BUFLEN, fp) != NULL)
send(clientfd, buf, strlen(buf), 0);
pclose(fp);
}
close(clientfd);
}
}
}
}
}
int main(int argc, char *argv[])
{
struct addrinfo *ailist, *aip;
struct addrinfo hint;
int sockfd, err, n;
char *host;
fd_set sockset; //read sets
int maxfd = -1; //maximum socket descriptor
Server_FD fdarray[FD_SETSIZE]; //array to store server sockets waiting for connection requests
int maxindex = 0; //the index past the last effective element in array fdarray
if(argc != 1)
err_quit("usage: ruptimed");
#ifdef _SC_HOST_NAME_MAX
n = sysconf(_SC_HOST_NAME_MAX);
if(n < 0)
#endif
n = HOST_NAME_MAX;
host = malloc(n);
if(host == NULL)
err_sys("malloc error");
if(gethostname(host, n) < 0)
err_sys("gethostname error");
printf("Host Name: %s\n", host);
daemonize("ruptimed");
hint.ai_flags = AI_CANONNAME;
hint.ai_family = 0;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = 0;
hint.ai_addrlen = 0;
hint.ai_canonname = NULL;
hint.ai_addr = NULL;
hint.ai_next = NULL;
if((err = getaddrinfo(host, "ruptime", &hint, &ailist)) != 0){
syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err));
exit(1);
}
FD_ZERO(&sockset);
for(aip = ailist; aip != NULL; aip = aip->ai_next){
if((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0){
if(sockfd > maxfd)
maxfd = sockfd;
//add the socket to fd_set sockset
FD_SET(sockfd, &sockset);
//add the socket and corresponding address to array fdarray
fdarray[maxindex].fd = sockfd;
fdarray[maxindex].addr = *aip;
maxindex++;
}
}
if(maxfd >=0)
serve(&sockset, maxfd, fdarray, maxindex);
exit(1);
}