Linux socket-多进程面向连接的服务器客户端程序

来源:互联网 发布:js使用java变量 编辑:程序博客网 时间:2024/05/18 00:34

总结:

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>intinitserver(int type, const struct sockaddr *addr, socklen_t addrlen, int maxconn){intsockfd;interr;intreuse = 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-2eNote:1. HOST_NAME_MAX(see APUE-2e)2. shutdown() and close() in a multi-process program3. /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] */#ifndefHOST_NAME_MAX#defineHOST_NAME_MAX256#endif#defineMAXCONN32#defineBUFLEN256static void sig_chld(int signo){while( waitpid(-1, NULL, WNOHANG) > 0 ){}return;}voidserve(int listensock){intacceptsock;pid_tpid;FILE*fp;charbuf[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(){intsockfd, hostlen, errcode;char*hostname = NULL;struct addrinfohint;struct addrinfo *ailist = NULL;struct addrinfo *aip = NULL;/*  Get hostname */#ifdef_SC_HOST_NAME_MAXhostlen = 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_CANONNAMEhint.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;}/*$ ./ruptimedflags: 0family: inetsocket type: streamprotocol: IPPROTO_TCPhost:address: 127.0.1.1port: 60000$ ./ruptime localhostflags: 0family: inetsocket type: streamprotocol: IPPROTO_TCPhost:address: 127.0.0.1port: 60000Connection refusedcat /etc/hostnameUbuntu-Server$ cat /etc/hosts127.0.0.1       localhost127.0.1.1       Ubuntu-Server.localdomain       Ubuntu-Server# The following lines are desirable for IPv6 capable hosts::1     ip6-localhost ip6-loopbackfe00::0 ip6-localnetff00::0 ip6-mcastprefixff02::1 ip6-allnodesff02::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"#defineBUFLEN256int main(int argc, char *argv[]){struct addrinfohint;struct addrinfo*ailist = NULL;struct addrinfo*aip = NULL;interrcode, sockfd;charbuf[BUFLEN];intn;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;}



原创粉丝点击