socket网络编程(二)-----多进程并发服务器

实际应用中,一个服务器需要能够同时处理多个客户端的连接请求,在上一篇的代码中没能实现,本篇对上一篇的代码做了一点改进。服务器端将监听时间与处理请求这两个功能分开,主进程负责监听,当接收到客户端连接请求后,创建一个子进程与客户端通信,而主进程则继续监听。注意的时,每当主进程执行accept()获得请求的描述符,并创建子进程之后,需要将这个描述符关闭,因为子进程会完整得复制这个描述符。子进程处理完事件后,关闭描述符并推出。

下面是代码:

服务器端:

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

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#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 <host> <port>\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);
}

执行结果:

在这里插入图片描述

发布了7 篇原创文章 · 获赞 0 · 访问量 98
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览