小知识:Linux网络编程使用多进程实现服务器并发访问

采用多进程的方式实现服务器的并发访问的经典范例。 程序实现功能: 1.客户端从标准输入读入一行文字,发送到服务器. 2.服务器接收到客户端发来的文字后,原样返回给客户端. 3.客户端接收到服务器的发来的文字后,输出到标准输出,然后继续以上步骤。  服务器端过程:建立好监听套接字后,等待客户端的连接,接收到一个连接后,创建一个子进程来与客户端进行通信,主进程则继续等待其他客户端的连接。代码如下: #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <netdb.h> #define SERV_PORT 1113 #define LISTENQ  32 #define MAXLINE 1024 /***连接处理函数***/ void str_echo(int fd); int  main(int argc, char *argv[]){   int listenfd,connfd;   pid_t childpid;   socklen_t clilen;   struct sockaddr_in servaddr;   struct sockaddr_in cliaddr;   if((listenfd = socket(AF_INET, SOCK_STREAM,0))==-1){      fprintf(stderr,”Socket error:%s\n\a”,strerror(errno));      exit(1);   }   /* 服务器端填充 sockaddr结构*/    bzero(&servaddr, sizeof(servaddr));   servaddr.sin_family = AF_INET;   servaddr.sin_addr.s_addr = htonl (INADDR_ANY);   servaddr.sin_port = htons(SERV_PORT);   /* 捆绑listenfd描述符  */    if(bind(listenfd,(struct sockaddr*)(&servaddr),sizeof(struct sockaddr))==-1){     fprintf(stderr,”Bind error:%s\n\a”,strerror(errno));     exit(1);    }    /* 监听listenfd描述符*/     if(listen(listenfd,5)==-1){         fprintf(stderr,”Listen error:%s\n\a”,strerror(errno));         exit(1);     }   for ( ; ; )  {     clilen = sizeof(cliaddr);     /* 服务器阻塞,直到客户程序建立连接  */     if((connfd=accept(listenfd,(struct sockaddr*)(&cliaddr),&clilen))==-1){         fprintf(stderr,”Accept error:%s\n\a”,strerror(errno));         exit(1);     }     //有客户端建立了连接后     if ( (childpid = fork()) == 0) { /*子进程*/      close(listenfd);    /* 关闭监听套接字*/      str_echo(connfd);   /*处理该客户端的请求*/      exit (0);     }     close(connfd);/*父进程关闭连接套接字,继续等待其他连接的到来*/  } } void str_echo(int sockfd){  ssize_t n;     char  buf[MAXLINE];     again:       while ( (n = read(sockfd, buf, MAXLINE)) > 0)           write(sockfd, buf, n);       if (n < 0 && errno == EINTR)//被中断,重入           goto again;       else if (n < 0){//出错         fprintf(stderr,”read error:%s\n\a”,strerror(errno));         exit(1);       } } 客户端过程:创建连接套接字,主动向服务器发起连接请求,建立连接后,等待标准输入,输入完成后,将输入的内容发送给服务器,接着接收服务器发送过来的内容,并将接收到的内容输出到标准输出。代码如下: #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <netdb.h> #define SERV_PORT 1113 #define MAXLINE 1024 void str_cli(FILE *fp, int sockfd); int main(int argc, char **argv)  {     int     sockfd;     struct sockaddr_in servaddr;     if (argc != 2){         fprintf(stderr,”usage: tcpcli <IPaddress>\n\a”);         exit(0);     }  if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){         fprintf(stderr,”Socket error:%s\n\a”,strerror(errno));         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){             fprintf(stderr,”inet_pton Error:%s\a\n”,strerror(errno));             exit(1);    }       /* 客户程序发起连接请求*/        if(connect(sockfd,(struct sockaddr *)(&servaddr),sizeof(struct sockaddr))==-1){             fprintf(stderr,”connect Error:%s\a\n”,strerror(errno));             exit(1);       }      str_cli(stdin, sockfd);     /* do it all */      exit(0);  }  void str_cli(FILE *fp, int sockfd) {    int nbytes=0;    char  sendline[MAXLINE],recvline[MAXLINE];    while (fgets(sendline, MAXLINE, fp) != NULL){//从标准输入中读取一行       write(sockfd, sendline, strlen(sendline));//将该行发送给服务器       if ((nbytes=read(sockfd, recvline, MAXLINE)) == 0){//从sockfd读取从服务器发来的数据     fprintf(stderr,”str_cli: server terminated prematurely\n”);           exit(1);    }       recvline[nbytes]=\0;    fputs(recvline, stdout);    } } 运行结果:      1.首先启动服务器端程序。 viidiot@ubuntu$ ./dissrv &(在后台运行)      2.启动一个客户端 viidiot@ubuntu$ ./discli 127.0.0.1           hello,world!(客户端输入的内容)           hello,world!(服务器端返回的内容)       3.输入命令netstat -at查看tcp的连接情况,可以发现服务器端与客户端已经建立起了一个连接,而服务器的主进程则仍然在1113端口监听,等待连接其他连接的到来。 viidiot@ubuntu$ netstat –at tcp        0      0 *:1113                  *:*                     LISTEN      tcp        0      0 localhost.localdom:1113 localhost.localdo:57430 ESTABLISHED  tcp        0      0 localhost.localdo:57430 localhost.localdom:1113 ESTABLISHED 客户端输入EOF结束通信。 但是当我们开启多个客户端连接到服务器进行通信,完了之后通过按EOF结束通信后,可以从后台发现一个有趣的事情,即出现了大量的僵尸进程。如下所示: viidiot@ubuntu$ ps -a    PID TTY          TIME CMD  19403 pts/1    00:00:00 dissrv  19405 pts/1    00:00:00 dissrv <defunct>  19423 pts/1    00:00:00 dissrv <defunct>  19434 pts/1    00:00:00 dissrv <defunct>  19441 pts/1    00:00:00 dissrv <defunct>  造成大量僵尸进程的原因是,服务器子进程终止时,会发送一个SIGCHLD信号给父进程。但我们的代码中,并没有捕获该信号,而这个信号的默认动作是忽略,因此,为了避免僵尸进程的产生,我们需要捕获SIGCHLD来清楚僵尸进程。

声明: 猿站网有关资源均来自网络搜集与网友提供,任何涉及商业盈利目的的均不得使用,否则产生的一切后果将由您自己承担! 本平台资源仅供个人学习交流、测试使用 所有内容请在下载后24小时内删除,制止非法恶意传播,不对任何下载或转载者造成的危害负任何法律责任!也请大家支持、购置正版! 。本站一律禁止以任何方式发布或转载任何违法的相关信息访客发现请向站长举报,会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。本网站的资源部分来源于网络,如有侵权烦请发送邮件至:2697268773@qq.com进行处理。
建站知识

小知识:linux网络编程socket介绍

2023-6-6 3:53:28

建站知识

小知识:Linux网络编程wait()和waitpid()的讲解

2023-6-6 4:09:55

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索