Linux TCP 服务器编程(二):简单的并发服务器

来源:互联网 发布:python 数组 clear 编辑:程序博客网 时间:2024/04/29 17:10

【版权声明:转载请保留出处:blog.csdn.net/gentleliu。邮箱:shallnew*163.com】

在讲述并发服务器之前,需要先了解一下fork函数。

  #include<unistd.h>  pid_t fork(void);

该函数很特殊,调用一次返回2次:在父进程中返回一次,返回的是子进程的ID号;在子进程中再返回一次,返回值为0;

  父进程中打开的所有描述符在fork之后与子进程分享,一般父进程调用accept函数之后就调用fork函数,连接的套接字和子进程共享。通常情况下,父进程关闭连接套接字,子进程继续读写该套接字。Linux编写并发服务器最简单的方法就是使用fork一个子进程来服务每个客户。

下面修改前一章给出的服务器,得出一个简单的并发服务器。

  #include <stdio.h>  #include <unistd.h>  #include <errno.h>  #include <string.h>  #include <sys/types.h>  #include <sys/socket.h>  #include <stdlib.h>  #include <arpa/inet.h>  #include <netinet/in.h>    #include "server.h"  #include "ly_debug.h"    void accept_handle(int connfd, struct sockaddr_in peeraddr);    int main(int argc, const char *argv[])  {      int                 sockfd, connfd;      struct sockaddr_in  seraddr, peeraddr;      socklen_t           addrlen;        COMPILE_TIME_PRINT;        if (argc != 2) {          printf("Usage: %s <port>\n", argv[0]);          return -1;      }      sockfd = socket(AF_INET, SOCK_STREAM, 0);      if (sockfd < 0) {          LY_ERR("socket: %s\n", strerror(errno));          return -1;      }        seraddr.sin_family = AF_INET;      seraddr.sin_port = ntohs(atoi(argv[1]));      seraddr.sin_addr.s_addr = htonl(INADDR_ANY);        if (bind(sockfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr))) {          LY_ERR("bind: %s\n", strerror(errno));          return -1;      }        if (listen(sockfd, MAX_CONN) < 0) {          LY_ERR("listen: %s\n", strerror(errno));          return -1;      }        addrlen = sizeof(struct sockaddr);      for (;;) {          connfd = accept(sockfd, (struct sockaddr *)(&peeraddr), &addrlen);          if (connfd < 0) {              LY_ERR("accept: %s\n", strerror(errno));              continue;          }                    if (0 == fork()) {  close(sockfd);              accept_handle(connfd, peeraddr);              close(connfd);              exit(0);          }          close(connfd);      }      return 0;  }    void accept_handle(int connfd, struct sockaddr_in peeraddr)  {      char            buf[32] = "Hello, welcome to server!";        LY_IFO("Receive request from %s, port %d\n",              inet_ntop(AF_INET, &peeraddr.sin_addr, buf, sizeof(buf)),              ntohs(peeraddr.sin_port));        sleep(10);        snprintf(buf, sizeof(buf), "Hello, welcome to server!");      if (send(connfd, buf, sizeof(buf), 0) < 0) {          LY_ERR("send: %s\n", strerror(errno));          return;      }  }
如上一节同样的方式运行,只用一个客户端连接服务器和上一节执行结果没有什么区别,但是如果使用多个客户端连接服务器就不一样了。如果使用2个或多个客户端几乎同时连接服务器的话,服务器大约10秒就可以处理完客户请求并返回信息,上一节的迭代服务器处理时间会随着客户端的连接数量成倍数的增加。

  如果在多个客户连接且服务器处理完成之后,读者使用ps查看进程时会发现有很多的僵尸进程,其实那些都是服务器父进程产生处理客户请求的子进程,子进程调用exit退出之后发送SIGCHLD信号给父进程,在此服务器程序之中父进程没有处理该信号,该信号默认是被忽略,这样子进程就进入了僵死状态。如下图,服务器在处理了3个客户请求后出现了三个服务器进程状态为Z+。

我们必须清理僵死进程,否则会随着客户连接的增多耗尽系统资源,这涉及到信号处理,在下一章我们将讲解服务器对信号的处理,解决该问题。


0 0