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

服务器处理多个连接请求,上篇使用了fork()创建子进程,进程的创建和销毁,会消耗服务器大量的资源。利用线程去处理请求,减轻服务器断电压力。

服务器端:

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>//
#include <pthread.h>
#include <sys/wait.h>
#include <netdb.h> //gethostbyaddr
#define MAXLINE 1024
/*
基于多线程的并发服务器
*/
void toggle(int conn_sock);
void handlep(int* conn_sock_p);
int startup(int *port);
void error_die(const char *sc);

int main(int argc,char **argv){
  int listen_sock,port,clientlen;
  int *conn_sock_p; 
  clientlen = sizeof(struct sockaddr_in);   //must
  struct sockaddr_in clientaddr;  
  pthread_t tid;

  if(argc!=2){
    fprintf(stderr,"input:%s <port>\n",argv[0]);  
    exit(1);
  }
  port=atoi(argv[1]);  //string to int
 
  listen_sock = startup(&port);
  while(1){
    conn_sock_p = malloc(sizeof(int));
    *conn_sock_p = accept(listen_sock,(struct sockaddr*)&clientaddr,&clientlen);
    pthread_create(&tid,NULL,(void *)handlep,conn_sock_p);
  }
  exit(0);
}

/*线程处理*/

void handlep(int* conn_sock_p){    
  int conn_sock = *conn_sock_p;
  pthread_detach(pthread_self());
  free(conn_sock_p);

  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);
  return;
}

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<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <netdb.h> //gethostbyaddr
#define MAXLINE 1024
/*
  可同事处理多个客户端连接请求的并发服务器
*/
void toggle(int conn_sock);
int startup(int *port);
void error_die(const char *sc);

/*
  处理僵尸进程
*/
void sigchld_handler(int sig){     
  while(waitpid(-1,0,WNOHANG)>0);
  return;
}

int main(int argc,char **argv){
  int listen_sock,conn_sock,port,clientlen,cpid;
  struct sockaddr_in clientaddr;
  struct hostent *hp;
  char *haddrp;
  if(argc!=2){
    fprintf(stderr,"input:%s <port>\n",argv[0]);  
    exit(1);
  }
  port=atoi(argv[1]);  //string to int
  
  signal(SIGCHLD,sigchld_handler);
  listen_sock = startup(&port);
  while(1){
    clientlen=sizeof(clientaddr);
    conn_sock=accept(listen_sock,(struct sockaddr*)&clientaddr,&clientlen);
    
    if(fork() == 0){  //子进程运行,父进程继续循环accept()
      hp=gethostbyaddr((const char*)&clientaddr.sin_addr.s_addr,
        sizeof(clientaddr.sin_addr.s_addr),AF_INET);
      haddrp=inet_ntoa(clientaddr.sin_addr);
      cpid = getpid();
      printf("Server(chile process %d) connected to %s(%s)\n",cpid,hp->h_name,haddrp);
      
      close(listen_sock);
      toggle(conn_sock);
      close(conn_sock);
      printf("Server(chile process %d) connection closed\n",cpid);
      exit(0);   //子进程退出
    }
    close(conn_sock); //父进程关闭这个连接描述符,因为这个描述符对父进程是没有用的,子进程复制了一个 
  }
  exit(0);
}


void toggle(int conn_sock){
  int n;
  int i;
  char buf[MAXLINE];
  while(n=recv(conn_sock,buf,MAXLINE,0)){ 
    printf("toggle 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)  
 {
  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);
}

执行结果:

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值