网络socket服务器并发解决之多进程模型

目录

    • 1.服务器并发访问
    • 2.多进程编程
      • 创建进程
        • fork()系统调用
    • 3.多进程改写服务器程序
    • 4.代码实现

1.服务器并发访问

服务器按处理方式可以分为迭代服务器和并发服务器两类。上一节介绍了网络socket服务器的编程,但是服务器每次只能处理一个客户的请求,它实现简单但效率很低,通常这种服务器被称为迭代服务器。 然而在实际应用中,不可能让一个服务器长时间地为一个客户服务,而需要其具有同时处理 多个客户请求的能力,这种同时可以处理多个客户请求的服务器称为并发服务器,其效率很 高却实现复杂。在实际应用中,并发服务器应用的最广泛。linux有3种实现并发服务器的方式:多进程并发服务器,多线程并发服务器,IO复用,先来看多进程并发服务器的实现。
网络socket服务器并发解决之多进程模型_第1张图片

2.多进程编程

什么是一个进程?在操作系统原理使用这样的术语来描述的:正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进程。站在程序员的角度来看,我们使用vim编辑生成的C文件叫做源码,源码给程序员来看的但机器不识别,这时我们需要使用编译器gcc编译生成CPU可识别的二进制可执行程序并保存在存储介质上,这时编译生成的可执行程序只能叫做程序而不能叫进程。而一旦我们通过命令(./a.out)开始运行时,那正在运行的这个程序及其占用的资源就叫做进程了。进程这个概念是针对系统而不是针对用户的,对用户来说,他面对的概念是程序。很显然,一个程序可以执行多次,这也意味着多个进程可以执行同一个程序。

创建进程

Linux内核在启动的最后阶段会创建init进程来执行程序/sbin/init,该进程是系统运行的第一个进程,进程号为 1,称为Linux 系统的初始化进程,该进程会创建其他子进程来启动不同写系统服务,而每个服务又可能创建不同的子进程来执行不同的程序。Linux下有两个基本的系统调用可以用于创建子进程:fork()和vfork()。

fork()系统调用

 pid_t fork(void);

fork在英文中是"分叉"的意思。为什么取这个名字呢?因为一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就”分叉”了,所以这个名字取得很形象。在我们编程的过程中,一个函数调用只有一次返回(return),但由于fork()系统调用会创建一个新的进程,这时它会有两次返回。一次返回是给父进程,其返回值是子进程的PID(Process ID),第二次返回是给子进程,其返回值为0。所以我们在调用fork()后,需要通过其返回值来判断当前的代码是在父进程还是子进程运行,如果返回值是0说明现在是子进程在运行,如果返回值>0说明是父进
程在运行,而如果返回值<0的话,说明fork()系统调用出错。fork 函数调用失败的原因主要有两个:
1.系统中已经有太多的进 程;
2.该实际用户 ID 的进程总数超过了系统限制。

每个子进程只有一个父进程,并且每个进程都可以通过getpid()获取自己的进程PID,也可以通过getppid()获取父进程的PID,这样在fork()时返回0给子进程是可取的。一个进程可以创建多个子进程,这样对于父进程而言,他并没有一个API函数可以获取其子进程的进程ID,所以父进程在通过fork()创建子进程的时候,必须通过返回值的形式告诉父进程其创建的子进程PID。这也是fork()系统调用两次返回值设计的原因。

3.多进程改写服务器程序

网络socket服务器并发解决之多进程模型_第2张图片

4.代码实现

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#define  MSG_NUM  1024


void print_usage(char *progname)
{
     
        printf("%s usage: \n", progname);
        printf("-p(--LISTEN_PORT): sepcify server port.\n");
        printf("-h(--Help): print this help information.\n");
        return ;
}



int main(int argc,char **argv)
{
     
      int    sockfd = -1;
      int    clifd =-1;
      int    rv  =-1;
      int    LISTEN_PORT = 0;
      int    rw;
      int    on =1;
      char   buf[MSG_NUM];
      struct sockaddr_in     servaddr;
      struct sockaddr_in     cliaddr;
      pid_t pid;
      socklen_t              cli_len=sizeof(struct sockaddr);  
      struct option longopts[] = {
     
          {
     "help", no_argument, NULL, 'h'},
          {
     "LISTEN_PORT", required_argument, NULL, 'P'},
          {
     0, 0, 0, 0}
          };
 
        while( (rw=getopt_long(argc, argv, "p:h", longopts, NULL)) != -1 )
         {
     
             switch(rw)
             {
     
                 case 'p':
                     LISTEN_PORT=atoi(optarg);
                     break;
                 case 'h':
                     print_usage(argv[0]);
                     return 0;
                     
             }
                  
             
         }
        
         
        if( !LISTEN_PORT )
        {
     
             print_usage(argv[0]);
             return 0;
         }
         


      sockfd=socket(AF_INET, SOCK_STREAM,0);
      if(sockfd<0)
      {
     
         printf("Create socket failure:%s\n",strerror(errno));
         return 0;
      }
      printf("Create socket[%d] successfully!\n",sockfd);
      setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
      memset(&servaddr, 0, sizeof(servaddr));
      servaddr.sin_family=AF_INET;
      servaddr.sin_port = htons(LISTEN_PORT);
      servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      if(bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
      {
     
         printf("Bind socket failure:%s\n ",strerror(errno));
         return -1;
         
      }
      printf("socket[%d] bind on port[%d] for all IP address ok",sockfd,LISTEN_PORT);



      listen(sockfd,13);
      printf("Start to listen on port [%d]\n", LISTEN_PORT);
       


      while(1)
      {
     
           printf("Start accept new client incoming...\n");      
      
           printf("Value of cli_len:%d",cli_len);
           clifd=accept(sockfd,(struct sockaddr*)&cliaddr,&cli_len);
           if(clifd<0)
           {
      
               printf("Accept new client failure: %s\n", strerror(errno));
               return -2;
           }
           printf("Accept new client[%s:%d] with fd [%d]\n", inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port), clifd);
 
           pid = fork();
           if (pid<0)
           {
     
               printf("fork error:%s",strerror(errno));
               close(clifd);
               continue;

           }
           else if (pid>0)
           {
     
              close(clifd);
              continue;
           }
           else if (pid==0)
           {
     
              printf("Child process PID[%d] running...\n",getpid());
              close(sockfd);
              memset(buf,0,sizeof(buf));
              rv=read(clifd,buf,sizeof(buf));
      
          if(rv<0)
          {
     
              printf("Read data from client[%d] failure:%s\n",clifd,strerror(errno));
              close(clifd);
              return -3;
        
          }
          if (rv==0)
          {
     
             printf("The client[%d] has disconnected!",clifd);
             return -4;
          }
          printf("Read %d bytes data from socket[%d]:%s\n",rv,clifd,buf);

          if((write(clifd,buf,rv))<0)
             {
      
              printf("write to client[%d] failure:%s\n",clifd,strerror(errno));
              close(clifd);
              return -5;
              }
         }

      printf("close client socket[%d] and child process exit\n", clifd);      
        
      close(clifd);
      exit(0);
      }

      close(sockfd);
    

}

你可能感兴趣的:(网络socket服务器并发解决之多进程模型)