这个架构容易写,在《Unix网络编程》中可以看到性能也还可以,最关键是我学会了。
基本思路就是服务器接收到一个客户的请求,就将个这个请求的任务添加到线程池的任务队列中,使用AddWork(void* (*func)(void *arg), void *arg)。
因此我们可以将自己的业务代码写在void* (*func)(void *arg)中,然后就OK了。我这里的业务是一个服务器echo客户端。
先看服务器端的代码:
线程池:
/*
* ThreadPool.h
*
* Created on: 2014年4月21日
* Author: tomcat
*/
#ifndef THREADPOOL_H_
#define THREADPOOL_H_
#include <stdlib.h>
#include <pthread.h>
typedef struct work{
void* (*func)(void *arg);
void *arg;
struct work *next;
}tpool_work_t;
typedef struct{
int threadNum;
bool shutDown;
tpool_work_t* head;
tpool_work_t* tail;
pthread_t *tid;
pthread_mutex_t queueMutex;
pthread_cond_t queueReady;
}tpool;
static tpool *thread_pool = NULL;
int CreateThreadPool(tpool *thread_pool);
int AddWork(void* (*func)(void *arg), void *arg);
void DestroyThreadPool();
static void* ThreadFunc(void *arg)
{
tpool_work_t *work;
while(1){
pthread_mutex_lock(&thread_pool->queueMutex);
while(!thread_pool->head && !thread_pool->shutDown){
pthread_cond_wait(&thread_pool->queueReady, &thread_pool->queueMutex);
}
if(thread_pool->shutDown){
pthread_mutex_unlock(&thread_pool->queueMutex);
pthread_exit(NULL);
}
work = thread_pool->head;
thread_pool->head = thread_pool->head->next;
pthread_mutex_unlock(&thread_pool->queueMutex);
work->func(work->arg);
free(work);
}
return NULL;
}
int CreateThreadPool(int threadNum){
int i;
thread_pool = (tpool*)malloc(sizeof(tpool));
if(!thread_pool){
printf("thread_pool create fail!\n");
return -1;
}
thread_pool->threadNum = threadNum;
thread_pool->shutDown = false;
thread_pool->head = NULL;
thread_pool->tail = NULL;
thread_pool->tid = (pthread_t *)malloc(threadNum*sizeof(pthread_t));
if(!thread_pool->tid){
printf("thread create fail!\n");
return -1;
}
if(pthread_mutex_init(&thread_pool->queueMutex, NULL) != 0){
printf("mutex initialize error!\n");
return -1;
}
if(pthread_cond_init(&thread_pool->queueReady, NULL) != 0){
printf("cond initialize error!\n");
return -1;
}
for(i=0; i<threadNum; i++)
{
if(pthread_create(&thread_pool->tid[i], NULL, ThreadFunc, NULL)){
printf("thread create error!\n");
return -1;
}
}
return 0;
}
int AddWork(void* (*func)(void *arg), void *arg)
{
tpool_work_t *work;
if(!arg){
printf("the input function is invalid!\n");
return -1;
}
work = (tpool_work_t*)malloc(sizeof(tpool_work_t));
if(!work){
printf("new work create fail!\n");
return -1;
}
work->func = func;
work->arg = arg;
work->next = NULL;
pthread_mutex_lock(&thread_pool->queueMutex);
if(!thread_pool->head){
thread_pool->head = work;
thread_pool->tail = work;
}else{
thread_pool->tail->next = work;
thread_pool->tail = work;
}
pthread_cond_signal(&thread_pool->queueReady);
pthread_mutex_unlock(&thread_pool->queueMutex);
return 0;
}
void DestroyThreadPool()
{
int i;
tpool_work_t *work;
if(thread_pool->shutDown){
return ;
}
thread_pool->shutDown = true;
pthread_cond_broadcast(&thread_pool->queueReady);
for(i=0; i<thread_pool->threadNum; ++i){
pthread_join(thread_pool->tid[i], NULL);
}
free(thread_pool->tid);
while(thread_pool->head){
work = thread_pool->head;
thread_pool->head = thread_pool->head->next;
free(work);
}
pthread_mutex_destroy(&thread_pool->queueMutex);
pthread_cond_destroy(&thread_pool->queueReady);
free(thread_pool);
}
#endif /* THREADPOOL_H_ */
EchoServer.cpp
//============================================================================
// Name : EchoServer.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "ThreadPool.h"
const unsigned int PORT = 7777;
const unsigned int LISTEN_NUM = 128;
const unsigned int MAXLINE = 1024;
void* doit(void *arg)
{
pthread_detach(pthread_self());
char buf[MAXLINE];
int connfd = (int)arg;
int n = 0;
while((n = recv(connfd, buf, sizeof(buf), 0)) > 0){
send(connfd, buf, n, 0);
}
if(n == -1){
printf("receive error\n");
}
close(connfd);
return NULL;
}
int main() {
struct sockaddr_in cliaddr, serveraddr;
int listenfd, connfd;
socklen_t clilen;
pthread_t tid;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(PORT);
if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1){
printf("bind error\n");
return -1;
}
if(listen(listenfd,LISTEN_NUM) == -1){
printf("listen error\n");
return -1;
}
CreateThreadPool(LISTEN_NUM);
for(;;){
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
if(connfd>0){
//if(pthread_create(&tid, NULL, doit, (void *)connfd)){
if(AddWork(doit, (void *)connfd)){
printf("pthread create error\n");
return -1;
}
}
}
DestroyThreadPool();
close(listenfd);
return 0;
}
这里的
void* doit(void *arg)
就是我的echo业务。
客户端:
//============================================================================
// Name : EchoClient.cpp
// Author :
// Version :
// Copyright : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <sys/socket.h>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
const char *IPADRR = "127.0.0.1";
const unsigned int SERVERPORT = 7777;
const unsigned int MAXLINE = 1024;
void doit(FILE *fp, int sockfd)
{
int ret;
char send_buf[MAXLINE], recv_buf[MAXLINE];
while(fgets(send_buf, MAXLINE, fp) != NULL){
if((ret = send(sockfd, send_buf, strlen(send_buf), 0)) == -1){
printf("send error\n");
return;
}
//每次接收数据前将recv_buf清空,主要是填充'\0',因为接收数据不会在添上'\0'为结尾
bzero(recv_buf, MAXLINE);
if((ret = recv(sockfd, recv_buf, MAXLINE, 0)) == 0){
printf("server stopped!\n");
}
fputs(recv_buf, stdout);
}
}
int main() {
int clifd;
struct sockaddr_in serveraddr;
if((clifd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("socket create error\n");
return -1;
}
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(IPADRR);
serveraddr.sin_port = htons(SERVERPORT);
if(connect(clifd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
printf("connect to server fail\n");
return -1;
}
printf("connect to server successfully!\n");
doit(stdin, clifd);
return 0;
}
客户端的执行结果就是这个样子:
tomcat@tomcat-Lenovo-IdeaPad-Y550:~/文档/Echo$ ./EchoClient
connect to server successfully!
12344
12344
2
2
adasdasdad
adasdasdad