前述: 线程池网络服务是针对多线程网络服务模式的不足而提出的改进模式;其基本理念是先创建一批资源,当有用户到来时,直接分配已经创建的资源;线程池的主要目的是减少系统在频繁创建资源时的开销。
线程池网络服务的实现原理:
1> 主服务线程创建的即定数量的服务线程,同时在指定端口进行侦听;
2> 若当前有新连接到来,则从线程池中找出空闲的服务线程,为其服务,服务完毕,线程不进行释放,重新放回线程池;
3> 若当前线程池已经满了,则将当前的连接加入等待队列或者为其创建新的线程为其服务(依实现而定);
线程池网络服务的流程图如下:
线程池服务的优点: 性能高效
线程池的核心设计思想是: 系统在初始化时就创建一定数量的服务线程,并使它们处于空闲装态,若当前有新用户到来,则系统先查找有无空闲线程,若有则立即为其分配服务线程,若没有,则将新用户加入待服务的队列并在其他线程服务完成后,重新为其分配服务线程。
新用户若在等待服务队列里耗时过长,会影响用户体验,针对此问题,有以下的改进方案:
1> 动态创建新的服务线程,服务结束后,该线程加入到线程池,此种改进方法的好处是用户体验得到提升,但是潜在问题是线程的规模在长时间,大并发用户的状态下,会变得很大,最后会因资源消耗过大,系统退出;
2> 增加一个线程资源回收机制,当线程池的规模达到一定程度或满足某种即定规则时,会主动杀死一些线程,以达到在用户体验于系统运行稳定性之间的折中;
线程池的设计方案:
1> 系统在初始化时,开辟THREAD_MAX个线程,组成服务线程池。
2> 每个线程均有两种状态,空闲、工作状态,每个线程均有一块属于自己得公共数据区域,这块数据区域存放两个变量,一个是用来标记自身状态,0:空闲 1:工作,另一个变量对应客户端套接字号,如果线程处于空闲状态,则套接字号为初始值。
3> 当有新用户到来时,系统通过查找公共数据区域,判断当前系统是否有空闲线程,若有则直接为其分配服务线程。
4> 若当前没有空闲线程,系统将不做任何处理,提示当前系统忙,关闭新客户套接字
以下是线程池的一个示例:
//客户端代码
#include "unp.h"
void Input(Oper *op);
void help();
int main(int ac, char *av[])
{
int sockcli = socket(AF_INET, SOCK_STREAM, 0); //得到客户端的套接字
if(sockcli == -1){
perror("socket");
}
struct sockaddr_in addrser; //服务器的地址结构
addrser.sin_family = AF_INET; //设置服务器的协议家族
addrser.sin_port = htons(SERVER_PORT); //设置服务器的端口号
addrser.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器的IP地址
socklen_t addrlen = sizeof(struct sockaddr); //得到服务器的地址结构大小
int res = connect(sockcli, (struct sockaddr*)&addrser, addrlen); //连接服务器
if(res == -1){
perror("connect");
}else{
printf("client connect server ok.\n");
}
char cmd[20]; //用于存储客户所请求的服务名称
Oper op; //客户的请求的结构体(包括客户请求的服务类型,输入的数据)
int result; //由服务器发回的请求的服务结果
//客户发送请求并接收请求结果
while(1){
printf("please input cmd:>");
scanf("%s", cmd);
if(strcmp(cmd, "add") == 0){
op.oper = ADD;
Input(&op);
}else if(strcmp(cmd, "sub") == 0){
op.oper = SUB;
Input(&op);
}else if(strcmp(cmd, "mul") == 0){
op.oper = MUL;
Input(&op);
}else if(strcmp(cmd, "div") == 0){
op.oper = DIV;
while(1){
Input(&op);
if(op.op2 == 0){ //除数为0时重新输入
perror("op2 == 0 error");
printf("please input again.\n");
continue;
}
break;
}
}else if(strcmp(cmd, "help") == 0){
help();
continue;
}else if(strcmp(cmd, "quit") == 0){
op.oper = QUIT;
}else{
printf("your input is invalid.\n");
}
int res = send(sockcli, (char *)&op, sizeof(op), 0); //发送客户的请求给服务器
if(res == -1){
perror("send");
}
if(op.oper == QUIT){
break;
}
recv(sockcli, &result, sizeof(int), 0); //接收服务器发送回来的结果
printf("result = %d\n", result);
}
close(sockcli); //关闭客户端的套接字
return 0;
}
void Input(Oper *op) //得到进行操作的两个数
{
printf("please input op1 add op2.\n");
scanf("%d, %d", &op->op1, &op->op2);
}
void help() //提示信息
{
printf("#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*\n");
printf("^^^^^^^^^version^^^^^^^^^ : ^^^^^v1.0^^^^^^^^^^\n");
printf("^^^^^^^^^author^^^^^^^^^^ : ^^^^^haha^^^^^^^^^^\n");
printf("~*~*~*~*~cmd~*~*~*~*~*~*~ ~*~*~describe~*~*~\n");
printf("~*~*~*~*~add~*~*~*~*~*~*~ ~*~*~op1 + op2~*~*\n");
printf("~*~*~*~*~sub~*~*~*~*~*~*~ ~*~*~op1 - op2~*~*\n");
printf("~*~*~*~*~mul~*~*~*~*~*~*~ ~*~*~op1 * op2~*~*\n");
printf("~*~*~*~*~div~*~*~*~*~*~*~ ~*~*~op1 / op2~*~*\n");
printf("#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*\n");
}
//服务器端的代码
#include "../unp.h"
#define THREAD_MAX 3
typedef struct thread_arg{
bool flag;
int sockconn;
}thread_arg;
typedef struct thread_arg threadpool[THREAD_MAX];
threadpool pool;
void* handler(void *arg)
{
int index = *(int *)arg;
printf("[%d] thread startup.\n", index);
struct thread_arg *pthread = &pool[index];
int sockconn = pthread->sockconn;
char sendbuf[100];
Oper op;
int result;
while(1)
{
if(pthread->flag){
printf("[%d] thread start work.\n", index);
int res = recv(pool[index].sockconn, (char *)&op, sizeof(op), 0);
if(res == -1){
perror("recv");
}
if(op.oper == ADD){
result = op.op1 + op.op2;
}else if(op.oper == SUB){
result = op.op1 - op.op2;
}else if(op.oper == MUL){
result = op.op1 * op.op2;
}else if(op.oper == DIV){
result = op.op1 / op.op2;
}else if(op.oper == QUIT){
struct sockaddr_in addrcli;
socklen_t addrlen = sizeof(struct sockaddr);
getpeername(pool[index].sockconn, (struct sockaddr*)&addrcli, &addrlen);
printf("client[%d] quit.\n", addrcli.sin_port);
pthread->flag = 0;
close(sockconn);
continue;
}
send(pool[index].sockconn, (char *)&result, sizeof(int), 0);
}else{
printf("[%d] thread is idel.\n", index);
sleep(1);
}
}
}
int main(int ac, char *av[])
{
int sockser = socket(AF_INET, SOCK_STREAM, 0);
if(sockser == -1){
perror("socket");
}
int yes = 1;
if(setsockopt(sockser, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
}
struct sockaddr_in addrser, addrcli;
addrser.sin_family = AF_INET;
addrser.sin_port = htons(SERVER_PORT);
addrser.sin_addr.s_addr = inet_addr(SERVER_IP);
socklen_t addrlen = sizeof(struct sockaddr);
int res = bind(sockser, (struct sockaddr*)&addrser, addrlen);
if(res == -1){
perror("bind");
}
listen(sockser, QUEUE_SIZE);
pthread_t tid[THREAD_MAX];
for(int i = 0; i < THREAD_MAX; ++i){
pool[i].flag = 0;
pool[i].sockconn = 0;
}
for(int i = 0; i < THREAD_MAX; ++i){
pthread_create(&tid[i], NULL, handler, &i);
sleep(1);
}
int sockconn;
int j = -1;
while(1){
sockconn = accept(sockser, (struct sockaddr*)&addrcli, &addrlen);
if(sockconn == -1){
perror("accept");
}
printf("client[%d] connect server ok.\n", addrcli.sin_port);
for(int i = 0; i < THREAD_MAX; ++i){
if(pool[i].flag == 0){
pool[i].sockconn = sockconn;
pool[i].flag = 1;
break;
}
++j;
if(j >= THREAD_MAX){
pthread_create(&tid[j], NULL, handler, &j);
pool[j].sockconn = sockconn;
pool[j].flag = 1;
sleep(1);
break;
}
}
}
close(sockser);
return 0;
}
//头文件
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 7070
#define QUEUE_SIZE 5
typedef enum{ADD, SUB, MUL, DIV, QUIT}OPER_STATE;
typedef struct Oper{
OPER_STATE oper;
int op1;
int op2;
}Oper;