xml地图|网站地图|网站标签 [设为首页] [加入收藏]
Linux网络编程 - TCP Socket 简单练习:线程池实现并发
分类:网络频道

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

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

服务器函数执行流程

main

init_system

creat_pthread_pool

child_work

thread_manager

task_manager

process_client

monitor

sys_clean

子进程accept

Makefile文件

[plain] view plain copy

 print?宝马娱乐在线 1宝马娱乐在线 2

  1. CC = gcc  
  2. TARGET = pthread_pool  
  3. SRC = pthread_pool.c base.c  
  4. OBJECT = pthread_pool.o  base.o  
  5. INCLUDES = -I./  
  6. LDFLAGS = -lpthread  
  7.   
  8. all:$(TARGET)  
  9.   
  10. $(OBJECT):$(SRC)  
  11.     $(CC) -c $(INCLUDES) ${SRC}  
  12.   
  13. $(TARGET):$(OBJECT)  
  14.     $(CC) -o $@ $(OBJECT) $(LDFLAGS)  
  15.   
  16. .PHONY:clean  
  17.   
  18. clean:  
  19.     @rm -rf $(OBJECT) $(TARGET) *~  

prethread:

服务器代码

主线程accept

头文件

[cpp] view plain copy

 print?宝马娱乐在线 3宝马娱乐在线 4

  1. #ifndef __PTHREAD_POOL_H__  
  2.   
  3. #define __PTHREAD_POOL_H__  
  4.   
  5. #include <stdio.h>  
  6. #include <pthread.h>  
  7. #include <stdlib.h>  
  8. #include <string.h>  
  9. #include <time.h>  
  10. #include <sys/syscall.h>  
  11. #include <sys/types.h>  
  12. #include <unistd.h>  
  13. #include <assert.h>  
  14. #include <sys/stat.h>  
  15. #include <sys/types.h>  
  16. #include <fcntl.h>  
  17. #include <sys/socket.h>  
  18. #include <netinet/in.h>  
  19. #include <arpa/inet.h>  
  20. #include <net/if.h>  
  21. #include <sys/ioctl.h>  
  22. #include <errno.h>  
  23.   
  24. #define THREAD_MAX_NUM  100     /* max number of thread. */  
  25. #define THREAD_DEF_NUM  20      /* by default ,number of thread. */  
  26. #define THREAD_MIN_NUM  5       /* min number of thread pool. */  
  27. #define LISNUM<span style="white-space:pre">  </span>5  
  28. #define PORT    9001  
  29. #define MAXBUF  1024  
  30.   
  31.   
  32. /* 
  33.  * *ds of the every task. make all task in a single link 
  34.  */  
  35. //任务结构节点,用于描述每个任务的具体属性  
  36. typedef struct task_node  
  37. {  
  38.     void *arg;                              /* fun arg. */  
  39.     void *(*fun)(void *);                   /* the real work of the task. */  
  40.     pthread_t       tid;            /* which thread exec this task. */  
  41.     int         work_id;        /* task id. */  
  42.     int         flag;           /* 1: assigned, 0: unassigned. */  
  43.     struct task_node    *next;  
  44.     pthread_mutex_t     mutex;          /* when modify this ds and exec the work,lock the task ds. */  
  45. } TASK_NODE;  
  46.   
  47.   
  48. /* 
  49.  * *the ds  of the task_queue 
  50.  */  
  51. //任务队列结构,用于控制整个任务队列  
  52. typedef struct task_queue  
  53. {  
  54.     pthread_mutex_t     mutex;  
  55.     pthread_cond_t      cond;   /* when no task, the manager thread wait for ;when a new task come, signal. */  
  56.     struct task_node    *head;  /* point to the task_link. */  
  57.     int         number; /* current number of task, include unassinged and assigned but no finished. */  
  58. } TASK_QUEUE_T;  
  59.   
  60.   
  61. /* 
  62.  * *the ds of every thread, make all thread in a double link queue. 
  63.  */  
  64. //线程结构节点,用于描述每个线程的具体属性  
  65. typedef struct pthread_node  
  66. {  
  67.     pthread_t       tid;    /* the pid of this thread in kernel,the value is  syscall return . */  
  68.     int         flag;   /*  1:busy, 0:free. */  
  69.     struct task_node    *work;  /*  if exec a work, which work. */  
  70.     struct pthread_node *next;  
  71.     struct pthread_node *prev;  
  72.     pthread_cond_t      cond;   /* when assigned a task, signal this child thread by manager. */  
  73.     pthread_mutex_t     mutex;  
  74. } THREAD_NODE;  
  75.   
  76.   
  77. /* 
  78.  * *the ds of the thread queue 
  79.  */  
  80. //线程队列结构,用于控制空闲线程队列和忙碌线程队列  
  81. typedef struct pthread_queue  
  82. {  
  83.     int         number; /* the number of thread in this queue. */  
  84.     struct pthread_node *head;  
  85.     struct pthread_node *rear;  
  86.     pthread_cond_t      cond;   /* when no idle thread, the manager wait for ,or when a thread return with idle, signal. */  
  87.     pthread_mutex_t     mutex;  
  88. } PTHREAD_QUEUE_T;  
  89.   
  90. //在pthread_poll()中定义的三个结构的指针  
  91. extern PTHREAD_QUEUE_T  *pthread_queue_idle;    /* the idle thread double link queue. */  
  92. extern PTHREAD_QUEUE_T  *pthread_queue_busy;    /* the work thread double link queue. */  
  93. extern TASK_QUEUE_T *task_queue_head;       /* the task queuee single link list. */  
  94.   
  95. void *child_work( void *ptr );  
  96.   
  97. void create_pthread_pool( void );  
  98.   
  99. void init_system( void );  
  100.   
  101. void *thread_manager( void *ptr );  
  102.   
  103. void *prcoess_client( void *ptr );  
  104.   
  105. void *task_manager( void *ptr );  
  106.   
  107. void *monitor( void *ptr );  
  108.   
  109. void sys_clean( void );  
  110.   
  111. #endif  

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

基础函数

[cpp] view plain copy

 print?宝马娱乐在线 5宝马娱乐在线 6

  1. #include "pthread_pool.h"  
  2.   
  3.   
  4. /* 
  5.  * *child_work:the code exec in child thread 
  6.  * *ptr: the ds of thread_node of current thread. 
  7.  * *return :nothing.void * just avoid warning. 
  8.  */  
  9. /* 
  10.     child_work为创建的线程执行的函数 
  11.     主要用来等待线程属性状态的变化,来判断是否有任务要执行 
  12.     并且判断线程的工作状态的变化,来决定加入哪个线程队列(空闲还是忙碌) 
  13. */  
  14. void *  
  15. child_work( void *ptr )  
  16. {  
  17.     //这里的ptr为(void *) &temp[i]  
  18.     THREAD_NODE * self = (THREAD_NODE *) ptr;  
  19.   
  20.     /*modify the tid attribute the first time exec */  
  21.     pthread_mutex_lock( &self->mutex );  
  22.     self->tid = syscall( SYS_gettid );//获得线程自身id  
  23.     pthread_mutex_unlock( &self->mutex );  
  24.   
  25.     while ( 1 )  
  26.     {  
  27.         pthread_mutex_lock( &self->mutex );  
  28.   
  29.         /*if no task exec,blocked */  
  30.         /*  
  31.             关键的一句话 
  32.             从线程的属性struct task_node *work(即为self->work) 
  33.             判断是否已给当前线程分配任务 
  34.         */  
  35.         //如果该线程尚没有分配任务,则通过条件变量阻塞等待条件变量self->cond  
  36.         if ( NULL == self->work )   
  37.         {  
  38.             pthread_cond_wait( &self->cond, &self->mutex );  
  39.         }  
  40.   
  41.         pthread_mutex_lock( &self->work->mutex );  
  42.   
  43.         /*execute the real work.  
  44.             开始执行任务 
  45.         */  
  46.         self->work->fun( self->work->arg );  
  47.   
  48.         /*after finished the work  
  49.             任务执行完后,撤销任务的属性,并销毁任务本身,释放其占用的资源 
  50.         */  
  51.         self->work->fun       = NULL;  
  52.         self->work->flag  = 0;  
  53.         self->work->tid       = 0;  
  54.         self->work->next  = NULL;  
  55.   
  56.         free( self->work->arg );  
  57.   
  58.         pthread_mutex_unlock( &self->work->mutex ); /* unlock the task */  
  59.         pthread_mutex_destroy( &self->work->mutex );  
  60.   
  61.         /*free the task space */  
  62.         free( self->work );  
  63.   
  64.         /*make self thread no work */  
  65.         self->work   = NULL;  
  66.         self->flag   = 0;  
  67.   
  68.   
  69.         /* 
  70.          * *get new task from the task_link if not NULL. 
  71.          * *there no idle thread if there are task to do. 
  72.          * *if on task ,make self idle and add to the idle queue. 
  73.          */  
  74.         /* 
  75.             执行完上一个任务后,查看任务队列中是否还有任务      
  76.         */  
  77.         pthread_mutex_lock( &task_queue_head->mutex );  
  78.         if ( task_queue_head->head != NULL )//如果有任务,则分配任务  
  79.         {  
  80.             TASK_NODE * temp = task_queue_head->head;  
  81.   
  82.             /*get the first task */  
  83.             task_queue_head->head = task_queue_head->head->next;  
  84.   
  85.             /*modify self thread attribute */  
  86.             self->flag   = 1;  
  87.             self->work   = temp;  
  88.             temp->tid    = self->tid;  
  89.             temp->next   = NULL;  
  90.             temp->flag   = 1;  
  91.   
  92.             task_queue_head->number--;  
  93.   
  94.             pthread_mutex_unlock( &task_queue_head->mutex );  
  95.   
  96.             pthread_mutex_unlock( &self->mutex );  
  97.   
  98.             continue;  
  99.         }  
  100.         else //如果没有任务,从忙碌线程队列中删除此线程,将其加入空闲线程队列中    
  101.         {  
  102.             /*no task need to exec, add self to idle queue and del from busy queue */  
  103.             pthread_mutex_unlock( &task_queue_head->mutex );  
  104.   
  105.             pthread_mutex_lock( &pthread_queue_busy->mutex );  
  106.   
  107.             /*self is the last execte thread  
  108.                 如果此线程是忙碌的线程队列中的仅剩的一个线程 
  109.             */  
  110.             if ( pthread_queue_busy->head == self  
  111.                  && pthread_queue_busy->rear == self )  
  112.             {  
  113.                 pthread_queue_busy->head = pthread_queue_busy->rear = NULL;  
  114.                 self->next           = self->prev = NULL;  
  115.             }  
  116.             /*the first one thread in busy queue  
  117.                 如果此线程是忙碌的线程队列中的第一个线程 
  118.             */  
  119.             else if ( pthread_queue_busy->head == self  
  120.                   && pthread_queue_busy->rear != self )  
  121.             {  
  122.                 pthread_queue_busy->head = pthread_queue_busy->head->next;  
  123.                 pthread_queue_busy->head->prev    = NULL;  
  124.   
  125.                 self->next = self->prev = NULL;  
  126.             }  
  127.             /*the last one thread in busy queue  
  128.                 如果此线程是忙碌的线程队列中的末尾的一个线程 
  129.             */  
  130.             else if ( pthread_queue_busy->head != self  
  131.                   && pthread_queue_busy->rear == self )  
  132.             {  
  133.                 pthread_queue_busy->rear = pthread_queue_busy->rear->prev;  
  134.                 pthread_queue_busy->rear->next    = NULL;  
  135.   
  136.                 self->next = self->prev = NULL;  
  137.             }  
  138.             /*middle one  
  139.                 如果此线程是忙碌的线程队列中的中间的某个线程 
  140.             */  
  141.             else{  
  142.                 self->next->prev  = self->prev;  
  143.                 self->prev->next  = self->next;  
  144.                 self->next       = self->prev = NULL;  
  145.             }  
  146.   
  147.             pthread_mutex_unlock( &pthread_queue_busy->mutex );  
  148.   
  149.             /*add self to the idle queue  
  150.                 将此线程加入空闲线程队列中 
  151.             */  
  152.             pthread_mutex_lock( &pthread_queue_idle->mutex );  
  153.   
  154.             /*now the idle queue is empty  
  155.                 判断空闲线程队列的情况,根据不同的情况将此线程加入不同的位置 
  156.             */  
  157.             if ( pthread_queue_idle->head == NULL  
  158.                  || pthread_queue_idle->rear == NULL )  
  159.             {  
  160.                 pthread_queue_idle->head = pthread_queue_idle->rear = self;  
  161.                 self->next           = self->prev = NULL;  
  162.             }else  {  
  163.                 self->next       = pthread_queue_idle->head;  
  164.                 self->prev       = NULL;  
  165.                 self->next->prev  = self;  
  166.   
  167.                 pthread_queue_idle->head = self;  
  168.                 pthread_queue_idle->number++;  
  169.             }  
  170.   
  171.             pthread_mutex_unlock( &pthread_queue_idle->mutex );  
  172.   
  173.             pthread_mutex_unlock( &self->mutex );  
  174.   
  175.             /*signal have idle thread  
  176.                 告知阻塞等待条件变量pthread_queue_idle->cond的位置已有空闲线程 
  177.             */  
  178.             pthread_cond_signal( &pthread_queue_idle->cond );  
  179.         }  
  180.     }  
  181. }  
  182.   
  183.   
  184. /* 
  185.  * *create thread pool when the system on, and thread number is THREAD_DEF_NUM. 
  186.  * *when init, initial all the thread into a double link queue and all wait fo self->cond. 
  187.  */  
  188. void  
  189. create_pthread_pool( void )  
  190. {  
  191.     //分配线程节点  
  192.     THREAD_NODE * temp =  
  193.         (THREAD_NODE *) malloc( sizeof(THREAD_NODE) * THREAD_DEF_NUM );  
  194.   
  195.     if ( temp == NULL )  
  196.     {  
  197.         printf( " malloc failuren" );  
  198.         exit( EXIT_FAILURE );  
  199.     }  
  200.   
  201.     /*init as a double link queue  
  202.         初始化为双向链式队列 
  203.     */  
  204.     int i;  
  205.   
  206.     //THREAD_DEF_NUM为线程池中线程的最大数量  
  207.     //for循环开始创建线程池  
  208.     for ( i = 0; i < THREAD_DEF_NUM; i++ )  
  209.     {  
  210.         temp[i].tid     = i + 1;  
  211.         temp[i].work    = NULL;  
  212.         temp[i].flag    = 0;  
  213.   
  214.         if ( i == THREAD_DEF_NUM - 1 )  
  215.             temp[i].next = NULL;  
  216.   
  217.         if ( i == 0 )  
  218.             temp[i].prev = NULL;  
  219.   
  220.         //双向链表的体现  
  221.         temp[i].prev    = &temp[i - 1];  
  222.         temp[i].next    = &temp[i + 1];  
  223.   
  224.         pthread_cond_init( &temp[i].cond, NULL );  
  225.         pthread_mutex_init( &temp[i].mutex, NULL );  
  226.   
  227.         /*create this thread  
  228.             在此创建线程,各个线程执行的函数为child_work 
  229.         */  
  230.         pthread_create( &temp[i].tid, NULL, child_work, (void *) &temp[i] );  
  231.     }  
  232.   
  233.     /*modify the idle thread queue attribute  
  234.         修改空闲线程队列的属性 
  235.     */  
  236.     pthread_mutex_lock( &pthread_queue_idle->mutex );  
  237.   
  238.     pthread_queue_idle->number   = THREAD_DEF_NUM;  
  239.     //此句就将刚创建的那些线程给空闲线程队列  
  240.     pthread_queue_idle->head = &temp[0];  
  241.     pthread_queue_idle->rear = &temp[THREAD_DEF_NUM - 1];  
  242.   
  243.     pthread_mutex_unlock( &pthread_queue_idle->mutex );  
  244. }  
  245.   
  246.   
  247. /* 
  248.  * *init_system :init the system glob pointor. 
  249.  */  
  250. void  
  251. init_system( void )  
  252. {  
  253.     /*init the pthread_queue_idle  
  254.         初始化空闲线程队列,采用的是普通的双向链式结构(未循环) 
  255.     */  
  256.     pthread_queue_idle =  
  257.         (PTHREAD_QUEUE_T *) malloc( sizeof(PTHREAD_QUEUE_T) );  
  258.   
  259.     pthread_queue_idle->number   = 0;  
  260.     pthread_queue_idle->head = NULL;  
  261.     pthread_queue_idle->rear = NULL;  
  262.     pthread_mutex_init( &pthread_queue_idle->mutex, NULL );  
  263.     pthread_cond_init( &pthread_queue_idle->cond, NULL );  
  264.   
  265.     /*init the pthread_queue_busy  
  266.         初始化空闲线程队列,采用的是普通的双向链式结构(未循环) 
  267.     */  
  268.     pthread_queue_busy =  
  269.         (PTHREAD_QUEUE_T *) malloc( sizeof(PTHREAD_QUEUE_T) );  
  270.   
  271.     pthread_queue_busy->number   = 0;  
  272.     pthread_queue_busy->head = NULL;  
  273.     pthread_queue_busy->rear = NULL;  
  274.     pthread_mutex_init( &pthread_queue_busy->mutex, NULL );  
  275.     pthread_cond_init( &pthread_queue_busy->cond, NULL );  
  276.   
  277.     /*init the task_queue_head  
  278.         初始化任务队列,采用单向链表 
  279.     */  
  280.     task_queue_head = (TASK_QUEUE_T *) malloc( sizeof(TASK_QUEUE_T) );  
  281.   
  282.     task_queue_head->head    = NULL;  
  283.     task_queue_head->number = 0;  
  284.     pthread_cond_init( &task_queue_head->cond, NULL );  
  285.     pthread_mutex_init( &task_queue_head->mutex, NULL );  
  286.   
  287.     /*create thread poll  
  288.         创建线程池 
  289.     */  
  290.     create_pthread_pool();  
  291. }  
  292.   
  293.   
  294. /* 
  295.  * *thread_manager:code exec in manager thread. 
  296.  *               block on task_queue_head->cond when no task come. 
  297.  *               block on pthread_queue_idle->cond when no idle thread 
  298.  **ptr:no used ,in order to avoid warning. 
  299.  **return :nothing. 
  300.  */  
  301.   
  302. void *  
  303. thread_manager( void *ptr )  
  304. {  
  305.     while ( 1 )  
  306.     {  
  307.         THREAD_NODE * temp_thread   = NULL;  
  308.         TASK_NODE   * temp_task = NULL;  
  309.   
  310.         /* 
  311.          * *get a new task, and modify the task_queue. 
  312.          * *if no task block om task_queue_head->cond. 
  313.          */  
  314.         pthread_mutex_lock( &task_queue_head->mutex );  
  315.         //如果任务队列为空,则阻塞等待条件变量task_queue_head->cond  
  316.         if ( task_queue_head->number == 0 )  
  317.             pthread_cond_wait( &task_queue_head->cond,  
  318.                        &task_queue_head->mutex );  
  319.   
  320.         //如果不为空,则开始准备分配任务,并修改任务队列属性  
  321.         temp_task       = task_queue_head->head;  
  322.         task_queue_head->head    = task_queue_head->head->next;  
  323.         task_queue_head->number--;  
  324.   
  325.         pthread_mutex_unlock( &task_queue_head->mutex );  
  326.   
  327.   
  328.         /* 
  329.          * *get a new idle thread, and modify the idle_queue. 
  330.          * *if no idle thread, block on pthread_queue_idle->cond. 
  331.          */  
  332.         //有了任务之后,开始判断是否有空闲线程  
  333.         pthread_mutex_lock( &pthread_queue_idle->mutex );  
  334.           
  335.         //如果没有空闲线程,则阻塞等待条件变量pthread_queue_idle->cond  
  336.         if ( pthread_queue_idle->number == 0 )  
  337.             pthread_cond_wait( &pthread_queue_idle->cond,  
  338.                        &pthread_queue_idle->mutex );  
  339.                          
  340.         //如果有空闲线程则取出一个空闲线程,然后修改空闲线程队列属性  
  341.         temp_thread = pthread_queue_idle->head;  
  342.   
  343.         /*if this is the last idle thread ,modiry the head and rear pointor */  
  344.         if ( pthread_queue_idle->head == pthread_queue_idle->rear )  
  345.         {  
  346.             pthread_queue_idle->head = NULL;  
  347.             pthread_queue_idle->rear = NULL;  
  348.         }  
  349.         /*if idle thread number>2, get the first one,modify the head pointor  */  
  350.         else{  
  351.             pthread_queue_idle->head = pthread_queue_idle->head->next;  
  352.             pthread_queue_idle->head->prev    = NULL;  
  353.         }  
  354.   
  355.         pthread_queue_idle->number--;//将空闲线程队列数量减一  
  356.   
  357.         pthread_mutex_unlock( &pthread_queue_idle->mutex );  
  358.   
  359.         /*modify the  task attribute.  
  360.             修改取出的线程的线程结构属性和相关的任务结构属性 
  361.         */  
  362.         pthread_mutex_lock( &temp_task->mutex );  
  363.   
  364.         temp_task->tid   = temp_thread->tid;  
  365.         temp_task->next = NULL;  
  366.         temp_task->flag = 1;  
  367.   
  368.         pthread_mutex_unlock( &temp_task->mutex );  
  369.   
  370.         /*modify the idle thread attribute. */  
  371.         pthread_mutex_lock( &temp_thread->mutex );  
  372.   
  373.         temp_thread->flag    = 1;  
  374.         temp_thread->work    = temp_task;  
  375.         temp_thread->next    = NULL;  
  376.         temp_thread->prev    = NULL;  
  377.   
  378.         pthread_mutex_unlock( &temp_thread->mutex );  
  379.   
  380.         /*add the thread assinged task to the busy queue. */  
  381.         //将已分配任务的线程加入忙碌线程队列中  
  382.         pthread_mutex_lock( &pthread_queue_busy->mutex );  
  383.   
  384.         /*if this is the first one in busy queue */  
  385.         if ( pthread_queue_busy->head == NULL )  
  386.         {  
  387.             pthread_queue_busy->head = temp_thread;  
  388.             pthread_queue_busy->rear = temp_thread;  
  389.             temp_thread->prev        = temp_thread->next = NULL;  
  390.         }else  {  
  391.             /*insert in thre front of the queue */  
  392.             pthread_queue_busy->head->prev    = temp_thread;  
  393.             temp_thread->prev        = NULL;  
  394.             temp_thread->next        = pthread_queue_busy->head;  
  395.             pthread_queue_busy->head = temp_thread;  
  396.             pthread_queue_busy->number++;  
  397.         }  
  398.         pthread_mutex_unlock( &pthread_queue_busy->mutex );  
  399.   
  400.         /*signal the child thread to exec the work */  
  401.         //告知阻塞等待条件变量temp_thread->cond的位置,开始执行任务  
  402.         pthread_cond_signal( &temp_thread->cond );  
  403.     }  
  404. }  
  405.   
  406.   
  407. /* 
  408.  * *code to process the new client in every chilld pthead. 
  409.  * *ptr: the fd come from listen thread that can communicate to the client. 
  410.  * *return:nothing. void * only used to avoid waring. 
  411.  */  
  412. //用来处理任务的函数  
  413. void *  
  414. prcoess_client( void *ptr )  
  415. {  
  416.     int net_fd;  
  417.     net_fd = atoi( (char *) ptr );  
  418.   
  419.     socklen_t   len;  
  420.     char        buf[MAXBUF + 1];  
  421.     /*下面是select用到的变量的定义 */  
  422.     fd_set      rfds;  
  423.     struct timeval  tv;  
  424.     int     retval;  
  425.     int     maxfd = -1;  
  426.   
  427.     while ( 1 )  
  428.     {  
  429.         FD_ZERO( &rfds );                                                       /* 初始化rfds为空 */  
  430.         FD_SET( 0, &rfds );                                                     /* 将标准输入的描述符0加入到集合rfds中 */  
  431.         FD_SET( net_fd, &rfds );                                                /* 将net_fd加入到集合rfds中 */  
  432.         maxfd       = net_fd + 1;  
  433.         tv.tv_sec   = 1;                                                    /* 阻塞等待时间为1s */  
  434.         tv.tv_usec  = 0;  
  435.   
  436.         retval = select( maxfd, &rfds, NULL, NULL, &tv );                       /* 多路复用,同时监测描述符0和net_fd */  
  437.         if ( retval == -1 )                                                     /* select函数执行出错 */  
  438.         {  
  439.             perror( "select" );  
  440.             exit( EXIT_FAILURE );  
  441.         }else if ( retval == 0 )                                                /* select函数执行超时 */  
  442.             continue;  
  443.         else{ /*有描述符引起异常 */  
  444.             if ( FD_ISSET( 0, &rfds ) )                                     /* 判断是不是标准输入0引起的异常 */  
  445.             {  
  446.                 bzero( buf, sizeof(buf) );                              /* 清空buf */  
  447.                 fgets( buf, sizeof(buf) - 1, stdin );                   /* 从终端接收输入 */  
  448.   
  449.                 if ( !strncasecmp( buf, "quit", 4 ) )                   /* 判断是否为退出 */  
  450.                 {  
  451.                     printf( "i will close the connect!n" );  
  452.                     close( net_fd );  
  453.                     goto clean;  
  454.                 }  
  455.   
  456.                 len = send( net_fd, buf, strlen( buf ) - 1, 0 );        /* 向客户端发送消息 */  
  457.                 if ( len > 0 )  
  458.                 {  
  459.                     printf( "send successful,%d byte send!n", len );  
  460.                 }else  {  
  461.                     printf( "message '%s' send failure !n", buf );  
  462.                     printf( "errno code is %d, errno message is '%s'n", errno, strerror( errno ) );  
  463.                     close( net_fd );  
  464.                     goto clean;  
  465.                 }  
  466.             }  
  467.   
  468.             if ( FD_ISSET( net_fd, &rfds ) )                        /* 判断是不是net_fd引起的异常 */  
  469.             {  
  470.                 bzero( buf, sizeof(buf) );  
  471.                 len = recv( net_fd, buf, sizeof(buf) - 1, 0 );  /* 从客户端接收消息 */  
  472.                 if ( len > 0 )  
  473.                     printf( "message recv successful : '%s', %d Byte recvn", buf, len );  
  474.                 else if ( len < 0 )  
  475.                 {  
  476.                     printf( "recv failure !nerrno code is %d, errno message is '%s'n", errno, strerror( errno ) );  
  477.                     close( net_fd );  
  478.                     goto clean;  
  479.                 }else  { /* 如果客户端已关闭 */  
  480.                     printf( "the other one close quitn" );  
  481.                     close( net_fd );  
  482.                     return;  
  483.                 }  
  484.             }  
  485.         }  
  486.     }  
  487.     close( net_fd );  
  488.     return;  
  489.   
  490. clean:  
  491.     sys_clean();  
  492. }  
  493.   
  494.   
  495. /* 
  496.  * *task_manager: get new task and add to the tail of the task_link. 
  497.  * *ptr: no used. just avoid warning. 
  498.  * *return:nothing. 
  499.  */  
  500. //用来监听客户端的连接,产生任务  
  501. void *  
  502. task_manager( void *ptr )  
  503. {  
  504.     int listen_fd;  
  505.   
  506.     if ( -1 == (listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) )  
  507.     {  
  508.         perror( "socket" );  
  509.         goto clean;  
  510.     }  
  511.   
  512.     struct ifreq ifr;  
  513.   
  514.     //eno16777736类似于eth0,在Linux系统中可以修改为eth0  
  515.     strcpy( ifr.ifr_name, "eno16777736" );  
  516.     //获取eno16777736的ip地址  
  517.     if ( ioctl( listen_fd, SIOCGIFADDR, &ifr ) < 0 )  
  518.     {  
  519.         perror( "ioctl" );  
  520.         goto clean;  
  521.     }  
  522.   
  523.     struct sockaddr_in myaddr;  
  524.   
  525.     myaddr.sin_family   = AF_INET;  
  526.     myaddr.sin_port     = htons( PORT );//PORT为9001,在头文件中设置,是全局变量  
  527.     myaddr.sin_addr.s_addr  =  
  528.         ( (struct sockaddr_in *) &(ifr.ifr_addr) )->sin_addr.s_addr;  
  529.   
  530. <span style="white-space:pre">    </span>//输出ip和port信息  
  531. <span style="white-space:pre">    </span>printf("server_ip = %snserver_port = %dnlisnum = %dn", inet_ntoa(myaddr.sin_addr), PORT, LISNUM);  
  532.   
  533.     //绑定IP地址和端口port  
  534.     if ( -1 == bind( listen_fd, (struct sockaddr *) &myaddr, sizeof(myaddr) ) )  
  535.     {  
  536.         perror( "bind" );  
  537.         goto clean;  
  538.     }  
  539.     //监听  
  540.     if ( -1 == listen( listen_fd, 5 ) )  
  541.     {  
  542.         perror( "listen" );  
  543.         goto clean;  
  544.     }  
  545.   
  546.     /*i is the id of the task */  
  547.     int i;  
  548.     //开始接受客户端的连接,产生任务  
  549.     for ( i = 1; ; i++ )  
  550.     {  
  551.         int newfd;  
  552.         struct sockaddr_in  client;  
  553.         socklen_t len = sizeof(client);  
  554.   
  555.         if ( -1 ==  
  556.              (newfd = accept( listen_fd, (struct sockaddr *) &client, &len ) ) )  
  557.         {  
  558.             perror( "accept" );  
  559.             goto clean;  
  560.         }  
  561.         /* 打印本次连接的客户端的地址信息 inet_ntoa  ntohs */  
  562.         printf( "server: got connection from %s, port %d, socket %dn", inet_ntoa( client.sin_addr ), ntohs( client.sin_port ), newfd );  
  563.   
  564.         //开始将产生的新任务加入任务队列之中  
  565.         TASK_NODE   * temp      = NULL;  
  566.         TASK_NODE   * newtask   = (TASK_NODE *) malloc( sizeof(TASK_NODE) );  
  567.         if ( newtask == NULL )  
  568.         {  
  569.             printf( "malloc error" );  
  570.             goto clean;  
  571.         }  
  572.   
  573.         /* 
  574.          * *initial the attribute of the task. 
  575.          * *because this task havn't add to system,so,no need lock the mutex. 
  576.          */  
  577.   
  578.         newtask->arg = (void *) malloc( 128 );  
  579.   
  580.         memset( newtask->arg, '', 128 );  
  581.         //任务执行的参数为连接的客户端的socket描述符  
  582.         sprintf( newtask->arg, "%d", newfd );  
  583.         //新任务的处理函数均为prcoess_client,newfd即为函数prcoess_client的参数  
  584.         newtask->fun     = prcoess_client;  
  585.         newtask->tid     = 0;  
  586.         newtask->work_id = i;  
  587.         newtask->next        = NULL;  
  588.         pthread_mutex_init( &newtask->mutex, NULL );  
  589.   
  590.         /*add new task to task_link */  
  591.         pthread_mutex_lock( &task_queue_head->mutex );  
  592.   
  593.         /*find the tail of the task link and add the new one to tail  
  594.             开始将产生的新任务加入到任务队列中 
  595.         */  
  596.         temp = task_queue_head->head;  
  597.   
  598.         if ( temp == NULL )  
  599.         {  
  600.             task_queue_head->head = newtask;  
  601.         }else  {  
  602.             while ( temp->next != NULL )  
  603.                 temp = temp->next;  
  604.   
  605.             temp->next = newtask;  
  606.         }  
  607.         task_queue_head->number++;//任务队列数量加一  
  608.   
  609.         pthread_mutex_unlock( &task_queue_head->mutex );  
  610.   
  611.         /*signal the manager thread , task coming  
  612.             告知阻塞等待条件变量task_queue_head->cond的位置,已有未执行的任务 
  613.         */  
  614.         pthread_cond_signal( &task_queue_head->cond );  
  615.     }  
  616.   
  617.     return;  
  618.   
  619. clean:  
  620.     sys_clean();  
  621. }  
  622.   
  623.   
  624. /* 
  625.  * *monitor: get the system info 
  626.  * *ptr: not used ,only to avoid warning for pthread_create 
  627.  * *return: nothing. 
  628.  */  
  629. //用来输出哪些线程在工作  
  630. void *  
  631. monitor( void *ptr )  
  632. {  
  633.     /*in order to prevent warning. */  
  634.     ptr = ptr;  
  635.   
  636.     THREAD_NODE * temp_thread = NULL;  
  637.   
  638.     while ( 1 )  
  639.     {  
  640.         pthread_mutex_lock( &pthread_queue_busy->mutex );  
  641.   
  642.         /*output the busy thread works one by one */  
  643.         temp_thread = pthread_queue_busy->head;  
  644.   
  645.         printf( "n*******************************n" );  
  646.         while ( temp_thread )  
  647.         {  
  648.             printf( "thread %ld is  execute work_number %dn",             
  649.                 temp_thread->tid, temp_thread->work->work_id );  
  650.             temp_thread = temp_thread->next;  
  651.         }  
  652.         printf( "*******************************nn" );  
  653.   
  654.         pthread_mutex_unlock( &pthread_queue_busy->mutex );  
  655.   
  656.         sleep( 10 );  
  657.     }  
  658.   
  659.     return;  
  660. }  
  661.   
  662.   
  663. /* 
  664.  * *sys_clean: clean the system . 
  665.  * *this function code need to do to make it more stronger. 
  666.  */  
  667. //清理函数  
  668. void  
  669. sys_clean( void )  
  670. {  
  671.     printf( "the system exit abnormallyn" );  
  672.     exit( EXIT_FAILURE );  
  673. }  

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

主函数

[cpp] view plain copy

 print?宝马娱乐在线 7宝马娱乐在线 8

  1. #include "pthread_pool.h"  
  2.   
  3. //定义三个结构的指针  
  4. PTHREAD_QUEUE_T * pthread_queue_idle;           /* the idle thread double link queue. */  
  5. PTHREAD_QUEUE_T *pthread_queue_busy;            /* the work thread double link queue. */  
  6. TASK_QUEUE_T    *task_queue_head;               /* the task queuee single link list. */  
  7.   
  8. int  
  9. main( int argc, char *argv[] )  
  10. {  
  11.     pthread_t thread_manager_tid, task_manager_tid, monitor_id;  
  12.   
  13.     //初始化空闲线程、在工作线程和要完成的任务  
  14.     init_system();  
  15.   
  16.     //创建线程池管理线程、创建任务管理线程和线程状态监视线程  
  17.     pthread_create( &thread_manager_tid, NULL, thread_manager, NULL );      /* create thread to manage the thread pool. */  
  18.     pthread_create( &task_manager_tid, NULL, task_manager, NULL );          /* create thread recive task from client. */  
  19.     pthread_create( &monitor_id, NULL, monitor, NULL );                     /* create thread to monitor the system info. */  
  20.   
  21.     //等待线程退出  
  22.     pthread_join( thread_manager_tid, NULL );  
  23.     pthread_join( task_manager_tid, NULL );  
  24.     pthread_join( monitor_id, NULL );  
  25.   
  26.     //清理服务器,准备退出主函数  
  27.     sys_clean();  
  28.   
  29.     return(0);  
  30. }  

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

客户端代码

[cpp] view plain copy

 print?宝马娱乐在线 9宝马娱乐在线 10

  1. /************************************************************************* 
  2.     > File Name: socket_select_client.c 
  3.     > Author: genglut 
  4.     > Mail: genglut@163.com 
  5.     > Created Time: 2014年12月22日 星期一 18时06分06秒 
  6.  ************************************************************************/  
  7.   
  8. #include <stdio.h>  
  9. #include <string.h>  
  10. #include <errno.h>  
  11. #include <sys/socket.h>  
  12. #include <resolv.h>  
  13. #include <stdlib.h>  
  14. #include <netinet/in.h>  
  15. #include <arpa/inet.h>  
  16. #include <unistd.h>  
  17.   
  18. #define MAXBUF 1024  
  19.   
  20. int main(int argc, char *argv[])  
  21. {  
  22.     int sockfd;  
  23.     socklen_t len;  
  24.     struct sockaddr_in server_addr;  
  25.     char buf[MAXBUF + 1];  
  26.   
  27.     //下面是select用到的变量的定义  
  28.     fd_set rfds;  
  29.     struct timeval tv;  
  30.     int retval;  
  31.     int maxfd = -1;  
  32.       
  33.     if(argc != 3)  
  34.     {  
  35.         printf("error failure, it must be:ntt%s IP port n", argv[0]);  
  36.         exit(EXIT_FAILURE);  
  37.     }  
  38.   
  39.     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
  40.     {  
  41.         perror("socket");  
  42.         exit(EXIT_FAILURE);  
  43.     }  
  44.   
  45.     bzero(&server_addr, sizeof(server_addr));  
  46.     server_addr.sin_family = AF_INET;  
  47.     server_addr.sin_port = htons(atoi(argv[2]));  
  48.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);  
  49.   
  50.     if(connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)  
  51.     {  
  52.         perror("connect");  
  53.         exit(EXIT_FAILURE);  
  54.     }  
  55.   
  56.     printf("already connected to server %sn", argv[1]);  
  57.       
  58.       
  59.     while(1)  
  60.     {  
  61.         FD_ZERO(&rfds);//初始化rfds为空  
  62.         FD_SET(0, &rfds);//将标准输入的描述符0加入到集合rfds中  
  63.         FD_SET(sockfd, &rfds);//将newfd加入到集合rfds中  
  64.         maxfd = sockfd + 1;  
  65.         tv.tv_sec = 1;//阻塞等待时间为1s  
  66.         tv.tv_usec = 0;  
  67.           
  68.         retval = select(maxfd, &rfds, NULL, NULL, &tv);//多路复用,同时监测描述符0和newfd  
  69.           
  70.         if(retval == -1)//select函数执行出错  
  71.         {  
  72.             perror("select");  
  73.             exit(EXIT_FAILURE);  
  74.         }  
  75.         else if(retval == 0)//select函数执行超时  
  76.             continue;  
  77.         else//有描述符引起异常  
  78.         {  
  79.             if(FD_ISSET(0, &rfds))//判断是不是标准输入0引起的异常  
  80.             {  
  81.                 bzero(buf, sizeof(buf));//清空buf  
  82.                 fgets(buf, sizeof(buf)-1, stdin);//从终端接收输入  
  83.   
  84.                 if(!strncasecmp(buf, "quit", 4))//判断是否为退出  
  85.                 {  
  86.                     printf("i will quit!n");  
  87.                     break;  
  88.                 }  
  89.                   
  90.                 len = send(sockfd, buf, strlen(buf)-1, 0);//向客户端发送消息  
  91.                 if(len > 0)  
  92.                 {  
  93.                     printf ("send successful,%d byte send!n",len);  
  94.                 }  
  95.                 else  
  96.                 {  
  97.                     printf("message '%s' send failure !n", buf);  
  98.                     printf("errno code is %d, errno message is '%s'n", errno, strerror(errno));  
  99.                     break;  
  100.                 }                     
  101.             }  
  102.               
  103.             if(FD_ISSET(sockfd, &rfds))//判断是不是newfd引起的异常  
  104.             {  
  105.                 bzero(buf, sizeof(buf));  
  106.                 len = recv(sockfd, buf, sizeof(buf)-1, 0);//从客户端接收消息  
  107.                 if(len > 0 )  
  108.                     printf("message recv successful : '%s', %d Byte recvn", buf, len);  
  109.                 else if(len < 0)  
  110.                 {  
  111.                     printf("recv failure !nerrno code is %d, errno message is '%s'n", errno, strerror(errno));  
  112.                     break;  
  113.                 }  
  114.                 else//如果客户端已关闭  
  115.                 {  
  116.                     printf("the other one close, quitn");  
  117.                     break;  
  118.                 }                     
  119.             }  
  120.         }     
  121.     }  
  122.       
  123.     close(sockfd);  
  124.     printf("i quited!n");  
  125.     return 0;  
  126. }  

话不多说,贴上代码:

运行结果

Server:

服务器:

[cpp] view plain copy

 print?宝马娱乐在线 11宝马娱乐在线 12

  1. $ ./pthread_pool  
  2.   
  3. -----------------------------------------  
  4. -----------------------------------------  
  5.   
  6. server_ip = 172.18.229.60  
  7. server_port = 9001  
  8. lisnum = 5  
  9.   
  10. -----------------------------------------  
  11. -----------------------------------------  
  12.   
  13. server: got connection from 172.18.229.60, port 56023, socket 4  
  14. message recv successful : 'hello', 5 Byte recv  
  15.   
  16. -----------------------------------------  
  17. thread 40159 is  execute work_number 1  
  18. -----------------------------------------  
  19.   
  20. server: got connection from 172.18.229.60, port 56024, socket 5  
  21. message recv successful : 'hi', 2 Byte recv  
  22. the other one close quit  
  23.   
  24. -----------------------------------------  
  25. thread 40159 is  execute work_number 1  
  26. -----------------------------------------  
  27.   
  28. the other one close quit  
  1. /* 
  2.     基本思路: 
  3.     server预先创建几个子进程,他们都在各自与server独立链接的pipe socket等待数据, 
  4.     server调用accept,然后遍历子进程,将connfd通过pipe给字进程处理! 
  5.     一旦子进程结束就返回任意内容告知已经处理结束!那么server将其状态位置为闲置!!! 
  6.     注意:这里应该使用select或者epoll,因为server接受的不仅仅是listen的msg,还有child 
  7.     处理完成返回的标志信息!   
  8. */  
  9.   
  10. #include <stdio.h>   
  11. #include <unistd.h>   
  12. #include <stdlib.h>   
  13. #include <string.h>   
  14. #include <sys/select.h>   
  15. #include <sys/types.h>   
  16. #include <errno.h>   
  17. #include <sys/socket.h>   
  18. #include <netinet/in.h>   
  19. #include <sys/epoll.h>   
  20. #include <fcntl.h>   
  21.   
  22. #define PORT            6000   
  23. #define MAXBACK 100   
  24. #define MAXLINE     1024   
  25. #define CHILD_NUM   10   
  26.   
  27. typedef struct child_process  
  28. {  
  29.     pid_t       s_pid;          //!> 子进程的pid   
  30.     int     s_pipe_fd;      //!> 与子进程通信的pipe口   
  31.     int     s_status;       //!> 子进程的状态!0:闲 1:忙   
  32. }child_process;  
  33.   
  34. child_process  child[CHILD_NUM];    //!> 定义10个子进程( 此处以10个为例 )   
  35.   
  36. static  int n_child_use = 0;        //!> 几个child在工作( if 全忙就不给他们 )   
  37.   
  38.   
  39. //!> 发送socket描述符( 这个代码在上一篇博文上有 )   
  40. //!>   
  41. int send_fd( int fd_send_to, void * data, size_t len, int sock_fd )  
  42. {  
  43.     struct msghdr   msghdr_send;        //!> the info struct   
  44.     struct iovec        iov[1];     //!> io vector   
  45.     size_t              n;      //!>   
  46.       
  47.     union  
  48.     {  
  49.         struct cmsghdr  cm;     //!> control msg   
  50.         char    ctl[CMSG_SPACE(sizeofint ))]; //!> the pointer of char   
  51.     }ctl_un;  
  52.       
  53.     struct cmsghdr * pCmsghdr = NULL;   //!> the pointer of control   
  54.       
  55.     msghdr_send.msg_control = ctl_un.ctl;     
  56.     msghdr_send.msg_controllen = sizeof( ctl_un.ctl );  
  57.       
  58.     //!> design : the first info   
  59.     pCmsghdr = CMSG_FIRSTHDR( &msghdr_send );       //!> the info of head   
  60.     pCmsghdr->cmsg_len = CMSG_LEN(sizeof(int));     //!> the msg len   
  61.     pCmsghdr->cmsg_level = SOL_SOCKET;       //!> -> stream mode                  
  62.     pCmsghdr->cmsg_type = SCM_RIGHTS;        //!> -> file descriptor   
  63.     *((int *)CMSG_DATA( pCmsghdr )) = sock_fd;  //!> data: the file fd    
  64.       
  65.     //!> these infos are nosignification   
  66.     msghdr_send.msg_name = NULL;            //!> the name       
  67.     msghdr_send.msg_namelen = 0;            //!> len of name   
  68.       
  69.     iov[0].iov_base = data;             //!> no data here   
  70.     iov[0].iov_len = len;               //!> the len of data   
  71.       
  72.     msghdr_send.msg_iov = iov;          //!> the io/vector info   
  73.     msghdr_send.msg_iovlen = 1;         //!> the num of iov   
  74.       
  75.     return ( sendmsg( fd_send_to, &msghdr_send, 0 ) );  //!> send msg now   
  76. }  
  77.   
  78. //!> 接收socket描述符   
  79. //!>    
  80. int recv_sock_fd( int fd, void * data, size_t len, int * recv_fd )  
  81. {  
  82.     struct msghdr   msghdr_recv;        //!> the info struct   
  83.     struct iovec        iov[1];     //!> io vector   
  84.     size_t              n;  //!>   
  85.       
  86.     union  
  87.     {  
  88.         struct cmsghdr  cm;     //!> control msg   
  89.         char    ctl[CMSG_SPACE(sizeofint ))]; //!> the pointer of char   
  90.     }ctl_un;  
  91.       
  92.     struct cmsghdr * pCmsghdr = NULL;       //!> the pointer of control   
  93.       
  94.     msghdr_recv.msg_control = ctl_un.ctl;     
  95.     msghdr_recv.msg_controllen = sizeof( ctl_un.ctl );  
  96.       
  97.     //!> these infos are nosignification   
  98.     msghdr_recv.msg_name = NULL;        //!> the name       
  99.     msghdr_recv.msg_namelen = 0;        //!> len of name   
  100.       
  101.     iov[0].iov_base = data;         //!> no data here   
  102.     iov[0].iov_len = len;           //!> the len of data   
  103.       
  104.     msghdr_recv.msg_iov = iov;      //!> the io/vector info   
  105.     msghdr_recv.msg_iovlen = 1;     //!> the num of iov   
  106.       
  107.     if( ( n = recvmsg( fd, &msghdr_recv, 0 ) ) < 0 ) //!> recv msg   
  108.     {                       //!> the msg is recv by msghdr_recv   
  109.         printf("recv error : %dn", errno);  
  110.         exit(EXIT_FAILURE);  
  111.     }  
  112.       
  113.     //!> now, we not use 'for' just because only one test_data_    
  114.     if(     ( pCmsghdr = CMSG_FIRSTHDR( &msghdr_recv ) ) != NULL    //!> now we need only one,   
  115.       && pCmsghdr->cmsg_len == CMSG_LEN( sizeofint ) )     //!> we should use 'for' when    
  116.      )                                                                              //!> there are many fds   
  117.      {  
  118.         if( pCmsghdr->cmsg_level != SOL_SOCKET )  
  119.         {  
  120.             printf("Ctl level should be SOL_SOCKET :%d n", errno);  
  121.             exit(EXIT_FAILURE);  
  122.         }  
  123.           
  124.         if( pCmsghdr->cmsg_type != SCM_RIGHTS )  
  125.         {  
  126.             printf("Ctl type should be SCM_RIGHTS : %dn", errno);  
  127.             exit(EXIT_FAILURE);  
  128.         }  
  129.           
  130.         *recv_fd =*((int*)CMSG_DATA(pCmsghdr)); //!> get the data : the file des*    
  131.      }  
  132.      else  
  133.      {  
  134.         *recv_fd = -1;  
  135.      }  
  136.        
  137.      return n;    
  138. }  
  139.   
  140.   
  141. //!> 子进程具体的执行过程   
  142. //!>   
  143. void web_child( int con_fd )  
  144. {  
  145.     char        buf[MAXLINE];  
  146.     int         n_read;  
  147.     int         i = 0;  
  148.       
  149.     while( strcmp( buf, "Q" ) != 0 && strcmp( buf, "q" ) != 0  )  
  150.     {  
  151.         memset( buf, 0, sizeof( buf ) );  
  152.           
  153.         if( ( n_read = read( conn_fd, buf, MAXLINE ) ) < 0 )  
  154.         {  
  155.             printf( "Read errnr! :%d n", errno );  
  156.             exit( EXIT_FAILURE );  
  157.         }  
  158.         else if( n_read == 0 )  
  159.         {  
  160.             continue;  
  161.         }  
  162.         else  
  163.         {  
  164.             while( buf[i] )  
  165.             {  
  166.                 buf[i] = toupper( buf[i] );  
  167.                 i++;  
  168.             }  
  169.             buf[i] = '';  
  170.                   
  171.             printf("Child %d done! n", ( unsigned int )pthread_self());  
  172.             printf("Child %d send %sn", ( unsigned int )pthread_self(), buf);  
  173.             write( conn_fd, buf, strlen( buf ) );           //!> 写回给client   
  174.         }  
  175.     }  
  176.       
  177.     printf("Child %d : Dating end!n", ( unsigned int )pthread_self());  
  178.           
  179. }  
  180.   
  181.   
  182. //!> child process 的主函数    
  183. //!>   
  184. void child_main( int i )  
  185. {  
  186.     char    data;       //!> 由于此处我们主要是传递socket,那么data一般就给一个” “做一个标志就好   
  187.     int     con_fd; //!> 接受con_fd   
  188.     int     n_read; //!> 读取长度   
  189.       
  190.     printf( "Child %d starting ... n", i );  
  191.       
  192.     while( 1 )  
  193.     {  
  194.         if( ( n_read = recv_sock_fd( STDERR_FILENO, &data, 1, &con_fd ) ) == 0 )   
  195.         {  
  196.             continue;           //!> 此处理论上应该是阻塞,但是简化为轮询   
  197.             //printf( " Child process %d read errnr! : %dn", i, errno );   
  198.             //exit( EXIT_FAILURE );   
  199.         }  
  200.           
  201.         if( con_fd < 0 )  
  202.         {  
  203.             printf("Child %d read connfd errnr! : %dn", i, errno);  
  204.             exit( EXIT_FAILURE );  
  205.         }  
  206.           
  207.         web_child( con_fd );                //!> child具体的执行过程   
  208.           
  209.         write( STDERR_FILENO, " ", 1 ); //!> 随便写点什么让server知道我处理完成了,那么就可以将状态位置为0了   
  210.     }  
  211. }  
  212.   
  213. //!> 产生子进程及相关处理   
  214. //!>    
  215. void child_make( int i, int listen_fd )  
  216. {  
  217.     int     sock_fd[2];     //!> 为了和主进程通信创建socket pair   
  218.     pid_t   pid;  
  219.       
  220.     //!> 创建 socketpair   
  221.     if( socketpair( AF_LOCAL, SOCK_STREAM, 0, sock_fd ) == -1 )  
  222.     {  
  223.         printf( "create socketpair error : %dn", errno );  
  224.         exit( EXIT_FAILURE );  
  225.     }  
  226.       
  227.     if( ( pid = fork() ) > 0 )       //!> 父进程   
  228.     {  
  229.         close( sock_fd[1] );  
  230.         child[i].s_pid = pid;  
  231.         child[i].s_pipe_fd = sock_fd[0];  
  232.         child[i].s_status = 0;  
  233.         return;  
  234.     }  
  235.       
  236.     if( dup2( sock_fd[0], STDERR_FILENO ) == -1 )   //!> 现在可以使用STDERR_FILENO替换刚刚创建的sock描述符   
  237.     {                       //!> 往后的child的操作就可以STDERR_FILENO中进行!   
  238.         printf("socket pair errnr! : %dn", errno);  
  239.         exit( EXIT_FAILURE );  
  240.     }  
  241.       
  242.     close( sock_fd[0] );        //!> 这些描述符都bu需要了!   
  243.     close( sock_fd[1] );  
  244.     close( listen_fd );  
  245.       
  246.     child_main( i );            //!> child 主循环   
  247. }  
  248.   
  249. //!> MAIN PROCESS   
  250. //!>   
  251. int main( int argc, char ** argv )  
  252. {  
  253.     int     i;  
  254.     int         listen_fd;  
  255.     int         conn_fd;  
  256.     int     max_fd;  
  257.     int     n_select;  
  258.     int     n_read;  
  259.     char    buf[5];  
  260.     fd_set  all_set, now_set;  
  261.     struct sockaddr_in servaddr;  
  262.     struct sockaddr_in cliaddr;  
  263.     int     len = sizeofstruct sockaddr_in );  
  264.       
  265.     //!> server 套接口   
  266.     //!>    
  267.     bzero( &servaddr, sizeof( servaddr ) );  
  268.     servaddr.sin_family = AF_INET;  
  269.     servaddr.sin_addr.s_addr = htonl( INADDR_ANY );  
  270.     servaddr.sin_port = htons( PORT );  
  271.       
  272.     //!> 建立套接字   
  273.     if( ( listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )  
  274.     {  
  275.         printf("Socket Error...n" , errno );  
  276.         exit( EXIT_FAILURE );  
  277.     }  
  278.       
  279.     //!> 绑定   
  280.     //!>   
  281.     if( bind( listen_fd, ( struct sockaddr *)&servaddr, sizeof( servaddr ) ) == -1 )  
  282.     {  
  283.         printf("Bind Error : %dn", errno);  
  284.         exit( EXIT_FAILURE );  
  285.     }  
  286.       
  287.     //!> 监听   
  288.     //!>    
  289.     if( listen( listen_fd, MAXBACK ) == -1 )  
  290.     {  
  291.         printf("Listen Error : %dn", errno);  
  292.         exit( EXIT_FAILURE );  
  293.     }  
  294.       
  295.     FD_ZERO( &all_set );  
  296.     FD_SET( listen_fd, &all_set );          //!> 将listenfd加入select   
  297.     max_fd = listen_fd;  
  298.       
  299.     for( i = 0; i < CHILD_NUM; i++ )  
  300.     {  
  301.         child_make( i, listen_fd );  
  302.         FD_SET( child[i].s_pipe_fd, &all_set ); //!> 将子进程socket加入   
  303.         max_fd = max_fd > child[i].s_pipe_fd ? max_fd : child[i].s_pipe_fd;  
  304.     }  
  305.       
  306.     while( 1 )                  //!> 主进程循环   
  307.     {  
  308.         now_set = all_set;  
  309.         if( n_child_use >= CHILD_NUM )   //!> 没有可以使用的child 了   
  310.         {               //!> 那么就将listenfd从中清空,也就是不在响应listen了,直到有child空闲   
  311.             FD_CLR( listen_fd, &now_set );  
  312.         }  
  313.           
  314.         if( (n_select = select( max_fd + 1, &now_set, NULL, NULL, NULL )) == -1)  
  315.         {  
  316.             printf(" Main process select errnr~ :%dn", errno);  
  317.             exit( EXIT_FAILURE );  
  318.         }  
  319.           
  320.         if( FD_ISSET( listen_fd, &now_set ) )           //!> if来了请求   
  321.         {  
  322.             if( ( conn_fd = accept( listen_fd, ( struct sockaddr *)&cliaddr , &len ) ) == -1 )  
  323.             {  
  324.                 printf("Server accept errnr! : %dn", errno);  
  325.                 exit( EXIT_FAILURE );  
  326.             }  
  327.               
  328.             for( i = 0; i < CHILD_NUM; i++ )  
  329.             {  
  330.                 if( child[i].s_status == 0 )        //!> 此child闲置   
  331.                 {  
  332.                     break;  
  333.                 }  
  334.             }  
  335.               
  336.             if( i == CHILD_NUM )        //!> 说明child已经全部处于忙态   
  337.             {  
  338.                 printf("All childs are busy! n");  
  339.                 exit( EXIT_FAILURE );       //!> 此处可以等待哦,或者丢弃数据   
  340.             }  
  341.               
  342.             child[i].s_status = 1;          //!> busy   
  343.             n_child_use++;              //!> busy child ++   
  344.               
  345.             send_fd( child[i].s_pipe_fd, " ", 1, conn_fd ); //!> 发送socket描述符   
  346.             close( conn_fd );               //!> server不需要处理了   
  347.               
  348.             if( --n_select == 0 )           //!> 没有其他的请求了   
  349.             {  
  350.                 continue;  
  351.             }  
  352.         }  
  353.           
  354.         for( i = 0; i < CHILD_NUM;  i++ )        //!> 看看那些child发来了msg,其实server知道肯定是child完成处理的提示标志   
  355.         {  
  356.             if( FD_ISSET( child[i].s_pipe_fd, &now_set ) )  
  357.             {  
  358.                 if( ( n_read = read( child[i].s_pipe_fd, buf, 5 ) ) == 0 )  //!> 这里的buf中data没有用,仅仅是child告诉server我完成了   
  359.                 {  
  360.                     printf("Child %d exit error! : %dn", i,  errno);  
  361.                     exit( EXIT_FAILURE );  
  362.                 }  
  363.                   
  364.                 child[i].s_status = 0;      //!> 状态位置闲   
  365.                   
  366.                 if( --n_select == 0 )       //!> if没有其他child回送消息就不要浪费时间for了   
  367.                 {  
  368.                     break;  
  369.                 }  
  370.             }  
  371.         }    
  372.           
  373.     }  
  374.       
  375.     return 0;  
  376. }  

客户端1:

[cpp] view plain copy

 print?宝马娱乐在线 13宝马娱乐在线 14

  1. $ ./client 172.18.229.60 9001  
  2. already connected to server 172.18.229.60  
  3. hello  
  4. send successful,5 byte send!  
  5. quit  
  6. i will quit!  
  7. i quited!  

Client:

客户端2:

[cpp] view plain copy

 print?宝马娱乐在线 15宝马娱乐在线 16

  1. $ ./client 172.18.229.60 9001  
  2. already connected to server 172.18.229.60  
  3. hi  
  4. send successful,2 byte send!  
  5. quit  
  6. i will quit!  
  7. i quited!  
  1. #include <stdio.h>   
  2. #include <unistd.h>   
  3. #include <stdlib.h>   
  4. #include <string.h>   
  5. #include <errno.h>   
  6. #include <netinet/in.h>   
  7. #include <sys/types.h>   
  8. #include <sys/socket.h>   
  9. #include  <arpa/inet.h>   
  10. #include <sys/select.h>   
  11.   
  12. #define MAXLINE 1024   
  13. #define SERV_PORT 6000   
  14.   
  15. //!> 注意输入是由stdin,接受是由server发送过来   
  16. //!> 所以在client端也是需要select进行处理的   
  17. void send_and_recv( int connfd )  
  18. {  
  19.     FILE * fp = stdin;  
  20.     int   lens;  
  21.     char send[MAXLINE];  
  22.     宝马娱乐在线,char recv[MAXLINE];  
  23.     fd_set rset;  
  24.     FD_ZERO( &rset );  
  25.     int maxfd = ( fileno( fp ) > connfd ? fileno( fp ) : connfd  + 1 );    
  26.                                 //!> 输入和输出的最大值   
  27.     int n;  
  28.       
  29.     while( 1 )  
  30.     {  
  31.         FD_SET( fileno( fp ), &rset );  
  32.         FD_SET( connfd, &rset );            //!> 注意不要把rset看作是简单的一个变量   
  33.                                 //!> 注意它其实是可以包含一组套接字的哦,   
  34.                                 //!> 相当于是封装的数组!每次都要是新的哦!   
  35.           
  36.         if( select( maxfd, &rset, NULL, NULL, NULL ) == -1 )  
  37.         {  
  38.             printf("Client Select Error..n");  
  39.             exit(EXIT_FAILURE  );  
  40.         }  
  41.           
  42.         //!> if 连接口有信息   
  43.         if( FD_ISSET( connfd, &rset ) ) //!> if 连接端口有信息   
  44.         {  
  45.             printf( "client get from server ...n" );  
  46.             memset( recv, 0, sizeof( recv ) );  
  47.             n = read( connfd, recv, MAXLINE );  
  48.             if( n == 0 )  
  49.             {  
  50.                 printf("Recv ok...n");  
  51.                 break;  
  52.             }  
  53.             else if( n == -1 )  
  54.             {  
  55.                 printf("Recv error...n");  
  56.                 break;  
  57.             }  
  58.             else  
  59.             {  
  60.                 lens = strlen( recv );  
  61.                 recv[lens] = '';  
  62.                 //!> 写到stdout   
  63.                 write( STDOUT_FILENO, recv, MAXLINE );  
  64.                 printf("n");  
  65.             }  
  66.           
  67.         }  
  68.           
  69.         //!> if 有stdin输入   
  70.         if( FD_ISSET( fileno( fp ), &rset ) )   //!> if 有输入   
  71.         {  
  72.             //!> printf("client stdin ...n");   
  73.               
  74.             memset( send, 0, sizeof( send ) );  
  75.             if( fgets( send, MAXLINE, fp ) == NULL )  
  76.             {  
  77.                 printf("End...n");  
  78.                 exit( EXIT_FAILURE );  
  79.             }  
  80.             else  
  81.             {  
  82.                 //!>if( str )   
  83.                 lens = strlen( send );  
  84.                 send[lens-1] = '';        //!> 减一的原因是不要回车字符   
  85.                                 //!> 经验值:这一步非常重要的哦!!!!!!!!   
  86.                 if( strcmp( send, "q" ) == 0 )  
  87.                 {  
  88.                     printf( "Bye..n" );  
  89.                     return;  
  90.                 }  
  91.                   
  92.                 printf("Client send : %sn", send);  
  93.                 write( connfd, send, strlen( send ) );  
  94.             }  
  95.         }  
  96.           
  97.     }  
  98.       
  99. }  
  100.   
  101. int main( int argc, char ** argv )  
  102. {  
  103.     //!> char * SERV_IP = "10.30.97.188";   
  104.     char    buf[MAXLINE];  
  105.     int     connfd;  
  106.     struct sockaddr_in servaddr;  
  107.       
  108.     if( argc != 2 )  
  109.     {  
  110.         printf("Input server ip !n");  
  111.         exit( EXIT_FAILURE );  
  112.     }  
  113.       
  114.     //!> 建立套接字   
  115.     if( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )  
  116.     {  
  117.         printf("Socket Error...n" , errno );  
  118.         exit( EXIT_FAILURE );  
  119.     }  
  120.   
  121.     //!> 套接字信息   
  122.     bzero(&servaddr, sizeof(servaddr));  
  123.     servaddr.sin_family = AF_INET;  
  124.     servaddr.sin_port = htons(SERV_PORT);  
  125.     inet_pton(AF_INET, argv[1], &servaddr.sin_addr);  
  126.       
  127.     //!> 链接server   
  128.     if( connect( connfd, ( struct sockaddr *  )&servaddr, sizeof( servaddr ) ) < 0 )  
  129.     {  
  130.         printf("Connect error..n");  
  131.         exit(EXIT_FAILURE);  
  132.     }     
  133.     /*else 
  134.     { 
  135.         printf("Connet ok..n"); 
  136.     }*/  
  137.   
  138.     //!>   
  139.     //!> send and recv   
  140.     send_and_recv( connfd );  
  141.       
  142.     //!>    
  143.   
  144.     close( connfd );  
  145.     printf("Exitn");  
  146.       
  147.     return 0;  
  148. }  

原文链接

http://blog.csdn.net/geng823/article/details/42144461

本文由宝马娱乐在线发布于网络频道,转载请注明出处:Linux网络编程 - TCP Socket 简单练习:线程池实现并发

上一篇:没有了 下一篇:没有了
猜你喜欢
热门排行
精彩图文