在讨论之前先给出一段代码,其中存在一个很大的隐患,就是僵尸进程!!!!可用top命令进行查看,如下图所示:
由上图可看出,存在1个zombie(僵尸进程)!!!
下面就给出具体的实现:
config.h:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <errno.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> const int MAX_LINE = 2048; const int PORT = 9877; const int BACKLOG = 10; const int LISTENQ = 6666; const int MAX_CONNECT = 20;
#include "config.h" int main(int argc,char **argv) { //声明服务器地址和客户链接地址 struct sockaddr_in servaddr,cliaddr; //声明服务器监听套接字和客户端链接套接字 int listenfd,connfd; pid_t childpid; //声明缓冲区 char buf[MAX_LINE]; socklen_t clilen; //(1)初始化监听套接字litenfd if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0){ perror("socket error"); exit(1); } //(2)设置服务器sockaddr_in结构 bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); //(3)绑定套接字和端口 if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0){ perror("bind error"); exit(1); } //(4)监听客户请求 if(listen(listenfd,LISTENQ) < 0){ perror("listen error"); } //(5)接受客户端请求 for(; ;){ clilen = sizeof(cliaddr); if((connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen)) < 0){ perror("accept error"); exit(1); } //新建子进程单独处理链接 if((childpid = fork()) == 0){ close(listenfd); //str_echo ssize_t n; char buff[MAX_LINE]; while((n = read(connfd,buff,MAX_LINE)) > 0){ write(connfd,buff,n); } exit(0); } close(connfd); } //(6)关闭监听套接字 close(listenfd); }
#include "config.h" //readline函数实现 ssize_t readline(int fd,char *vptr,size_t maxlen) { ssize_t n,rc; char c,*ptr; ptr = vptr; for(n = 1;n < maxlen;n++){ if((rc = read(fd,&c,1)) == 1){ *ptr++ = c; if(c == '\n'){ break; } }else if(rc == 0){ *ptr = 0; return(n - 1); }else { return(-1); } } *ptr = 0; return(n); } int main(int argc,char **argv) { //声明套接字和链接服务器地址 int sockfd; struct sockaddr_in servaddr; //判断是否为合法的输入 if(argc != 2){ perror("usage :tcpcli<IPaddress>"); exit(1); } //(1)创建套接字 if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){ perror("socket error"); exit(1); } //(2)设置链接服务器地址结构 bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr) < 0){ printf("inet_pton error for %s\n",argv[1]); exit(1); } //(3)发送链接服务器请求 if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0){ perror("connect error"); exit(1); } //(4)消息处理 char sendline[MAX_LINE],recvline[MAX_LINE]; while(fgets(sendline,MAX_LINE,stdin) != NULL){ write(sockfd,sendline,strlen(sendline)); if(readline(sockfd,recvline,MAX_LINE) == 0){ perror("server terminated prematurely"); exit(1); } if(fputs(recvline,stdout) == EOF){ perror("fputs error"); exit(1); } } //(5)关闭套接字 close(sockfd); }执行结果:
其实现的功能就是客户端发送什么,服务器就会回射回来什么。。。看似程序没什么问题,但其存在一个很大隐患就是僵尸进程,产生僵尸进程的最主要的原因就是:在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程……
#ifndef _CONFIG_H_ #define _CONFIG_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #include <strings.h> #include <errno.h> #include <netdb.h> #include <signal.h> #define MAX_LINE 4096 #define SERV_PORT 9879 #define LISTENQ 6666 #define SA struct sockaddr #define BACKLOG 10 typedef void Sigfunc(int); struct args{ long arg1; long arg2; }; struct result{ long sum; }; #endif
#include "config.h" void sig_chld(int signo) { pid_t pid; int stat; while((pid = waitpid(-1,&stat,WNOHANG)) > 0){ printf("child %d terminated\n",pid); } return; } Sigfunc *Signal(int signo,Sigfunc *func) { struct sigaction act,oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(signo == SIGALRM){ #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif }else{ #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif } if(sigaction(signo,&act,&oact) < 0){ return(SIG_ERR); } return(oact.sa_handler); } ssize_t readn(int fd,void *vptr,size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while(nleft > 0){ if((nread = read(fd,ptr,nleft)) < 0){ if(errno == EINTR){ nread = 0; }else{ return(-1); } }else if(nread == 0){ break; } nleft -= nread; ptr += nread; } return(n - nleft); } void str_echo(int sockfd) { ssize_t n; struct args args; struct result result; for(;;){ if((n = readn(sockfd,&args,sizeof(args))) == 0){ return; } //result.sum = args.arg1 ; if(write(sockfd,&result,sizeof(result)) < 0){ perror("write error"); exit(1); } } } void str_cli(FILE *fp,int sockfd) { char sendline[MAX_LINE]; struct args args; struct result result; while(fgets(sendline,MAX_LINE,fp) != NULL){ if(sscanf(sendline,"%ld%ld",&args.arg1,&args.arg2) != 2){ printf("invalid input:%s",sendline); continue; } if(write(sockfd,&args,sizeof(args)) < 0){ perror("write error"); exit(1); } if(readn(sockfd,&result,sizeof(result)) == 0){ perror("str_cli:server terminated prematurely"); exit(1); } result.sum = args.arg1 + args.arg2; printf("%ld\n",result.sum); } }
#include "config.h" int main(int argc,char **argv) { //定义监听套接字和连接套接字 int listenfd,connfd; //定义子进程pid pid_t childpid; //套接字地址结构的长度 socklen_t clilen; //ipv4套接字地址结构 struct sockaddr_in cliaddr,servaddr; //信号处理函数 void sig_chld(int); //创建套接字 if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0){ perror("socket error"); exit(1); } //指定服务器ip和端口 bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); //绑定协议地址和套接字 if((bind(listenfd,(SA *)&servaddr,sizeof(servaddr))) < 0){ perror("bind error"); exit(1); } //监听套接字 if(listen(listenfd,LISTENQ) < 0){ perror("listen error"); exit(1); } Signal(SIGCHLD,sig_chld); for(;;){ clilen = sizeof(cliaddr); if((connfd = accept(listenfd,(SA *)&cliaddr,&clilen)) < 0){ if(errno = EINTR){ continue; }else{ perror("accept error"); exit(1); } } //创建子进程 if((childpid = fork()) == 0){ //关闭监听套接字 close(listenfd); str_echo(connfd); exit(0); } //父进程关闭连接 close(connfd); } }
#include "config.h" int main(int argc,char **argv) { int sockfd; struct sockaddr_in servaddr; if(argc != 2){ printf("usage:tcpcli<IPaddress>"); exit(1); } if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0){ perror("socket error"); exit(1); } bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr) < 0){ printf("inet_pton error for %s\n",argv[1]); exit(1); } if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0){ perror("connect error"); exit(1); } str_cli(stdin,sockfd); exit(0); }