【原创】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
推荐阅读
- 宽容谁
- 我要做大厨
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 三十年后的广场舞大爷
- 叙述作文
- 20190302|20190302 复盘翻盘
- 学无止境,人生还很长