总结:
1.、TCP的地址复用(address reuse)问题
2、HOST_NAME_MAX 的处理方法
3、多进程socket编程中 close() 和 shutdown() 的问题
4、gethostname() 的问题
5、getaddrinfo(hostname, "ruptime", &hint, &ailist) 中 "ruptime" 服务的问题("Servname not supported for ai_socktype")
-----initserver.c
/* * this function is also support connectionless server * Note: * 1. address reuse */ #include "initserver.h" #include <errno.h> #include <unistd.h> int initserver(int type, const struct sockaddr *addr, socklen_t addrlen, int maxconn) { int sockfd; int err; int reuse = 1; /* non-zero */ if( (sockfd = socket(addr->sa_family, type, 0)) < 0 ) { return -1; } /* [Note-01] address reuse(APUE-2: 16.6-Socket Options)*/ if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { err = errno; goto errout; } if(bind(sockfd, addr, addrlen) < 0) { err = errno; /* [Q-01] I don't know why.*/ goto errout; } /* Listen to SOCK_STREAM or SOCK_SEQPACKET socket, this function is * also support connectionless server. */ if( (type == SOCK_STREAM) || (type == SOCK_SEQPACKET) ) { if(listen(sockfd, maxconn) < 0) { err = errno; goto errout; } } return sockfd; errout: errno = err; close(sockfd); return -1; }
-----ruptimed.c(server)
/* Server */ /* Example from: APUE-2e Note: 1. HOST_NAME_MAX(see APUE-2e) 2. shutdown() and close() in a multi-process program 3. /etc/hosts, /etc/hostname, gethostname() 4. 在多进程程序中注意关闭不用的文件描述符。在子进程中关闭从父进程继承来的不用的文件描述符,在父进程中关闭为子进程创建而自身不适用的文件描述符 */ #include "initserver.h" #include "daemonize.h" #include <sys/socket.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <netdb.h> #include <syslog.h> #include <signal.h> #include <sys/wait.h> #include "print_ai.h" /* [Note-01] */ #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 #endif #define MAXCONN 32 #define BUFLEN 256 static void sig_chld(int signo) { while( waitpid(-1, NULL, WNOHANG) > 0 ){} return; } void serve(int listensock) { int acceptsock; pid_t pid; FILE *fp; char buf[BUFLEN]; while(1) { if( (acceptsock = accept(listensock, NULL, NULL)) < 0 ) { syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno)); exit(errno); } if( (pid = fork()) < 0 ) { syslog(LOG_ERR, "ruptimed: fork error: %s", strerror(errno)); exit(errno); } if(pid == 0) /* use multi-process */ { close(listensock); /* close the listening socket in child process*/ if( (fp = popen("/usr/bin/uptime", "r")) == NULL) { sprintf(buf, "popen(\"/usr/bin/uptime\") error: %s\n", strerror(errno)); send(acceptsock, buf, strlen(buf), 0); } else { while(fgets(buf, BUFLEN, fp) != NULL) { send(acceptsock, buf, strlen(buf), 0); } sleep(20); /* test blocking */ pclose(fp); } // shutdown(acceptsock, SHUT_RDWR); close(acceptsock); /* close the accept socket in child process */ exit(0); } /** * [Note-02] If either of the child and the parent does not call close() to close the socket, the client will be blocked. * Calling shutdown() in either child or parent, or calling close() in both child and parent can solve this problem. */ close(acceptsock); /* close the accept socket in parent process */ } } int main() { int sockfd, hostlen, errcode; char *hostname = NULL; struct addrinfo hint; struct addrinfo *ailist = NULL; struct addrinfo *aip = NULL; /* Get hostname */ #ifdef _SC_HOST_NAME_MAX hostlen = sysconf(_SC_HOST_NAME_MAX); if(hostlen < 0) /* means that there is no definite limit. */ #endif { hostlen = HOST_NAME_MAX; } if( (hostname = malloc(hostlen)) < 0 ) { perror("malloc error"); exit(errno); } if(gethostname(hostname, hostlen) < 0) { perror("gethostname error"); exit(errno); } // printf("hostname: %s\n", hostname); /* get address. First, we should add the service "ruptime" to /etc/services, or there will be a error "Servname not supported for ai_socktype" */ hint.ai_flags = 0; // AI_CANONNAME hint.ai_family = 0; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; if( (errcode = getaddrinfo(hostname, "ruptime", &hint, &ailist)) != 0) { printf("ruptimed: getaddrinfo error : %s\n", gai_strerror(errcode)); exit(1); } /* Prevent zombies */ if( signal(SIGCHLD, sig_chld) == SIG_ERR ) { perror("signal(SIGCHLD) error"); exit(errno); } print_ai(ailist); daemonize("ruptimed"); for(aip = ailist; aip != NULL; aip = aip->ai_next) { if( (sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, MAXCONN)) >= 0 ) { serve(sockfd); close(sockfd); /* close the listening socket */ exit(0); } } syslog(LOG_ERR, "%s", strerror(errcode)); return 1; } /* $ ./ruptimed flags: 0 family: inet socket type: stream protocol: IPPROTO_TCP host: address: 127.0.1.1 port: 60000 $ ./ruptime localhost flags: 0 family: inet socket type: stream protocol: IPPROTO_TCP host: address: 127.0.0.1 port: 60000 Connection refused cat /etc/hostname Ubuntu-Server $ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 Ubuntu-Server.localdomain Ubuntu-Server # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters $ ./ruptime Ubuntu-Server # 能正常连接到服务器端 [Note-03] gethostname() 获取到 hostname 为 "Ubuntu-Server", 然后根据 /etc/hosts 中得到 Ubuntu-Server 的地址为 "127.0.1.1" */
-----ruptime.c(client)
/* Client */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <netdb.h> #include <unistd.h> #include "print_ai.h" #define BUFLEN 256 int main(int argc, char *argv[]) { struct addrinfo hint; struct addrinfo *ailist = NULL; struct addrinfo *aip = NULL; int errcode, sockfd; char buf[BUFLEN]; int n; if(argc != 2) { printf("usage: ruptime <hostname>\n"); exit(1); } hint.ai_flags = 0; hint.ai_family = 0; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; if( (errcode = getaddrinfo(argv[1], "ruptime", &hint, &ailist)) != 0 ) { printf("getaddrinfo error: %s\n", gai_strerror(errcode)); exit(1); } print_ai(ailist); for(aip = ailist; aip != NULL; aip = aip->ai_next) { if( (sockfd = socket(aip->ai_family, aip->ai_socktype, 0)) < 0 ) { errcode = errno; continue; } if(connect(sockfd, aip->ai_addr, aip->ai_addrlen) < 0) { errcode = errno; continue; } sleep(20); while( (n = recv(sockfd, buf, BUFLEN, 0)) > 0 ) { write(STDOUT_FILENO, buf, n); } if(n < 0) { perror("recv error"); exit(errno); } return 0; } fprintf(stderr, "%s\n", strerror(errcode)); return 1; }