服务器主线程在每收到客户端连接请求,创建一个工作线程,这会消耗服务器CPU资源,从而降低客户端的连接响应速度。服务器开启监听之前,可以预先创建工作线程,通过线程池管理这些线程。初始时,线程池可用的连接描述符为0,此时工作线程阻塞在取描述符中,当有连接请求时,主线程将accept()获得的描述符插入线程池,其中某一个工作线程会立马跳出阻塞,从线程池中取得描述符,然后开始和客户端通信。
服务器端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //gethostbyaddr
#include "taskpool.h"
#define NTHREADS 4
#define SBUFSIZE 2
#define MAXLINE 1024
/*
基于线程池的并发服务器
*/
void toggle(int conn_sock);
void handlep(int *conn_sock_p);
int startup(int *port);
void error_die(const char *sc);
task_pool_t tp; //线程池
int main(int argc,char **argv){
if(argc!=2){
fprintf(stderr,"input:%s \n" , argv[0]);
exit(1);
}
int i, listen_sock, conn_sock, port;
socklen_t clientlen = sizeof(struct sockaddr_in);
struct sockaddr_in clientaddr;
pthread_t tid;
port=atoi(argv[1]); //string to int
task_pool_init(&tp, SBUFSIZE);
listen_sock = startup(&port);
/*服务器首先创建工作线程*/
for(i = 0; i < NTHREADS; ++i){
pthread_create(&tid, NULL, (void*)handlep, NULL);
}
while(1){
conn_sock = accept(listen_sock,(struct sockaddr*)&clientaddr,&clientlen);
task_insert(&tp, conn_sock); //向线程池中插入描述符
}
exit(0);
}
void handlep(int* conn_sock_p){
pthread_detach(pthread_self());
while(1){
int conn_sock = task_remove(&tp);
printf("Server connection success(pthread %lu)\n", pthread_self()); //要使用%lu方式
toggle(conn_sock);
printf("Server connection over(pthread %lu)\n", pthread_self());
close(conn_sock);
}
}
void toggle(int conn_sock){
int n;
int i;
char buf[MAXLINE];
while(n = recv(conn_sock, buf, MAXLINE, 0)){
printf("togglest server received %d bytes\n", n);
for(i= 0; i < n; i++){
if(isupper(buf[i]))
buf[i] = tolower(buf[i]);
else
buf[i] = toupper(buf[i]);
}
send(conn_sock, buf, n, 0);
}
}
int startup(int *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定socket
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
error_die("bind");
//如果端口没有设置,提供个随机端口
if (*port == 0) /* if dynamically allocating a port */
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
//监听
if (listen(httpd, 5) < 0)
error_die("listen");
return(httpd);
}
void error_die(const char *sc)
{
perror(sc);
exit(1);
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 1024
//一个客户端程序
int open_client_sock(char *host,int port);
void error_die(const char *sc);
int main(int argc,char **argv){
int client_sock,port,n;
char *host,buf[MAXLINE];
if(argc!=3){
fprintf(stderr,"input:%s \n" ,argv[0]);
exit(1);
}
host=argv[1]; //char * server's name
port=atoi(argv[2]); //int server's port
client_sock=open_client_sock(host,port);
while(fgets(buf,MAXLINE,stdin)!=NULL){
n = send(client_sock,buf,strlen(buf),0);
printf("send %d bytes\n",n);
recv(client_sock,buf,MAXLINE,0);
fputs(buf,stdout);
}
close(client_sock);
exit(0);
}
//传入一个服务器主机名,监听端口,返回已连接描述符
int open_client_sock(char *host,int port){
int http = 0;
struct hostent *hp;
struct sockaddr_in serveraddr;
http = socket(AF_INET, SOCK_STREAM, 0);
if(http == -1)
error_die("socket");
if((hp = gethostbyname(host)) == NULL)//由域名获取IP
error_die("no host");
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
bcopy((char *)hp->h_addr_list[0], (char *)&serveraddr.sin_addr.s_addr, hp->h_length);
if(connect(http, (struct sockaddr *)&serveraddr, sizeof(serveraddr))<0)
error_die("can't connect");
return http;
}
void error_die(const char *sc)
{
perror(sc);
exit(1);
}
线程池:
taskpool.h:
#ifndef _TASKPOOL
#define _TASKPOOL
#include
#include
#include //sem_t
typedef struct{
int *socks; //数组
int cnt;
int inpos;
int outpos;
sem_t mutex;
sem_t avail;
sem_t ready;
}task_pool_t;
void task_pool_init(task_pool_t *tp, int n);
void task_insert(task_pool_t *tp, int item);
int task_remove(task_pool_t *tp);
void task_pool_deinit(task_pool_t *tp);
#endif
taskpool.c
#include
#include
#include //memset
#include //sem_t
#include"taskpool.h"
void task_pool_init(task_pool_t *tp, int n){
tp->socks = calloc(n, sizeof(int)); //在内存的动态存储区开辟n个int大小的区域
tp->cnt = n;
tp->outpos = tp->inpos = 0;
sem_init(&tp->mutex, 0, 1); //互斥量
sem_init(&tp->avail, 0, tp->cnt);
sem_init(&tp->ready, 0, 0);
}
void task_insert(task_pool_t *tp, int item){
sem_wait(&tp->avail); //可用减1
sem_wait(&tp->mutex);
tp->socks[tp->inpos] = item;
tp->inpos = (tp->inpos + 1)%(tp->cnt);
sem_post(&tp->mutex);
sem_post(&tp->ready); //已连接的描述符数量加1
}
int task_remove(task_pool_t *tp){
int conn_sock;
sem_wait(&tp->ready); //已用减1
sem_wait(&tp->mutex);
conn_sock = tp->socks[tp->outpos];
tp->outpos = (tp->outpos + 1) % (tp->cnt);
sem_post(&tp->mutex);
sem_post(&tp->avail); //可用加1
return conn_sock;
}
void task_pool_deinit(task_pool_t *tp){
memset((void*)tp->socks, -1, tp->cnt*sizeof(int));
tp->inpos = 0;
tp->outpos = 0;
sem_init(&tp->mutex, 0, 1);
sem_init(&tp->avail, 0, tp->cnt);
sem_init(&tp->ready, 0, 0);
}
执行结果:
两个连接请求: