Chinaunix首页 | 论坛 | 博客
  • 博客访问: 549590
  • 博文数量: 181
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1498
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-22 15:17
个人简介

用发呆的时间来理清自己的思绪

文章存档

2015年(7)

2014年(134)

2013年(40)

分类: 嵌入式

2014-10-28 00:16:49

    在网络程序里面,一般来说都是许多客户对应一个服务器,为了处理客户的请求, 对服务端的程序就提出了特殊的要求,这就要求服务器在同一个时刻可以响应多个客户端的请求,也就是所谓的并发服务器。
    对于并发服务器,其核心思想是:每提取一个客户端请求,就创建一个子进程或子线程和客户端交互,这就就使得一个客户端对于一个子进程或者线程单独为其进行服务,当多个客户端同时请求服务器时,在服务器上就会有多个多个进程或者线程运行,此时通过CPU的调度算法可以让这些进程、线程轮流执行(单CPU),即:宏观上并行,微观上串行;或者对于多CPU的情况,就会出现真正的并行执行,此时,服务器可以响应各个客户端的请求,以上就是并发服务器的基本原理。
    TCP的并发服务器可以使用多进程实现,也可以使用多线程实现;
    1.多进程实现的基本流程如下:
    socket(...);
    bind(...);
    listen(...);
    while(1) {
    accept(...);
    if(fork(..)==0) {
    process(...);
    close(...);
    exit(...);
    }
    close(...);
    }
    
    2.多线程实现的基本流程如下:
    socket(...);
    bind(...);
    listen(...);
    while(1)
    {
    pconnect_fd = malloc(sizeof(int));

    *pconnect_fd = accept(...);

    ret = pthread_create(&tid,NULL,do_client,pconnect_fd);
    if(ret != 0){

    }

    pthread_detach(tid);
    }

一、多进程实现代码
服务器代码:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <arpa/inet.h>


  7. int do_client(int connect_fd)
  8. {
  9.     int n;
  10.     char buf[1024];
  11.     
  12.     while(1)
  13.     {
  14.         n = read(connect_fd,buf,sizeof(buf) - 1);
  15.         if(n <= 0)
  16.             break;

  17.         buf[n] = '\0';
  18.         printf("Read %d bytes : %s\n",n,buf);
  19.     
  20.         if(strncmp(buf,"quit",4)==0)
  21.             break;
  22.     }

  23.     //子进程中如果输入“quit”,则子进程必须要结束运行,然后让父进程进行收尸
  24.     exit(EXIT_SUCCESS);
  25. }

  26. //./server ip port
  27. int main(int argc, const char *argv[])
  28. {
  29.     int pid;
  30.     int sockfd;
  31.     int connect_fd;
  32.     struct sockaddr_in server_addr;
  33.     struct sockaddr_in peer_addr;
  34.     socklen_t addrlen = sizeof(struct sockaddr);

  35.     if(argc < 3)
  36.     {
  37.         fprintf(stderr,"Usage : %s \n",argv[0]);
  38.         exit(EXIT_FAILURE);
  39.     }

  40.     //防止僵尸态的子进程出现。
  41.     //设置SIGCHLD信号处理方式:忽略(告诉内核,子进程结束的时候,系统自动回收僵尸态)
  42.     if(signal(SIGCHLD,SIG_IGN) == SIG_ERR)
  43.     {
  44.         perror("Fail to signal");
  45.         exit(EXIT_FAILURE);
  46.     }

  47.     //1.创建流式套接字
  48.     sockfd = socket(AF_INET,SOCK_STREAM,0);
  49.     if(sockfd < 0){
  50.         perror("Fail to socket");
  51.         exit(EXIT_FAILURE);
  52.     }

  53.     //2.填充服务器的IP地址和端口信息
  54.     server_addr.sin_family = AF_INET;
  55.     server_addr.sin_port = htons(atoi(argv[2]));
  56.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);
  57.     //3.把服务器的IP地址和端口信息和套接字进行绑定
  58.     if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
  59.     {
  60.         perror("Fail to bind");
  61.         exit(EXIT_FAILURE);
  62.     }

  63.     //4.设置套接字为监听模式
  64.     listen(sockfd,128);

  65.     printf("listen ...\n");

  66.     while(1)
  67.     {
  68.         //5.提取请求
  69.         connect_fd = accept(sockfd,(struct sockaddr *)&peer_addr,&addrlen);
  70.         if(connect_fd < 0){
  71.             perror("Fail to accept");
  72.             exit(EXIT_FAILURE);
  73.         }

  74.         printf("connect_fd : %d\n",connect_fd);

  75.         printf("-----------------------------\n");
  76.         printf("Port : %d\n",ntohs(peer_addr.sin_port));
  77.         printf("Ip : %s\n",inet_ntoa(peer_addr.sin_addr));
  78.         printf("-----------------------------\n");

  79.         //6.请求提取成功之后立刻创建子进程,让子进程来处理该客户端,如果有多个客户端请求,就
  80.         // 需要创建多个子进程。
  81.         pid = fork();
  82.         if(pid < 0){
  83.             perror("Fail to fork");
  84.             exit(EXIT_FAILURE);
  85.         }

  86.         if(pid == 0)
  87.         {
  88.             //子进程关闭拷贝的父进程中没用的文件描述符
  89.             close(sockfd);
  90.             //在子进程中读取客户端的数据
  91.             do_client(connect_fd);
  92.         }
  93.     
  94.         //防止文件描述符浪费
  95.         close(connect_fd);
  96.     }
  97.     
  98.     return 0;
  99. }
客户端代码:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>

  6. //./server ip port
  7. int main(int argc, const char *argv[])
  8. {
  9.     int n;
  10.     int sockfd;
  11.     int connect_fd;
  12.     char buf[1024];
  13.     struct sockaddr_in server_addr;
  14.     struct sockaddr_in peer_addr;
  15.     socklen_t addrlen = sizeof(struct sockaddr);

  16.     if(argc < 3)
  17.     {
  18.         fprintf(stderr,"Usage : %s \n",argv[0]);
  19.         exit(EXIT_FAILURE);
  20.     }

  21.     //1.创建套接字
  22.     sockfd = socket(AF_INET,SOCK_STREAM,0);
  23.     if(sockfd < 0){
  24.         perror("Fail to socket");
  25.         exit(EXIT_FAILURE);
  26.     }

  27.     //2.填充服务器的IP地址和端口信息
  28.     server_addr.sin_family = AF_INET;
  29.     server_addr.sin_port = htons(atoi(argv[2]));
  30.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);
  31.     
  32.     //3.和服务器进行连接,实质上是进行3次握手,连接成功之后,被连接的套接字就会变成连接套接字
  33.     // 这个连接套接字就能够和服务器进行通信
  34.     if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
  35.     {
  36.         perror("Fail to connect");
  37.         exit(EXIT_FAILURE);
  38.     }

  39.     //4.连接成功之后,向服务器端发送数据
  40.     while(1)
  41.     {
  42.         printf("client >");
  43.         fgets(buf,sizeof(buf),stdin);
  44.         buf[strlen(buf) - 1] = '\0';

  45.         n = write(sockfd,buf,strlen(buf));
  46.         if(n < 0){
  47.             perror("Fail to write");
  48.             exit(EXIT_FAILURE);
  49.         }
  50.     }
  51.     
  52.     return 0;
  53. }
运行测试结果如下:

点击(此处)折叠或打开

  1. ubuntu@ubuntu:~/network_teacher/day02/tcp1/client$ ./client 192.168.127.131 8888
  2. client >hello
  3. client >world
  4. client >quit
  5. client >quit
  6. client >quit
  7. ubuntu@ubuntu:~/network_teacher/day02/tcp1/client$

点击(此处)折叠或打开

  1. ubuntu@ubuntu:~/network_teacher/day02/tcp1/server$ ./server 192.168.127.131 8888
  2. listen ...
  3. connect_fd : 4
  4. -----------------------------
  5. Port : 53987
  6. Ip : 192.168.127.131
  7. -----------------------------
  8. Read 5 bytes : hello
  9. Read 5 bytes : world
  10. Read 4 bytes : quit
 二、多线程实现代码
    客户端的代码完全相同,服务器端的代码做少量的修改,代码如下:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>
  4. #include <pthread.h>
  5. #include <string.h>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>


  9. void *do_client(void *arg)
  10. {
  11.     int n;
  12.     int connect_fd = *((int *)arg);
  13.     char buf[1024];
  14.     
  15.     while(1)
  16.     {
  17.         n = read(connect_fd,buf,sizeof(buf) - 1);
  18.         if(n <= 0)
  19.             break;

  20.         buf[n] = '\0';
  21.         printf("Read %d bytes : %s\n",n,buf);
  22.     
  23.         if(strncmp(buf,"quit",4)==0)
  24.             break;
  25.     }

  26.     //线程处理完之后必须关闭线程要操作的文件描述符,释放分配的内存空间
  27.     close(connect_fd);
  28.     free(arg);

  29.     //线程退出
  30.     pthread_exit(NULL);
  31. }

  32. //./server ip port
  33. int main(int argc, const char *argv[])
  34. {
  35.     int ret;
  36.     int pid;
  37.     int sockfd;
  38.     int *pconnect_fd;
  39.     pthread_t tid;
  40.     struct sockaddr_in server_addr;
  41.     struct sockaddr_in peer_addr;
  42.     socklen_t addrlen = sizeof(struct sockaddr);

  43.     if(argc < 3)
  44.     {
  45.         fprintf(stderr,"Usage : %s \n",argv[0]);
  46.         exit(EXIT_FAILURE);
  47.     }

  48.     //1.创建套接字
  49.     sockfd = socket(AF_INET,SOCK_STREAM,0);
  50.     if(sockfd < 0){
  51.         perror("Fail to socket");
  52.         exit(EXIT_FAILURE);
  53.     }

  54.     //2.填充服务器的Ip和端口信息
  55.     server_addr.sin_family = AF_INET;
  56.     server_addr.sin_port = htons(atoi(argv[2]));
  57.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);
  58.     //3.绑定服务器的IP地址和端口信息到套接字
  59.     if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
  60.     {
  61.         perror("Fail to bind");
  62.         exit(EXIT_FAILURE);
  63.     }
  64.     
  65.     //4.设置套接字为监听模式
  66.     listen(sockfd,128);

  67.     printf("listen ...\n");

  68.     while(1)
  69.     {
  70.         pconnect_fd = (int *)malloc(sizeof(int));
  71.         if(pconnect_fd == NULL){
  72.             perror("Fail to malloc");
  73.             exit(EXIT_FAILURE);
  74.         }

  75.         //5.从请求队列中提取请求
  76.         *pconnect_fd = accept(sockfd,(struct sockaddr *)&peer_addr,&addrlen);
  77.         if(connect_fd < 0){
  78.             perror("Fail to accept");
  79.             exit(EXIT_FAILURE);
  80.         }

  81.         printf("connect_fd : %d\n",*pconnect_fd);
  82.         printf("-----------------------------\n");
  83.         printf("Port : %d\n",ntohs(peer_addr.sin_port));
  84.         printf("Ip : %s\n",inet_ntoa(peer_addr.sin_addr));
  85.         printf("-----------------------------\n");
  86.         
  87.         //6.提取请求成功之后立刻创建线程,每个客户端的请求都对于一个新创建的线程
  88.         ret = pthread_create(&tid,NULL,do_client,pconnect_fd);
  89.         if(ret != 0){
  90.             fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret));
  91.         }

  92.         //分离式线程:系统自动回收,线程结束的时候未释放的资源
  93.         pthread_detach(tid);
  94.     }
  95.     
  96.     return 0;
  97. }
    测试结果和多进程的结果相同。
阅读(977) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~