【原创】TCP|【原创】TCP Socket 简单练习 --- 线程池实现并发服务器 分类( ...)



【原创】TCP Socket 简单练习 --- 线程池实现并发服务器


服务器函数执行流程 main

init_system
creat_pthread_pool
child_work


thread_manager


task_manager
process_client


monitor


sys_clean


Makefile文件
CC = gcc TARGET = pthread_pool SRC = https://www.it610.com/article/pthread_pool.c base.c OBJECT = pthread_pool.obase.o INCLUDES = -I./ LDFLAGS = -lpthreadall:$(TARGET)$(OBJECT):$(SRC) $(CC) -c $(INCLUDES) ${SRC}$(TARGET):$(OBJECT) $(CC) -o $@ $(OBJECT) $(LDFLAGS).PHONY:cleanclean: @rm -rf $(OBJECT) $(TARGET) *~


服务器代码 头文件
#ifndef __PTHREAD_POOL_H__#define __PTHREAD_POOL_H__#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define THREAD_MAX_NUM 100/* max number of thread. */ #define THREAD_DEF_NUM 20/* by default ,number of thread. */ #define THREAD_MIN_NUM 5/* min number of thread pool. */ #define LISNUM 5 #define PORT 9001 #define MAXBUF 1024/* * *ds of the every task. make all task in a single link */ //任务结构节点,用于描述每个任务的具体属性 typedef struct task_node { void *arg; /* fun arg. */ void *(*fun)(void *); /* the real work of the task. */ pthread_ttid; /* which thread exec this task. */ intwork_id; /* task id. */ intflag; /* 1: assigned, 0: unassigned. */ struct task_node *next; pthread_mutex_tmutex; /* when modify this ds and exec the work,lock the task ds. */ } TASK_NODE; /* * *the dsof the task_queue */ //任务队列结构,用于控制整个任务队列 typedef struct task_queue { pthread_mutex_tmutex; pthread_cond_tcond; /* when no task, the manager thread wait for ; when a new task come, signal. */ struct task_node *head; /* point to the task_link. */ intnumber; /* current number of task, include unassinged and assigned but no finished. */ } TASK_QUEUE_T; /* * *the ds of every thread, make all thread in a double link queue. */ //线程结构节点,用于描述每个线程的具体属性 typedef struct pthread_node { pthread_ttid; /* the pid of this thread in kernel,the value issyscall return . */ intflag; /*1:busy, 0:free. */ struct task_node *work; /*if exec a work, which work. */ struct pthread_node *next; struct pthread_node *prev; pthread_cond_tcond; /* when assigned a task, signal this child thread by manager. */ pthread_mutex_tmutex; } THREAD_NODE; /* * *the ds of the thread queue */ //线程队列结构,用于控制空闲线程队列和忙碌线程队列 typedef struct pthread_queue { intnumber; /* the number of thread in this queue. */ struct pthread_node *head; struct pthread_node *rear; pthread_cond_tcond; /* when no idle thread, the manager wait for ,or when a thread return with idle, signal. */ pthread_mutex_tmutex; } PTHREAD_QUEUE_T; //在pthread_poll()中定义的三个结构的指针 extern PTHREAD_QUEUE_T *pthread_queue_idle; /* the idle thread double link queue. */ extern PTHREAD_QUEUE_T *pthread_queue_busy; /* the work thread double link queue. */ extern TASK_QUEUE_T *task_queue_head; /* the task queuee single link list. */void *child_work( void *ptr ); void create_pthread_pool( void ); void init_system( void ); void *thread_manager( void *ptr ); void *prcoess_client( void *ptr ); void *task_manager( void *ptr ); void *monitor( void *ptr ); void sys_clean( void ); #endif


基础函数

#include "pthread_pool.h"/* * *child_work:the code exec in child thread * *ptr: the ds of thread_node of current thread. * *return :nothing.void * just avoid warning. */ /* child_work为创建的线程执行的函数 主要用来等待线程属性状态的变化,来判断是否有任务要执行 并且判断线程的工作状态的变化,来决定加入哪个线程队列(空闲还是忙碌) */ void * child_work( void *ptr ) { //这里的ptr为(void *) &temp[i] THREAD_NODE * self = (THREAD_NODE *) ptr; /*modify the tid attribute the first time exec */ pthread_mutex_lock( &self->mutex ); self->tid = syscall( SYS_gettid ); //获得线程自身id pthread_mutex_unlock( &self->mutex ); while ( 1 ) { pthread_mutex_lock( &self->mutex ); /*if no task exec,blocked */ /* 关键的一句话 从线程的属性struct task_node *work(即为self->work) 判断是否已给当前线程分配任务 */ //如果该线程尚没有分配任务,则通过条件变量阻塞等待条件变量self->cond if ( NULL == self->work ) { pthread_cond_wait( &self->cond, &self->mutex ); }pthread_mutex_lock( &self->work->mutex ); /*execute the real work. 开始执行任务 */ self->work->fun( self->work->arg ); /*after finished the work 任务执行完后,撤销任务的属性,并销毁任务本身,释放其占用的资源 */ self->work->fun= NULL; self->work->flag = 0; self->work->tid= 0; self->work->next = NULL; free( self->work->arg ); pthread_mutex_unlock( &self->work->mutex ); /* unlock the task */ pthread_mutex_destroy( &self->work->mutex ); /*free the task space */ free( self->work ); /*make self thread no work */ self->work = NULL; self->flag = 0; /* * *get new task from the task_link if not NULL. * *there no idle thread if there are task to do. * *if on task ,make self idle and add to the idle queue. */ /* 执行完上一个任务后,查看任务队列中是否还有任务 */ pthread_mutex_lock( &task_queue_head->mutex ); if ( task_queue_head->head != NULL )//如果有任务,则分配任务 { TASK_NODE * temp = task_queue_head->head; /*get the first task */ task_queue_head->head = task_queue_head->head->next; /*modify self thread attribute */ self->flag = 1; self->work = temp; temp->tid = self->tid; temp->next = NULL; temp->flag = 1; task_queue_head->number--; pthread_mutex_unlock( &task_queue_head->mutex ); pthread_mutex_unlock( &self->mutex ); continue; } else //如果没有任务,从忙碌线程队列中删除此线程,将其加入空闲线程队列中 { /*no task need to exec, add self to idle queue and del from busy queue */ pthread_mutex_unlock( &task_queue_head->mutex ); pthread_mutex_lock( &pthread_queue_busy->mutex ); /*self is the last execte thread 如果此线程是忙碌的线程队列中的仅剩的一个线程 */ if ( pthread_queue_busy->head == self && pthread_queue_busy->rear == self ) { pthread_queue_busy->head = pthread_queue_busy->rear = NULL; self->next= self->prev = NULL; } /*the first one thread in busy queue 如果此线程是忙碌的线程队列中的第一个线程 */ else if ( pthread_queue_busy->head == self && pthread_queue_busy->rear != self ) { pthread_queue_busy->head = pthread_queue_busy->head->next; pthread_queue_busy->head->prev = NULL; self->next = self->prev = NULL; } /*the last one thread in busy queue 如果此线程是忙碌的线程队列中的末尾的一个线程 */ else if ( pthread_queue_busy->head != self && pthread_queue_busy->rear == self ) { pthread_queue_busy->rear = pthread_queue_busy->rear->prev; pthread_queue_busy->rear->next = NULL; self->next = self->prev = NULL; } /*middle one 如果此线程是忙碌的线程队列中的中间的某个线程 */ else{ self->next->prev = self->prev; self->prev->next = self->next; self->next= self->prev = NULL; }pthread_mutex_unlock( &pthread_queue_busy->mutex ); /*add self to the idle queue 将此线程加入空闲线程队列中 */ pthread_mutex_lock( &pthread_queue_idle->mutex ); /*now the idle queue is empty 判断空闲线程队列的情况,根据不同的情况将此线程加入不同的位置 */ if ( pthread_queue_idle->head == NULL || pthread_queue_idle->rear == NULL ) { pthread_queue_idle->head = pthread_queue_idle->rear = self; self->next= self->prev = NULL; }else{ self->next= pthread_queue_idle->head; self->prev= NULL; self->next->prev = self; pthread_queue_idle->head = self; pthread_queue_idle->number++; }pthread_mutex_unlock( &pthread_queue_idle->mutex ); pthread_mutex_unlock( &self->mutex ); /*signal have idle thread 告知阻塞等待条件变量pthread_queue_idle->cond的位置已有空闲线程 */ pthread_cond_signal( &pthread_queue_idle->cond ); } } }/* * *create thread pool when the system on, and thread number is THREAD_DEF_NUM. * *when init, initial all the thread into a double link queue and all wait fo self->cond. */ void create_pthread_pool( void ) { //分配线程节点 THREAD_NODE * temp = (THREAD_NODE *) malloc( sizeof(THREAD_NODE) * THREAD_DEF_NUM ); if ( temp == NULL ) { printf( " malloc failure\n" ); exit( EXIT_FAILURE ); } /*init as a double link queue 初始化为双向链式队列 */ int i; //THREAD_DEF_NUM为线程池中线程的最大数量 //for循环开始创建线程池 for ( i = 0; i < THREAD_DEF_NUM; i++ ) { temp[i].tid= i + 1; temp[i].work = NULL; temp[i].flag = 0; if ( i == THREAD_DEF_NUM - 1 ) temp[i].next = NULL; if ( i == 0 ) temp[i].prev = NULL; //双向链表的体现 temp[i].prev = &temp[i - 1]; temp[i].next = &temp[i + 1]; pthread_cond_init( &temp[i].cond, NULL ); pthread_mutex_init( &temp[i].mutex, NULL ); /*create this thread 在此创建线程,各个线程执行的函数为child_work */ pthread_create( &temp[i].tid, NULL, child_work, (void *) &temp[i] ); } /*modify the idle thread queue attribute 修改空闲线程队列的属性 */ pthread_mutex_lock( &pthread_queue_idle->mutex ); pthread_queue_idle->number = THREAD_DEF_NUM; //此句就将刚创建的那些线程给空闲线程队列 pthread_queue_idle->head = &temp[0]; pthread_queue_idle->rear = &temp[THREAD_DEF_NUM - 1]; pthread_mutex_unlock( &pthread_queue_idle->mutex ); }/* * *init_system :init the system glob pointor. */ void init_system( void ) { /*init the pthread_queue_idle 初始化空闲线程队列,采用的是普通的双向链式结构(未循环) */ pthread_queue_idle = (PTHREAD_QUEUE_T *) malloc( sizeof(PTHREAD_QUEUE_T) ); pthread_queue_idle->number = 0; pthread_queue_idle->head = NULL; pthread_queue_idle->rear = NULL; pthread_mutex_init( &pthread_queue_idle->mutex, NULL ); pthread_cond_init( &pthread_queue_idle->cond, NULL ); /*init the pthread_queue_busy 初始化空闲线程队列,采用的是普通的双向链式结构(未循环) */ pthread_queue_busy = (PTHREAD_QUEUE_T *) malloc( sizeof(PTHREAD_QUEUE_T) ); pthread_queue_busy->number = 0; pthread_queue_busy->head = NULL; pthread_queue_busy->rear = NULL; pthread_mutex_init( &pthread_queue_busy->mutex, NULL ); pthread_cond_init( &pthread_queue_busy->cond, NULL ); /*init the task_queue_head 初始化任务队列,采用单向链表 */ task_queue_head = (TASK_QUEUE_T *) malloc( sizeof(TASK_QUEUE_T) ); task_queue_head->head = NULL; task_queue_head->number = 0; pthread_cond_init( &task_queue_head->cond, NULL ); pthread_mutex_init( &task_queue_head->mutex, NULL ); /*create thread poll 创建线程池 */ create_pthread_pool(); }/* * *thread_manager:code exec in manager thread. *block on task_queue_head->cond when no task come. *block on pthread_queue_idle->cond when no idle thread **ptr:no used ,in order to avoid warning. **return :nothing. */void * thread_manager( void *ptr ) { while ( 1 ) { THREAD_NODE * temp_thread = NULL; TASK_NODE * temp_task = NULL; /* * *get a new task, and modify the task_queue. * *if no task block om task_queue_head->cond. */ pthread_mutex_lock( &task_queue_head->mutex ); //如果任务队列为空,则阻塞等待条件变量task_queue_head->cond if ( task_queue_head->number == 0 ) pthread_cond_wait( &task_queue_head->cond, &task_queue_head->mutex ); //如果不为空,则开始准备分配任务,并修改任务队列属性 temp_task= task_queue_head->head; task_queue_head->head = task_queue_head->head->next; task_queue_head->number--; pthread_mutex_unlock( &task_queue_head->mutex ); /* * *get a new idle thread, and modify the idle_queue. * *if no idle thread, block on pthread_queue_idle->cond. */ //有了任务之后,开始判断是否有空闲线程 pthread_mutex_lock( &pthread_queue_idle->mutex ); //如果没有空闲线程,则阻塞等待条件变量pthread_queue_idle->cond if ( pthread_queue_idle->number == 0 ) pthread_cond_wait( &pthread_queue_idle->cond, &pthread_queue_idle->mutex ); //如果有空闲线程则取出一个空闲线程,然后修改空闲线程队列属性 temp_thread = pthread_queue_idle->head; /*if this is the last idle thread ,modiry the head and rear pointor */ if ( pthread_queue_idle->head == pthread_queue_idle->rear ) { pthread_queue_idle->head = NULL; pthread_queue_idle->rear = NULL; } /*if idle thread number>2, get the first one,modify the head pointor*/ else{ pthread_queue_idle->head = pthread_queue_idle->head->next; pthread_queue_idle->head->prev = NULL; }pthread_queue_idle->number--; //将空闲线程队列数量减一pthread_mutex_unlock( &pthread_queue_idle->mutex ); /*modify thetask attribute. 修改取出的线程的线程结构属性和相关的任务结构属性 */ pthread_mutex_lock( &temp_task->mutex ); temp_task->tid = temp_thread->tid; temp_task->next = NULL; temp_task->flag = 1; pthread_mutex_unlock( &temp_task->mutex ); /*modify the idle thread attribute. */ pthread_mutex_lock( &temp_thread->mutex ); temp_thread->flag = 1; temp_thread->work = temp_task; temp_thread->next = NULL; temp_thread->prev = NULL; pthread_mutex_unlock( &temp_thread->mutex ); /*add the thread assinged task to the busy queue. */ //将已分配任务的线程加入忙碌线程队列中 pthread_mutex_lock( &pthread_queue_busy->mutex ); /*if this is the first one in busy queue */ if ( pthread_queue_busy->head == NULL ) { pthread_queue_busy->head = temp_thread; pthread_queue_busy->rear = temp_thread; temp_thread->prev= temp_thread->next = NULL; }else{ /*insert in thre front of the queue */ pthread_queue_busy->head->prev = temp_thread; temp_thread->prev= NULL; temp_thread->next= pthread_queue_busy->head; pthread_queue_busy->head = temp_thread; pthread_queue_busy->number++; } pthread_mutex_unlock( &pthread_queue_busy->mutex ); /*signal the child thread to exec the work */ //告知阻塞等待条件变量temp_thread->cond的位置,开始执行任务 pthread_cond_signal( &temp_thread->cond ); } }/* * *code to process the new client in every chilld pthead. * *ptr: the fd come from listen thread that can communicate to the client. * *return:nothing. void * only used to avoid waring. */ //用来处理任务的函数 void * prcoess_client( void *ptr ) { int net_fd; net_fd = atoi( (char *) ptr ); socklen_t len; charbuf[MAXBUF + 1]; /*下面是select用到的变量的定义 */ fd_setrfds; struct timeval tv; intretval; intmaxfd = -1; while ( 1 ) { FD_ZERO( &rfds ); /* 初始化rfds为空 */ FD_SET( 0, &rfds ); /* 将标准输入的描述符0加入到集合rfds中 */ FD_SET( net_fd, &rfds ); /* 将net_fd加入到集合rfds中 */ maxfd= net_fd + 1; tv.tv_sec = 1; /* 阻塞等待时间为1s */ tv.tv_usec = 0; retval = select( maxfd, &rfds, NULL, NULL, &tv ); /* 多路复用,同时监测描述符0和net_fd */ if ( retval == -1 )/* select函数执行出错 */ { perror( "select" ); exit( EXIT_FAILURE ); }else if ( retval == 0 )/* select函数执行超时 */ continue; else{ /*有描述符引起异常 */ if ( FD_ISSET( 0, &rfds ) )/* 判断是不是标准输入0引起的异常 */ { bzero( buf, sizeof(buf) ); /* 清空buf */ fgets( buf, sizeof(buf) - 1, stdin ); /* 从终端接收输入 */if ( !strncasecmp( buf, "quit", 4 ) )/* 判断是否为退出 */ { printf( "i will close the connect!\n" ); close( net_fd ); goto clean; }len = send( net_fd, buf, strlen( buf ) - 1, 0 ); /* 向客户端发送消息 */ if ( len > 0 ) { printf( "send successful,%d byte send!\n", len ); }else{ printf( "message '%s' send failure !\n", buf ); printf( "errno code is %d, errno message is '%s'\n", errno, strerror( errno ) ); close( net_fd ); goto clean; } }if ( FD_ISSET( net_fd, &rfds ) )/* 判断是不是net_fd引起的异常 */ { bzero( buf, sizeof(buf) ); len = recv( net_fd, buf, sizeof(buf) - 1, 0 ); /* 从客户端接收消息 */ if ( len > 0 ) printf( "message recv successful : '%s', %d Byte recv\n", buf, len ); else if ( len < 0 ) { printf( "recv failure !\nerrno code is %d, errno message is '%s'\n", errno, strerror( errno ) ); close( net_fd ); goto clean; }else{ /* 如果客户端已关闭 */ printf( "the other one close quit\n" ); close( net_fd ); return; } } } } close( net_fd ); return; clean: sys_clean(); }/* * *task_manager: get new task and add to the tail of the task_link. * *ptr: no used. just avoid warning. * *return:nothing. */ //用来监听客户端的连接,产生任务 void * task_manager( void *ptr ) { int listen_fd; if ( -1 == (listen_fd = socket( AF_INET, SOCK_STREAM, 0 ) ) ) { perror( "socket" ); goto clean; } struct ifreq ifr; //eno16777736类似于eth0,在Linux系统中可以修改为eth0 strcpy( ifr.ifr_name, "eno16777736" ); //获取eno16777736的ip地址 if ( ioctl( listen_fd, SIOCGIFADDR, &ifr ) < 0 ) { perror( "ioctl" ); goto clean; } struct sockaddr_in myaddr; myaddr.sin_family = AF_INET; myaddr.sin_port= htons( PORT ); //PORT为9001,在头文件中设置,是全局变量 myaddr.sin_addr.s_addr = ( (struct sockaddr_in *) &(ifr.ifr_addr) )->sin_addr.s_addr; //输出ip和port信息 printf("server_ip = %s\nserver_port = %d\nlisnum = %d\n", inet_ntoa(myaddr.sin_addr), PORT, LISNUM); //绑定IP地址和端口port if ( -1 == bind( listen_fd, (struct sockaddr *) &myaddr, sizeof(myaddr) ) ) { perror( "bind" ); goto clean; } //监听 if ( -1 == listen( listen_fd, 5 ) ) { perror( "listen" ); goto clean; } /*i is the id of the task */ int i; //开始接受客户端的连接,产生任务 for ( i = 1; ; i++ ) { int newfd; struct sockaddr_in client; socklen_t len = sizeof(client); if ( -1 == (newfd = accept( listen_fd, (struct sockaddr *) &client, &len ) ) ) { perror( "accept" ); goto clean; } /* 打印本次连接的客户端的地址信息 inet_ntoantohs */ printf( "server: got connection from %s, port %d, socket %d\n", inet_ntoa( client.sin_addr ), ntohs( client.sin_port ), newfd ); //开始将产生的新任务加入任务队列之中 TASK_NODE * temp= NULL; TASK_NODE * newtask = (TASK_NODE *) malloc( sizeof(TASK_NODE) ); if ( newtask == NULL ) { printf( "malloc error" ); goto clean; }/* * *initial the attribute of the task. * *because this task havn't add to system,so,no need lock the mutex. */newtask->arg = (void *) malloc( 128 ); memset( newtask->arg, '\0', 128 ); //任务执行的参数为连接的客户端的socket描述符 sprintf( newtask->arg, "%d", newfd ); //新任务的处理函数均为prcoess_client,newfd即为函数prcoess_client的参数 newtask->fun= prcoess_client; newtask->tid= 0; newtask->work_id = i; newtask->next= NULL; pthread_mutex_init( &newtask->mutex, NULL ); /*add new task to task_link */ pthread_mutex_lock( &task_queue_head->mutex ); /*find the tail of the task link and add the new one to tail 开始将产生的新任务加入到任务队列中 */ temp = task_queue_head->head; if ( temp == NULL ) { task_queue_head->head = newtask; }else{ while ( temp->next != NULL ) temp = temp->next; temp->next = newtask; } task_queue_head->number++; //任务队列数量加一pthread_mutex_unlock( &task_queue_head->mutex ); /*signal the manager thread , task coming 告知阻塞等待条件变量task_queue_head->cond的位置,已有未执行的任务 */ pthread_cond_signal( &task_queue_head->cond ); } return; clean: sys_clean(); }/* * *monitor: get the system info * *ptr: not used ,only to avoid warning for pthread_create * *return: nothing. */ //用来输出哪些线程在工作 void * monitor( void *ptr ) { /*in order to prevent warning. */ ptr = ptr; THREAD_NODE * temp_thread = NULL; while ( 1 ) { pthread_mutex_lock( &pthread_queue_busy->mutex ); /*output the busy thread works one by one */ temp_thread = pthread_queue_busy->head; printf( "\n*******************************\n" ); while ( temp_thread ) { printf( "thread %ld isexecute work_number %d\n",\ temp_thread->tid, temp_thread->work->work_id ); temp_thread = temp_thread->next; } printf( "*******************************\n\n" ); pthread_mutex_unlock( &pthread_queue_busy->mutex ); sleep( 10 ); } return; }/* * *sys_clean: clean the system . * *this function code need to do to make it more stronger. */ //清理函数 void sys_clean( void ) { printf( "the system exit abnormally\n" ); exit( EXIT_FAILURE ); }


主函数

#include "pthread_pool.h"//定义三个结构的指针 PTHREAD_QUEUE_T * pthread_queue_idle; /* the idle thread double link queue. */ PTHREAD_QUEUE_T *pthread_queue_busy; /* the work thread double link queue. */ TASK_QUEUE_T *task_queue_head; /* the task queuee single link list. */int main( int argc, char *argv[] ) { pthread_t thread_manager_tid, task_manager_tid, monitor_id; //初始化空闲线程、在工作线程和要完成的任务 init_system(); //创建线程池管理线程、创建任务管理线程和线程状态监视线程 pthread_create( &thread_manager_tid, NULL, thread_manager, NULL ); /* create thread to manage the thread pool. */ pthread_create( &task_manager_tid, NULL, task_manager, NULL ); /* create thread recive task from client. */ pthread_create( &monitor_id, NULL, monitor, NULL ); /* create thread to monitor the system info. */ //等待线程退出 pthread_join( thread_manager_tid, NULL ); pthread_join( task_manager_tid, NULL ); pthread_join( monitor_id, NULL ); //清理服务器,准备退出主函数 sys_clean(); return(0); }


客户端代码

/************************************************************************* > File Name: socket_select_client.c > Author: genglut > Mail: genglut@163.com > Created Time: 2014年12月22日 星期一 18时06分06秒 ************************************************************************/#include #include #include #include #include #include #include #include #include #define MAXBUF 1024int main(int argc, char *argv[]) { int sockfd; socklen_t len; struct sockaddr_in server_addr; char buf[MAXBUF + 1]; //下面是select用到的变量的定义 fd_set rfds; struct timeval tv; int retval; int maxfd = -1; if(argc != 3) { printf("error failure, it must be:\n\t\t%s IP port \n", argv[0]); exit(EXIT_FAILURE); } if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[2])); server_addr.sin_addr.s_addr = inet_addr(argv[1]); if(connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(EXIT_FAILURE); } printf("already connected to server %s\n", argv[1]); while(1) { FD_ZERO(&rfds); //初始化rfds为空 FD_SET(0, &rfds); //将标准输入的描述符0加入到集合rfds中 FD_SET(sockfd, &rfds); //将newfd加入到集合rfds中 maxfd = sockfd + 1; tv.tv_sec = 1; //阻塞等待时间为1s tv.tv_usec = 0; retval = select(maxfd, &rfds, NULL, NULL, &tv); //多路复用,同时监测描述符0和newfdif(retval == -1)//select函数执行出错 { perror("select"); exit(EXIT_FAILURE); } else if(retval == 0)//select函数执行超时 continue; else//有描述符引起异常 { if(FD_ISSET(0, &rfds))//判断是不是标准输入0引起的异常 { bzero(buf, sizeof(buf)); //清空buf fgets(buf, sizeof(buf)-1, stdin); //从终端接收输入if(!strncasecmp(buf, "quit", 4))//判断是否为退出 { printf("i will quit!\n"); break; }len = send(sockfd, buf, strlen(buf)-1, 0); //向客户端发送消息 if(len > 0) { printf ("send successful,%d byte send!\n",len); } else { printf("message '%s' send failure !\n", buf); printf("errno code is %d, errno message is '%s'\n", errno, strerror(errno)); break; } }if(FD_ISSET(sockfd, &rfds))//判断是不是newfd引起的异常 { bzero(buf, sizeof(buf)); len = recv(sockfd, buf, sizeof(buf)-1, 0); //从客户端接收消息 if(len > 0 ) printf("message recv successful : '%s', %d Byte recv\n", buf, len); else if(len < 0) { printf("recv failure !\nerrno code is %d, errno message is '%s'\n", errno, strerror(errno)); break; } else//如果客户端已关闭 { printf("the other one close, quit\n"); break; } } } } close(sockfd); printf("i quited!\n"); return 0; }


运行结果 服务器:
$ ./pthread_pool----------------------------------------- -----------------------------------------server_ip = 172.18.229.60 server_port = 9001 lisnum = 5----------------------------------------- -----------------------------------------server: got connection from 172.18.229.60, port 56023, socket 4 message recv successful : 'hello', 5 Byte recv----------------------------------------- thread 40159 isexecute work_number 1 -----------------------------------------server: got connection from 172.18.229.60, port 56024, socket 5 message recv successful : 'hi', 2 Byte recv the other one close quit----------------------------------------- thread 40159 isexecute work_number 1 -----------------------------------------the other one close quit


客户端1:

$ ./client 172.18.229.60 9001 already connected to server 172.18.229.60 hello send successful,5 byte send! quit i will quit! i quited!


客户端2:
$ ./client 172.18.229.60 9001 already connected to server 172.18.229.60 hi send successful,2 byte send! quit i will quit! i quited!


原文链接 http://blog.csdn.net/geng823/article/details/42144461

版权声明:本文为博主原创文章,未经博主允许不得转载。
【【原创】TCP|【原创】TCP Socket 简单练习 --- 线程池实现并发服务器 分类( ...)】转载于:https://www.cnblogs.com/gengzj/p/4675765.html

    推荐阅读