socket网络编程(四)-----基于线程池的并发服务器

服务器主线程在每收到客户端连接请求,创建一个工作线程,这会消耗服务器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);
}

执行结果:

两个连接请求:

socket网络编程(四)-----基于线程池的并发服务器_第1张图片
当有第五个连接请求时,服务器端无法响应这个客户端:

socket网络编程(四)-----基于线程池的并发服务器_第2张图片
此时结束某一个已连接客户端程序,之前阻塞的客户端成功连接,完成和服务器端的通信:

socket网络编程(四)-----基于线程池的并发服务器_第3张图片

你可能感兴趣的:(socket网络编程(四)-----基于线程池的并发服务器)