【原创】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 = pthread_pool.c base.c
OBJECT = pthread_pool.o base.o
INCLUDES = -I./
LDFLAGS = -lpthread
all:$(TARGET)
$(OBJECT):$(SRC)
$(CC) -c $(INCLUDES) ${SRC}
$(TARGET):$(OBJECT)
$(CC) -o $@ $(OBJECT) $(LDFLAGS)
.PHONY:clean
clean:
@rm -rf $(OBJECT) $(TARGET) *~
服务器代码
头文件
#ifndef __PTHREAD_POOL_H__
#define __PTHREAD_POOL_H__
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <errno.h>
#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<span style="white-space:pre"> </span>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_t tid; /* which thread exec this task. */
int work_id; /* task id. */
int flag; /* 1: assigned, 0: unassigned. */
struct task_node *next;
pthread_mutex_t mutex; /* when modify this ds and exec the work,lock the task ds. */
} TASK_NODE;
/*
* *the ds of the task_queue
*/
//任务队列结构,用于控制整个任务队列
typedef struct task_queue
{
pthread_mutex_t mutex;
pthread_cond_t cond; /* when no task, the manager thread wait for ;when a new task come, signal. */
struct task_node *head; /* point to the task_link. */
int number; /* 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_t tid; /* the pid of this thread in kernel,the value is syscall return . */
int flag; /* 1:busy, 0:free. */
struct task_node *work; /* if exec a work, which work. */
struct pthread_node *next;
struct pthread_node *prev;
pthread_cond_t cond; /* when assigned a task, signal this child thread by manager. */
pthread_mutex_t mutex;
} THREAD_NODE;
/*
* *the ds of the thread queue
*/
//线程队列结构,用于控制空闲线程队列和忙碌线程队列
typedef struct pthread_queue
{
int number; /* the number of thread in this queue. */
struct pthread_node *head;
struct pthread_node *rear;
pthread_cond_t cond; /* when no idle thread, the manager wait for ,or when a thread return with idle, signal. */
pthread_mutex_t mutex;
} 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 the task 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;
char buf[MAXBUF + 1];
/*下面是select用到的变量的定义 */
fd_set rfds;
struct timeval tv;
int retval;
int maxfd = -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;
<span style="white-space:pre"> </span>//输出ip和port信息
<span style="white-space:pre"> </span>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_ntoa ntohs */
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 is execute 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 <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXBUF 1024
int 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和newfd
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 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 is execute 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 is execute 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
版权声明:本文为博主原创文章,未经博主允许不得转载。