首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

Linux 停服务器设计( 一 )

2012-07-31 
Linux 下服务器设计( 一 )?这里讲的仅仅是一个简单的server的模型!为了处理同时来到很多小的链接请求( 解

Linux 下服务器设计( 一 )

?这里讲的仅仅是一个简单的server的模型!为了处理同时来到很多小的链接请求( 解释:就是请求很简单,持续时间很短,那么if? server在请求到来时在fork来处理它,有可能fork的时间比应答请求的还要少,那么就是不合理的服务设计 ),所以我们采用的是“prefork”和“prethread”模型!

?

????? Unix 网络编程 上的4个模型是:prefork:主进程accept

??????????????????????????????????????????????????????????????????????????????? 子进程accept

??????????????????????????????????????????????????????????????? prethread:

???????????????????????????????????????????????????????????????????????????????? 主线程accept

???????????????????????????????????????????????????????????????????????????????? 子线程accept?? ( 姑且使用主线程和子线程来描述 )

?

?????? 第一部分是:使用“预先生成进程”处理

?

?????? CODE_1 : server是:主进程accept,那么这是4种方法中最复杂的,因为要涉及到进程间传递socket描述符的问题!( 进程间传递描述符在上一篇bolg中有过 !),server采用轮询的方式将socket传递给子进程!

? ? ?? 话不多说,贴上代码:

?Server:

#include <stdio.h>  #include <unistd.h>  #include <stdlib.h>  #include <string.h>  #include <sys/select.h>  #include <sys/types.h>  #include <errno.h>  #include <sys/socket.h>  #include <netinet/in.h>  #include <sys/epoll.h>  #include <fcntl.h>    #define PORT            6000  #define MAXBACK 100  #define MAXLINE     1024  #define CHILD_NUM   10    typedef struct child_process  {      pid_t       s_pid;          //!> 子进程的pid      int     s_pipe_fd;      //!> 与子进程通信的pipe口      int     s_status;       //!> 子进程的状态!0:闲 1:忙  }child_process;    child_process  child[CHILD_NUM];    //!> 定义10个子进程( 此处以10个为例 )    static  int n_child_use = 0;        //!> 几个child在工作( if 全忙就不给他们 )      //!> 发送socket描述符( 这个代码在上一篇博文上有 )  //!>  int send_fd( int fd_send_to, void * data, size_t len, int sock_fd )  {      struct msghdr   msghdr_send;        //!> the info struct      struct iovec        iov[1];     //!> io vector      size_t              n;      //!>            union      {          struct cmsghdr  cm;     //!> control msg          char    ctl[CMSG_SPACE(sizeof( int ))]; //!> the pointer of char      }ctl_un;            struct cmsghdr * pCmsghdr = NULL;   //!> the pointer of control            msghdr_send.msg_control = ctl_un.ctl;         msghdr_send.msg_controllen = sizeof( ctl_un.ctl );            //!> design : the first info      pCmsghdr = CMSG_FIRSTHDR( &msghdr_send );       //!> the info of head      pCmsghdr->cmsg_len = CMSG_LEN(sizeof(int));     //!> the msg len      pCmsghdr->cmsg_level = SOL_SOCKET;       //!> -> stream mode                     pCmsghdr->cmsg_type = SCM_RIGHTS;        //!> -> file descriptor      *((int *)CMSG_DATA( pCmsghdr )) = sock_fd;  //!> data: the file fd             //!> these infos are nosignification      msghdr_send.msg_name = NULL;            //!> the name          msghdr_send.msg_namelen = 0;            //!> len of name            iov[0].iov_base = data;             //!> no data here      iov[0].iov_len = len;               //!> the len of data            msghdr_send.msg_iov = iov;          //!> the io/vector info      msghdr_send.msg_iovlen = 1;         //!> the num of iov            return ( sendmsg( fd_send_to, &msghdr_send, 0 ) );  //!> send msg now  }    //!> 接收socket描述符  //!>   int recv_sock_fd( int fd, void * data, size_t len, int * recv_fd )  {      struct msghdr   msghdr_recv;        //!> the info struct      struct iovec        iov[1];     //!> io vector      size_t              n;  //!>            union      {          struct cmsghdr  cm;     //!> control msg          char    ctl[CMSG_SPACE(sizeof( int ))]; //!> the pointer of char      }ctl_un;            struct cmsghdr * pCmsghdr = NULL;       //!> the pointer of control            msghdr_recv.msg_control = ctl_un.ctl;         msghdr_recv.msg_controllen = sizeof( ctl_un.ctl );            //!> these infos are nosignification      msghdr_recv.msg_name = NULL;        //!> the name          msghdr_recv.msg_namelen = 0;        //!> len of name            iov[0].iov_base = data;         //!> no data here      iov[0].iov_len = len;           //!> the len of data            msghdr_recv.msg_iov = iov;      //!> the io/vector info      msghdr_recv.msg_iovlen = 1;     //!> the num of iov            if( ( n = recvmsg( fd, &msghdr_recv, 0 ) ) < 0 ) //!> recv msg      {                       //!> the msg is recv by msghdr_recv          printf("recv error : %d\n", errno);          exit(EXIT_FAILURE);      }            //!> now, we not use 'for' just because only one test_data_       if(     ( pCmsghdr = CMSG_FIRSTHDR( &msghdr_recv ) ) != NULL    //!> now we need only one,        && pCmsghdr->cmsg_len == CMSG_LEN( sizeof( int ) )     //!> we should use 'for' when        )                                                                              //!> there are many fds       {          if( pCmsghdr->cmsg_level != SOL_SOCKET )          {              printf("Ctl level should be SOL_SOCKET :%d \n", errno);              exit(EXIT_FAILURE);          }                    if( pCmsghdr->cmsg_type != SCM_RIGHTS )          {              printf("Ctl type should be SCM_RIGHTS : %d\n", errno);              exit(EXIT_FAILURE);          }                    *recv_fd =*((int*)CMSG_DATA(pCmsghdr)); //!> get the data : the file des*        }       else       {          *recv_fd = -1;       }              return n;    }      //!> 子进程具体的执行过程  //!>  void web_child( int con_fd )  {      char        buf[MAXLINE];      int         n_read;      int         i = 0;            while( strcmp( buf, "Q" ) != 0 && strcmp( buf, "q" ) != 0  )      {          memset( buf, 0, sizeof( buf ) );                    if( ( n_read = read( conn_fd, buf, MAXLINE ) ) < 0 )          {              printf( "Read errnr! :%d \n", errno );              exit( EXIT_FAILURE );          }          else if( n_read == 0 )          {              continue;          }          else          {              while( buf[i] )              {                  buf[i] = toupper( buf[i] );                  i++;              }              buf[i] = '\0';                                printf("Child %d done! \n", ( unsigned int )pthread_self());              printf("Child %d send %s\n", ( unsigned int )pthread_self(), buf);              write( conn_fd, buf, strlen( buf ) );           //!> 写回给client          }      }            printf("Child %d : Dating end!\n", ( unsigned int )pthread_self());            }      //!> child process 的主函数   //!>  void child_main( int i )  {      char    data;       //!> 由于此处我们主要是传递socket,那么data一般就给一个” “做一个标志就好      int     con_fd; //!> 接受con_fd      int     n_read; //!> 读取长度            printf( "Child %d starting ... \n", i );            while( 1 )      {          if( ( n_read = recv_sock_fd( STDERR_FILENO, &data, 1, &con_fd ) ) == 0 )           {              continue;           //!> 此处理论上应该是阻塞,但是简化为轮询              //printf( " Child process %d read errnr! : %d\n", i, errno );              //exit( EXIT_FAILURE );          }                    if( con_fd < 0 )          {              printf("Child %d read connfd errnr! : %d\n", i, errno);              exit( EXIT_FAILURE );          }                    web_child( con_fd );                //!> child具体的执行过程                    write( STDERR_FILENO, " ", 1 ); //!> 随便写点什么让server知道我处理完成了,那么就可以将状态位置为0了      }  }    //!> 产生子进程及相关处理  //!>   void child_make( int i, int listen_fd )  {      int     sock_fd[2];     //!> 为了和主进程通信创建socket pair      pid_t   pid;            //!> 创建 socketpair      if( socketpair( AF_LOCAL, SOCK_STREAM, 0, sock_fd ) == -1 )      {          printf( "create socketpair error : %d\n", errno );          exit( EXIT_FAILURE );      }            if( ( pid = fork() ) > 0 )       //!> 父进程      {          close( sock_fd[1] );          child[i].s_pid = pid;          child[i].s_pipe_fd = sock_fd[0];          child[i].s_status = 0;          return;      }            if( dup2( sock_fd[0], STDERR_FILENO ) == -1 )   //!> 现在可以使用STDERR_FILENO替换刚刚创建的sock描述符      {                       //!> 往后的child的操作就可以STDERR_FILENO中进行!          printf("socket pair errnr! : %d\n", errno);          exit( EXIT_FAILURE );      }            close( sock_fd[0] );        //!> 这些描述符都bu需要了!      close( sock_fd[1] );      close( listen_fd );            child_main( i );            //!> child 主循环  }    //!> MAIN PROCESS  //!>  int main( int argc, char ** argv )  {      int     i;      int         listen_fd;      int         conn_fd;      int     max_fd;      int     n_select;      int     n_read;      char    buf[5];      fd_set  all_set, now_set;      struct sockaddr_in servaddr;      struct sockaddr_in cliaddr;      int     len = sizeof( struct sockaddr_in );            //!> server 套接口      //!>       bzero( &servaddr, sizeof( servaddr ) );      servaddr.sin_family = AF_INET;      servaddr.sin_addr.s_addr = htonl( INADDR_ANY );      servaddr.sin_port = htons( PORT );            //!> 建立套接字      if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )      {          printf("Socket Error...\n" , errno );          exit( EXIT_FAILURE );      }            //!> 绑定      //!>      if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( servaddr ) ) == -1 )      {          printf("Bind Error : %d\n", errno);          exit( EXIT_FAILURE );      }            //!> 监听      //!>       if( listen( listen_fd, MAXBACK ) == -1 )      {          printf("Listen Error : %d\n", errno);          exit( EXIT_FAILURE );      }            FD_ZERO( &all_set );      FD_SET( listen_fd, &all_set );          //!> 将listenfd加入select      max_fd = listen_fd;            for( i = 0; i < CHILD_NUM; i++ )      {          child_make( i, listen_fd );          FD_SET( child[i].s_pipe_fd, &all_set ); //!> 将子进程socket加入          max_fd = max_fd > child[i].s_pipe_fd ? max_fd : child[i].s_pipe_fd;      }            while( 1 )                  //!> 主进程循环      {          now_set = all_set;          if( n_child_use >= CHILD_NUM )   //!> 没有可以使用的child 了          {               //!> 那么就将listenfd从中清空,也就是不在响应listen了,直到有child空闲              FD_CLR( listen_fd, &now_set );          }                    if( (n_select = select( max_fd + 1, &now_set, NULL, NULL, NULL )) == -1)          {              printf(" Main process select errnr~ :%d\n", errno);              exit( EXIT_FAILURE );          }                    if( FD_ISSET( listen_fd, &now_set ) )           //!> if来了请求          {              if( ( conn_fd = accept( listen_fd, ( struct sockaddr *)&cliaddr , &len ) ) == -1 )              {                  printf("Server accept errnr! : %d\n", errno);                  exit( EXIT_FAILURE );              }                            for( i = 0; i < CHILD_NUM; i++ )              {                  if( child[i].s_status == 0 )        //!> 此child闲置                  {                      break;                  }              }                            if( i == CHILD_NUM )        //!> 说明child已经全部处于忙态              {                  printf("All childs are busy! \n");                  exit( EXIT_FAILURE );       //!> 此处可以等待哦,或者丢弃数据              }                            child[i].s_status = 1;          //!> busy              n_child_use++;              //!> busy child ++                            send_fd( child[i].s_pipe_fd, " ", 1, conn_fd ); //!> 发送socket描述符              close( conn_fd );               //!> server不需要处理了                            if( --n_select == 0 )           //!> 没有其他的请求了              {                  continue;              }          }                    for( i = 0; i < CHILD_NUM;  i++ )        //!> 看看那些child发来了msg,其实server知道肯定是child完成处理的提示标志          {              if( FD_ISSET( child[i].s_pipe_fd, &now_set ) )              {                  if( ( n_read = read( child[i].s_pipe_fd, buf, 5 ) ) == 0 )  //!> 这里的buf中data没有用,仅仅是child告诉server我完成了                  {                      printf("Child %d exit error! : %d\n", i,  errno);                      exit( EXIT_FAILURE );                  }                                    child[i].s_status = 0;      //!> 状态位置闲                                    if( --n_select == 0 )       //!> if没有其他child回送消息就不要浪费时间for了                  {                      break;                  }              }          }                  }            return 0;  }  Client:[cpp] view plaincopyprint?#include <stdio.h>  #include <unistd.h>  #include <stdlib.h>  #include <string.h>  #include <errno.h>  #include <netinet/in.h>  #include <sys/types.h>  #include <sys/socket.h>  #include  <arpa/inet.h>  #include <sys/select.h>    #define MAXLINE 1024  #define SERV_PORT 6000    //!> 注意输入是由stdin,接受是由server发送过来  //!> 所以在client端也是需要select进行处理的  void send_and_recv( int connfd )  {      FILE * fp = stdin;      int   lens;      char send[MAXLINE];      char recv[MAXLINE];      fd_set rset;      FD_ZERO( &rset );      int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd  + 1 );                                    //!> 输入和输出的最大值      int n;            while( 1 )      {          FD_SET( fileno( fp ), &rset );          FD_SET( connfd, &rset );            //!> 注意不要把rset看作是简单的一个变量                                  //!> 注意它其实是可以包含一组套接字的哦,                                  //!> 相当于是封装的数组!每次都要是新的哦!                    if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 )          {              printf("Client Select Error..\n");              exit(EXIT_FAILURE  );          }                    //!> if 连接口有信息          if( FD_ISSET( connfd, &rset ) ) //!> if 连接端口有信息          {              printf( "client get from server ...\n" );              memset( recv, 0, sizeof( recv ) );              n = read( connfd, recv, MAXLINE );              if( n == 0 )              {                  printf("Recv ok...\n");                  break;              }              else if( n == -1 )              {                  printf("Recv error...\n");                  break;              }              else              {                  lens = strlen( recv );                  recv[lens] = '\0';                  //!> 写到stdout                  write( STDOUT_FILENO, recv, MAXLINE );                  printf("\n");              }                    }                    //!> if 有stdin输入          if( FD_ISSET( fileno( fp ), &rset ) )   //!> if 有输入          {              //!> printf("client stdin ...\n");                            memset( send, 0, sizeof( send ) );              if( fgets( send, MAXLINE, fp ) == NULL )              {                  printf("End...\n");                  exit( EXIT_FAILURE );              }              else              {                  //!>if( str )                  lens = strlen( send );                  send[lens-1] = '\0';        //!> 减一的原因是不要回车字符                                  //!> 经验值:这一步非常重要的哦!!!!!!!!                  if( strcmp( send, "q" ) == 0 )                  {                      printf( "Bye..\n" );                      return;                  }                                    printf("Client send : %s\n", send);                  write( connfd, send, strlen( send ) );              }          }                }        }    int main( int argc, char ** argv )  {      //!> char * SERV_IP = "10.30.97.188";      char    buf[MAXLINE];      int     connfd;      struct sockaddr_in servaddr;            if( argc != 2 )      {          printf("Input server ip !\n");          exit( EXIT_FAILURE );      }            //!> 建立套接字      if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )      {          printf("Socket Error...\n" , errno );          exit( EXIT_FAILURE );      }        //!> 套接字信息      bzero(&servaddr, sizeof(servaddr));      servaddr.sin_family = AF_INET;      servaddr.sin_port = htons(SERV_PORT);      inet_pton(AF_INET, argv[1], &servaddr.sin_addr);            //!> 链接server      if( connect( connfd, ( struct sockaddr *  )&servaddr, sizeof( servaddr ) ) < 0 )      {          printf("Connect error..\n");          exit(EXIT_FAILURE);      }         /*else     {         printf("Connet ok..\n");     }*/        //!>      //!> send and recv      send_and_recv( connfd );            //!>         close( connfd );      printf("Exit\n");            return 0;  }  CODE_2 : server 是“子进程accept”,那么就是要比第一个简单一点,但是有一个新的问题就是,子进程要“抢”accept,那么必然存在一个“互斥”accept的问题,当然我么可以使用文件锁,但是效率比较低,而且在此处只是一个实验,所以姑且就使用“线程互斥量”,看起来有点不和谐,呵呵呵!~Server:[cpp] view plaincopyprint?/*     基本思路:     server预先创建几个子进程,由子进程进行互斥accept,     由于此处使用文件锁的效率会低一点,那么就使用互斥量     代替!     当然这是不协调的,这里只是简单处理! */    #include <stdio.h>  #include <unistd.h>  #include <stdlib.h>  #include <string.h>  #include <pthread.h>  #include <sys/select.h>  #include <sys/types.h>  #include <errno.h>  #include <sys/socket.h>  #include <netinet/in.h>  #include <sys/epoll.h>  #include <fcntl.h>    #define PORT            6000  #define MAXBACK 100  #define MAXLINE     1024  #define CHILD_NUM   10    pthread_mutex_t     g_mutex;        //!> 互斥量  pthread_mutexattr_t     g_mattr ;       //!> 属性    //!> 产生子进程及相关处理  //!>   void child_make( int i_num, int listen_fd )  {      int             i = 0;      pid_t       pid;      int         conn_fd;      int         n_read;      char        buf[MAXLINE];      struct sockaddr_in cliaddr;      int     len = sizeof( struct sockaddr_in );            if( ( pid = fork() ) > 0 )      {          return;      }            while( 1 )      {          pthread_mutex_lock( &g_mutex );     //!> 加锁                    if( ( conn_fd = accept( listen_fd, ( struct sockaddr *)&cliaddr , &len ) ) == -1 )          {              printf("Accept errnr! :%d\n", errno);              exit( EXIT_FAILURE );          }                    pthread_mutex_unlock( &g_mutex );   //!> 解锁                    if( ( n_read = read( conn_fd, buf, MAXLINE ) ) < 0 )          {              printf( "Read errnr! :%d \n", errno );              exit( EXIT_FAILURE );          }          else if( n_read == 0 )          {              continue;          }          else          {              while( buf[i] )              {                  buf[i] = toupper( buf[i] );                  i++;              }                            printf("Child %d done! \n", i_num);              printf("Child %d send %s\n", i_num, buf);              write( conn_fd, buf, strlen( buf ) );           //!> 写回给client          }                }  }    //!> MAIN PROCESS  //!>  int main( int argc, char ** argv )  {      int     i;      int         listen_fd;      int         conn_fd;      int     n_read;      char    buf[5];      struct sockaddr_in servaddr;            //!>下面需要设置进程间共享此锁      //!>       pthread_mutexattr_init( &g_mattr );      pthread_mutexattr_setpshared( &g_mattr, PTHREAD_PROCESS_SHARED );      pthread_mutex_init( &g_mutex, &g_mattr );   //!> 初始化            //!> server 套接口      //!>       bzero( &servaddr, sizeof( servaddr ) );      servaddr.sin_family = AF_INET;      servaddr.sin_addr.s_addr = htonl( INADDR_ANY );      servaddr.sin_port = htons( PORT );            //!> 建立套接字      if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )      {          printf("Socket Error...\n" , errno );          exit( EXIT_FAILURE );      }            //!> 绑定      //!>      if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( servaddr ) ) == -1 )      {          printf("Bind Error : %d\n", errno);          exit( EXIT_FAILURE );      }            //!> 监听      //!>       if( listen( listen_fd, MAXBACK ) == -1 )      {          printf("Listen Error : %d\n", errno);          exit( EXIT_FAILURE );      }            for( i = 0; i < CHILD_NUM; i++ )      {          child_make( i, listen_fd );      }            waitpid( 0 );               //!> 等待所有子进程而已            pthread_mutex_destroy( &g_mutex );  //!> 删除互斥灯            return 0;  }  Client:和上面的一样!!!
?

热点排行