linux网络编程echo多进程服务器
echo_server 多进程版本
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <memory.h> #include <signal.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> int sockfd; void sign_handler(int signo) { pid_t pid; int stat; if(signo==SIGINT) { printf("echo server close\n"); close(sockfd); exit(1); } if(signo==SIGCHLD){ while((pid=waitpid(-1,&stat,WNOHANG))>0){ printf("child %d closed\n",pid); } } return ; } void out_fd(int fd) { struct sockaddr_in arr; socklen_t len=sizeof(arr); if(getpeername(fd,(struct sockaddr*)&arr,&len)<0){ perror("getpeername fail\n"); exit(1); } char ip[16]; memset(&ip,0,sizeof(ip)); inet_ntop(AF_INET,&arr.sin_addr.s_addr,ip,sizeof(ip)); printf("%s connected\n",ip); } void server_do(int fd) { char buffer[BUFSIZ]; while(1){ printf("ready to read\n"); memset(buffer,0,sizeof(buffer)); ssize_t size; if((size=read(fd,buffer,sizeof(buffer)))<0){ perror("server child read fail\n"); break; }else if(size==0){ break; }else{ printf("number of received bytes=%ld\n",size); buffer[size-1]='\0'; printf("%s\n",buffer); if(write(fd,buffer,size)<0){ if(errno==EPIPE){ break; } perror("server child write fail\n"); } } } } int main(int argc,char *argv[]) { int fd; pid_t pid; if(argc<2) { printf("usage:%s <port>",argv[0]); exit(1); } //注册信号 if(signal(SIGINT,sign_handler)==SIG_ERR){ perror("signal sigint error\n"); exit(1); } if(signal(SIGCHLD,sign_handler)==SIG_ERR){ perror("signal sigint error\n"); exit(1); } /*create socket*/ sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0){ perror("socket create fail\n"); exit(1); } /*bind socket*/ struct sockaddr_in serveraddr; serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(atoi(argv[1])); serveraddr.sin_addr.s_addr=INADDR_ANY; if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){ perror("socket bind fail\n"); exit(1); } if(listen(sockfd,10)<0){ perror("socket listen fail\n"); exit(1); } /*accept*/ while(1){ if((fd=accept(sockfd,NULL,NULL))<0){ if(errno==EINTR){ continue; } perror("socket accept fail\n"); } pid_t pid=fork(); if((pid=fork())<0){ perror("fork fail\n"); continue; }else if(pid==0){ close(sockfd); out_fd(fd); server_do(fd); exit(0); } close(fd); } return 0; }
echo_client
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <memory.h> #include <signal.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h>
int sockfd;
void cli_do(int sockfd)
{
/*双向通信*/ char buffer[BUFSIZ];
char *promat=">";
size_t size;
while(1){
memset(buffer,0,sizeof(buffer));
write(STDOUT_FILENO,promat,1);
if((size=read(STDIN_FILENO,buffer,sizeof(buffer))>0){
if(size<0) continue;
buffer[size-1]='\0';
}else if(size==0){
continue;
}
if(write(sockfd,buffer,size)>0){
if(read(sockfd,buffer,sizeof(buffer))>0){
printf("%s\n",buffer);
}
}
}
}
int main(int argc,char *argv[]) { if(argc<3){ printf("usage:%s <ip><port>",argv[0]); exit(1); } /*create socket*/ sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0){ perror("socket create fail\n"); } struct sockaddr_in serveraddr; serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(atoi(argv[2])); inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr); if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){ perror("server connect fail\n"); exit(1); } cli_do(sockfd); close(sockfd); return 0; }
echo客户机改进:
客户机read阻塞与stdin时,如果此时服务器断开连接,服务器给客户机发送一个FIN,但是客户机此时阻塞与标准输入,它将看不到这个EOF
所以进程需要一个预先告知内核能力,使得内核一旦发现进程指定一个或者多个I/O条件就绪 ,它就通知进程,这个能力称为I/O多路复用
所以客户端cli_do改用select方式
cli_do修订
void cli_do(int sockfd) { char buffer[BUFSIZ]; char *promat=">"; size_t size; fd_set rset; FD_ZERO(&rset); while(1){ //select系统调用设置 FD_SET(sockfd,&rset); FD_SET(STDIN_FILENO,&rset); //设置select就绪事件 if(select(sockfd+1,&rset,NULL,NULL,NULL)<0){ perror("select close\n"); break; } if(FD_ISSET(STDIN_FILENO,&rset)){/*input is readable*/ if((size=read(STDIN_FILENO,buffer,sizeof(buffer))>0{ buffer[size-1]='\0';
}
write(sockfd,buffer,size) } if(FD_ISSET(sockfd,&rset)){/*socket is readable*/ if((size=read(sockfd,buffer,sizeof(buffer))==0){//服务端发送FIN过来 perror("server connect close\n"); break; }else{ printf("%s\n",buffer); } } } }
echo客户端改进:
这种客户端--服务端 应-答 模式全双工管道数据并没有写满,存在空间的极大浪费
我们改用 批量输入-批量应答 模式
使用select 和shutdown 函数,前者能接收到服务器关闭时的通知,后者运行我们批量输入
cli_do修订
void cli_do(int sockfd) { int stdineof; char buffer[BUFSIZ]; char *promat=">"; size_t size; fd_set rset; FD_ZERO(&rset); stdineof=0; while(1){ write(STDOUT_FILENO,promat,1); //select系统调用设置 FD_SET(sockfd,&rset); if(stdineof==0) FD_SET(STDIN_FILENO,&rset); //设置select就绪事件 if(select(sockfd+1,&rset,NULL,NULL,NULL)<0){ perror("select close\n"); break; } if(FD_ISSET(STDIN_FILENO,&rset)){/*input is readable*/ if((size=read(STDIN_FILENO,buffer,sizeof(buffer)))==0){ stdineof=1; shutdown(sockfd,SHUT_WR);/*send FIN*/ FD_CLR(STDIN_FILENO,&rset); continue; } buffer[size-1]='\0'; write(sockfd,buffer,size); } if(FD_ISSET(sockfd,&rset)){/*socket is readable*/ if((size=read(sockfd,buffer,sizeof(buffer)))==0){ if(stdineof==1){ return; }else{ perror("server terminated prematurely\n");
break; } } printf("%s\n",buffer); } } }
使用I/O复用模型 单进程 select 就绪事件监听描述符集 修改echo_server
echo_server 修订版
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <memory.h> #include <signal.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/select.h> int sockfd; void sign_handler(int signo) { pid_t pid; int stat; if(signo==SIGINT) { printf("echo server close\n"); close(sockfd); exit(1); } if(signo==SIGCHLD){ printf("client close\n"); wait(0); } return ; } void out_fd(int fd) { struct sockaddr_in arr; socklen_t len=sizeof(arr); if(getpeername(fd,(struct sockaddr*)&arr,&len)<0){ perror("getpeername fail\n"); exit(1); } char ip[16]; memset(&ip,0,sizeof(ip)); inet_ntop(AF_INET,&arr.sin_addr.s_addr,ip,sizeof(ip)); printf("%s connected\n",ip); } int main(int argc,char *argv[]) { int fd; pid_t pid; if(argc<2) { printf("usage:%s <port>",argv[0]); exit(1); } //注册信号 if(signal(SIGINT,sign_handler)==SIG_ERR){ perror("signal sigint error\n"); exit(1); } if(signal(SIGCHLD,sign_handler)==SIG_ERR){ perror("signal sigint error\n"); exit(1); } /*create socket*/ sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0){ perror("socket create fail\n"); exit(1); } /*bind socket*/ struct sockaddr_in serveraddr,cliaddr; serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(atoi(argv[1])); serveraddr.sin_addr.s_addr=INADDR_ANY; if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){ perror("socket bind fail\n"); exit(1); } if(listen(sockfd,10)<0){ perror("socket listen fail\n"); exit(1); } /*accept*/ int i,maxfd,maxi,connfd,clientfd; int nready,client[FD_SETSIZE]; ssize_t size; fd_set rset,allset; char buffer[BUFSIZ]; socklen_t clien; maxfd=sockfd; maxi=-1;/*index into client[] array*/ for(i=0;i<FD_SETSIZE;i++){ client[i]=-1; } FD_ZERO(&allset); FD_SET(sockfd,&allset); for(;;){ rset=allset; nready=select(maxfd+1,&rset,NULL,NULL,NULL); if(FD_ISSET(sockfd,&rset)){/*new client connection*/ clien=sizeof(cliaddr); if((connfd=accept(sockfd,(struct sockaddr*)&cliaddr,&clien))>0){ out_fd(connfd); } for(i=0;i<FD_SETSIZE;i++){ if(client[i]<0){ client[i]=connfd; break; } } if(i==FD_SETSIZE){ perror("too many clients\n"); } FD_SET(connfd,&allset);//添加新的监听就绪事件 if(connfd>maxfd) maxfd=connfd; if(i>maxi) maxi=i; if(--nready<=0) continue; } for(i=0;i<=maxi;i++){/*check all clients for data*/ if((clientfd=client[i])<0) continue; if(FD_ISSET(clientfd,&rset)){ printf("ready to read\n"); if((size=read(clientfd,buffer,sizeof(buffer)))==0){//接受到EOF,client已经关闭 close(clientfd); FD_CLR(clientfd,&allset); client[i]=-1; }else{ printf("%s\n",buffer); write(clientfd,buffer,size); if(--nready<=0) break; } } } } }
poll系统调用
void cli_do(int sockfd) { int stdineof; char buffer[BUFSIZ]; char *promat=">"; size_t size; fd_set rset; FD_ZERO(&rset); stdineof=0; while(1){ write(STDOUT_FILENO,promat,1); //select系统调用设置 FD_SET(sockfd,&rset); if(stdineof==0) FD_SET(STDIN_FILENO,&rset); //设置select就绪事件 if(select(sockfd+1,&rset,NULL,NULL,NULL)<0){ perror("select close\n"); break; } if(FD_ISSET(STDIN_FILENO,&rset)){/*input is readable*/ if((size=read(STDIN_FILENO,buffer,sizeof(buffer)))==0){ stdineof=1; shutdown(sockfd,SHUT_WR);/*send FIN*/ FD_CLR(STDIN_FILENO,&rset); continue; } buffer[size-1]='\0'; write(sockfd,buffer,size); } if(FD_ISSET(sockfd,&rset)){/*socket is readable*/ if((size=read(sockfd,buffer,sizeof(buffer)))==0){ if(stdineof==1){ return; }else{ perror("server terminated prematurely\n"); break; } } printf("%s\n",buffer); } } }
echo_server
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <memory.h> #include <signal.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <sys/select.h> #include <poll.h> int sockfd; void sign_handler(int signo) { pid_t pid; int stat; if(signo==SIGINT) { printf("echo server close\n"); close(sockfd); exit(1); } if(signo==SIGCHLD){ printf("client close\n"); wait(0); } return; } void out_fd(int fd) { struct sockaddr_in arr; socklen_t len=sizeof(arr); if(getpeername(fd,(struct sockaddr*)&arr,&len)<0){ perror("getpeername fail\n"); exit(1); } char ip[16]; memset(&ip,0,sizeof(ip)); inet_ntop(AF_INET,&arr.sin_addr.s_addr,ip,sizeof(ip)); printf("%s connected\n",ip); } int main(int argc,char *argv[]) { printf("tsest"); if(argc<2) { printf("usage:%s <port>",argv[0]); exit(1); } //注册信号 if(signal(SIGINT,sign_handler)==SIG_ERR){ perror("signal sigint error\n"); exit(1); } if(signal(SIGCHLD,sign_handler)==SIG_ERR){ perror("signal sigint error\n"); exit(1); } /*create socket*/ sockfd=socket(AF_INET,SOCK_STREAM,0); if(sockfd<0){ perror("socket create fail\n"); exit(1); } /*bind socket*/ struct sockaddr_in serveraddr,cliaddr; serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(atoi(argv[1])); serveraddr.sin_addr.s_addr=INADDR_ANY; if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){ perror("socket bind fail\n"); exit(1); } if(listen(sockfd,10)<0){ perror("socket listen fail\n"); exit(1); } /*poll系统调用*/ int i,maxfd,maxi,connfd,clientfd,nready,open_max; ssize_t size; char buffer[BUFSIZ]; socklen_t clien; open_max=sysconf(_SC_OPEN_MAX); struct pollfd client[open_max]; client[0].fd=sockfd; client[0].events=POLLRDNORM; for(i=0;i<open_max;i++) client[i].fd=-1; maxi=0; for(;;){ nready=poll(client,maxi+1,0); if(client[0].revents & POLLRDNORM){/*new client connection*/ printf("werwerew"); break; clien=sizeof(cliaddr); if((connfd=accept(sockfd,(struct sockaddr*)&cliaddr,&clien))>0) out_fd(connfd); for(i=0;i<open_max;i++){ if(client[i].fd<0){ client[i].fd=connfd; break; } } if(i==open_max) perror("too many clients\n"); client[i].events=POLLRDNORM;//添加新的监听就绪事件 if(i>maxi) maxi=i; if(--nready<=0) continue; } for(i=0;i<=maxi;i++){/*check all clients for data*/ if((clientfd=client[i].fd)<0) continue; if(client[i].revents&(POLLRDNORM|POLLERR)){ printf("ready to read\n"); break; if((size=read(clientfd,buffer,sizeof(buffer)))==0){//接受到EOF,client已经关闭 close(clientfd); client[i].fd=-1; }else{ printf("%s\n",buffer); write(clientfd,buffer,size); if(--nready<=0) break; } } } } return 0; }