• TCP——并发服务


    在上一节的程序中,服务端在进行到accept()环节会等待客户端的请求到来,若客户端一直不发生请求,则服务端会一直阻塞。

    因此,引入并发服务器的概念。

      一、并发服务器

      同一时刻可以响应多个客户端的请求,多任务完成服务每个客户端的请求,每个客户端不需要排队等待,可以立即进行服务。

      并发服务器设计技术一般有:多进程服务器、多线程服务器、I/O复用服务器(循环服务器)等。

      

       

       (图片来源)

      

      1、多线程服务器

    父进程监听客户端的连接请求,创建子线程,进行线程分离

    子线程与客户端进行数据交互

      多线程服务器是对多进程服务器的改进,由于多进程服务器在创建进程时要消耗较大的系统资源,所以用线程来取代进程,这样服务处理程序可以较快的创建。  

      优点: 服务效率高,客户端无需等待。
      缺点: 复杂、较多地占用了系统的资源,一旦客户端过多会严重浪费系统资源。

      

    TCP多线程实例:

      1 #include "net.h"
      2 void cli_data_handle(void *arg);
      3 
      4 int main()
      5 {
      6     int fd = -1;
      7     struct sockaddr_in sin; //Internet环境下套接字的地址形式,对其进行操作以建立信息
      8     
      9     /*1、创建套接字描述符fd */
     10     if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
     11     {
     12         perror("socket");
     13         exit(1);
     14     }
     15 
     16     /*2、将套接字绑定到一个本地地址与端口上*/
     17     
     18     /*2.1 填充struct sockaddr_in结构体变量*/
     19     bzero(&sin, sizeof(sin)); //初始值置零
     20     //sin_port和sin_addr都必须是NBD,且可视化的数字一般都是HBD,如端口号23
     21     sin.sin_family = AF_INET; //协议,ipv4
     22     sin.sin_port   = htons(SERV_PORT); //将端口号转化为NBD
     23 
     24     /*优化1:让服务器能绑定在任意IP上*/
     25     sin.sin_addr.s_addr = htonl(INADDR_ANY);
     26 
     27     /*2.2 绑定 */
     28     if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
     29     {
     30         perror("bind");
     31         exit(1);
     32     }
     33 
     34     /*3. 把套接字设为监听模式,准备接收客户请求*/
     35     if(listen(fd, BACKLOG) < 0)
     36     {
     37         perror("listen");
     38         exit(1);
     39     }
     40     printf("Severing start...OK!
    ");
     41 
     42     /*4.等待客户请求到来,当请求到来后,接收请求,返回一个基于此次的新的套接字 */
     43     int newfd = -1;
     44 
     45     /*优化3:用多进程/多线程处理已经建立好连接的客户端数据*/
     46     pthread_t tid;
     47 
     48     struct sockaddr_in cin;
     49     socklen_t addrlen = sizeof(cin);
     50     while(1)
     51     {
     52         /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址与端口号 */
     53         //获取客户端信息
     54         if((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen))<0)
     55         {
     56             perror("accept");
     57             exit(1);
     58         }
     59         //读出客户端信息,并将HBD转为NBD
     60         char ipv4_addr[16];
     61         if(!inet_ntop(AF_INET, (void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
     62         {
     63             perror("inet_ntop");
     64             exit(1);
     65         }
     66         //打印客户端的IP和端口号
     67         printf("Client(%s,%d) is connected!
    ",ipv4_addr,ntohs(cin.sin_port));
     68         //打印完之后创建一个线程
     69         pthread_create(&tid, NULL, (void *)cli_data_handle, (void *)&newfd);
     70     }
     71 
     72     close(fd);
     73     return 0;
     74 
     75 }
     76 
     77 void cli_data_handle(void *arg)
     78 {
     79     int newfd = *(int *)arg;
     80 
     81     printf("thread:newfd = %d
    ", newfd);
     82 
     83     /*5. 读写数据*/
     84     int ret = -1;
     85     char buf[BUFSIZ];
     86 
     87     while(1)
     88     {
     89     bzero(buf, BUFSIZ);
     90         do{
     91             ret = read(newfd, buf, BUFSIZ-1);
     92         }while(ret < 0 && EINTR == errno);
     93         if(ret < 0)
     94         {
     95             perror("read");
     96             exit(1);
     97         }
     98 
     99         if(!ret) //对方已经关闭
    100         {
    101             break;
    102         }
    103         printf("Receive data: %s",buf);
    104 
    105         if(!strncasecmp(buf,QUIT_STR, strlen(QUIT_STR)))
    106         {
    107             printf("Client is exiting!
    ");
    108             break;
    109         }
    110     }
    111     close(newfd);
    112 
    113 }
    pthread_tcp.c
     1 /*运行方式: ./client serv_ip serv_port */
     2 #include "net.h"
     3 
     4 void usage(char *s)
     5 {
     6     printf("
    %s serv_ip serv_port
    ",s);
     7     printf("
    	 serv_ip:serv ip address");
     8     printf("
    	 serv_port: sever port(>5000)
    
    ");
     9 }
    10 
    11 int main(int argc, char **argv)
    12 {
    13     int fd = -1;
    14 
    15     int port = -1;
    16     struct sockaddr_in sin;
    17     
    18     if(argc != 3)//参数错误检测
    19     {
    20         usage(argv[0]);
    21         exit(1);
    22     }
    23     /*1.创建sock fd */
    24     if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    25     {
    26         perror("socket");
    27         exit(1);
    28     }
    29 
    30     port = atoi(argv[2]);
    31     if(port < 5000)
    32     {
    33         usage(argv[0]);
    34         exit(1);
    35     }
    36     /*2.连接服务器 */
    37     /*2.1 填充struct sockaddr_in结构体变量*/
    38     bzero(&sin, sizeof(sin)); //初始值置零
    39     sin.sin_family = AF_INET; //
    40     sin.sin_port = htons(port); //转化为NBD
    41 #if 0
    42     sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
    43 #else
    44     if(inet_pton(AF_INET, argv[1],(void *)&sin.sin_addr.s_addr) != 1)
    45     {
    46         perror("inet_pton");
    47         exit(1);
    48     }
    49 #endif
    50 
    51     if(connect(fd,(struct sockaddr *)&sin, sizeof(sin)) < 0)
    52     {
    53         perror("connect");
    54         exit(1);
    55     }
    56 
    57     printf("Client starting ...
    ");
    58 
    59     /*3.读写数据*/
    60     char buf[BUFSIZ];
    61     int ret = -1;
    62     while(1)
    63     {
    64         bzero(buf,BUFSIZ);
    65         if(fgets(buf, BUFSIZ-1, stdin) == NULL)
    66         {
    67             continue;
    68         }
    69         do{
    70             ret = write(fd, buf, strlen(buf));
    71         }while(ret < 0 && EINTR == errno);
    72         if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))
    73         {
    74             printf("Clinet is exiting!
    ");
    75             break;
    76         }
    77 
    78     }
    79 
    80     /*4.关闭套接字 */
    81     close(fd);
    82 
    83         return 0;
    84 }
    client.c
    1 all:
    2     gcc  -o client client.c -lpthread
    3     gcc  -o pthread_tcp pthread_tcp.c -lpthread
    4 
    5 clean:
    6     rm  *.elf
    Makefile

    测试结果:

     2、多进程服务器

    在多进程处理模型中

    父进程主要用来监听客户端的连接请求,当有客户端请求连接时,accept()返回用于通信的文件描述符fd,父进程fock创建子进程,并负责回收子进程。
    子进程获得父进程数据空间、堆和栈的复制品,拿到文件描述符fd后,和客户端进行数据交互。
    通信结束后,子进程exit()结束,主进程进程资源的回收。

    TCP多进程实例:

      1 #include "net.h"
      2 void cli_data_handle(void *arg);
      3 void sig_child_handle(int signo)
      4 {
      5     if(SIGCHLD == signo)
      6     {
      7         waitpid(-1, NULL, WNOHANG);
      8     }
      9 }
     10 
     11 int main()
     12 {
     13     int fd = -1;
     14     struct sockaddr_in sin; //Internet环境下套接字的地址形式,对其进行操作以建立信息
     15     /*当客户端退出时,会通过信号机制回收子进程,防止变成僵尸进程*/    
     16     signal(SIGCHLD, sig_child_handle); //回收子进程
     17 
     18     /*1、创建套接字描述符fd */
     19     if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
     20     {
     21         perror("socket");
     22         exit(1);
     23     }
     24     
     25     /*优化4:允许绑定地址快速重用*/
     26     int b_reuse = 1;
     27     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
     28 
     29     /*2、将套接字绑定到一个本地地址与端口上*/
     30     
     31     /*2.1 填充struct sockaddr_in结构体变量*/
     32     bzero(&sin, sizeof(sin)); //初始值置零
     33     //sin_port和sin_addr都必须是NBD,且可视化的数字一般都是HBD,如端口号23
     34     sin.sin_family = AF_INET; //协议,ipv4
     35     sin.sin_port   = htons(SERV_PORT); //将端口号转化为NBD
     36 
     37     /*优化1:让服务器能绑定在任意IP上*/
     38     sin.sin_addr.s_addr = htonl(INADDR_ANY);
     39 
     40     /*2.2 绑定 */
     41     if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
     42     {
     43         perror("bind");
     44         exit(1);
     45     }
     46 
     47     /*3. 把套接字设为监听模式,准备接收客户请求*/
     48     if(listen(fd, BACKLOG) < 0)
     49     {
     50         perror("listen");
     51         exit(1);
     52     }
     53     printf("Severing start...OK!
    ");
     54 
     55     /*4.等待客户请求到来,当请求到来后,接收请求,返回一个基于此次的新的套接字 */
     56     int newfd = -1;
     57 
     58     struct sockaddr_in cin;
     59     socklen_t addrlen = sizeof(cin);
     60     while(1)
     61     {
     62         pid_t pid = -1;
     63         //获取客户端信息
     64         if((newfd = accept(fd, (struct sockaddr *)&cin, &addrlen)) < 0)
     65         {
     66             perror("accept");
     67             break;
     68         }
     69         /*创建一个子进程用于处理已经建立连接的客户的交互数据 */
     70         if((pid = fork()) < 0)
     71         {
     72             perror("fork");
     73             break;
     74         }
     75 
     76         if(0 == pid) //子进程
     77         {
     78             close(fd); //关闭不需要的文件描述符,节省资源
     79             //读出客户端信息,并将HBD转为NBD
     80             char ipv4_addr[16];
     81             if(!inet_ntop(AF_INET, (void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
     82             {
     83                 perror("inet_ntop");
     84                 exit(1);
     85             }
     86             
     87             //打印客户端的IP和端口号
     88             printf("Client(%s,%d) is connected!
    ",ipv4_addr,ntohs(cin.sin_port));
     89             cli_data_handle(&newfd);
     90             return 0; //客户端数据处理完毕,return跳出循环
     91 
     92         }else //pid > 0, 父进程
     93         {
     94             close(newfd); //关闭不需要的文件描述符
     95         }
     96     }
     97     close(fd);
     98     return 0;
     99 
    100 }
    101 
    102 void cli_data_handle(void *arg)
    103 {
    104     int newfd = *(int *)arg;
    105 
    106     printf("Child handle process:newfd = %d
    ", newfd);
    107 
    108     /*5. 读写数据*/
    109     int ret = -1;
    110     char buf[BUFSIZ];
    111 
    112     while(1)
    113     {
    114     bzero(buf, BUFSIZ);
    115         do{
    116             ret = read(newfd, buf, BUFSIZ-1);
    117         }while(ret < 0 && EINTR == errno);
    118         if(ret < 0)
    119         {
    120             perror("read");
    121             exit(1);
    122         }
    123 
    124         if(!ret) //对方已经关闭
    125         {
    126             break;
    127         }
    128         printf("Receive data: %s",buf);
    129 
    130         if(!strncasecmp(buf,QUIT_STR, strlen(QUIT_STR)))
    131         {
    132             printf("Client is exiting!
    ");
    133             break;
    134         }
    135     }
    136     close(newfd);
    137 
    138 }
    process_tcp.c
     1 /*运行方式: ./client serv_ip serv_port */
     2 #include "net.h"
     3 
     4 void usage(char *s)
     5 {
     6     printf("
    %s serv_ip serv_port
    ",s);
     7     printf("
    	 serv_ip:serv ip address");
     8     printf("
    	 serv_port: sever port(>5000)
    
    ");
     9 }
    10 
    11 int main(int argc, char **argv)
    12 {
    13     int fd = -1;
    14 
    15     int port = -1;
    16     struct sockaddr_in sin;
    17     
    18     if(argc != 3)//参数错误检测
    19     {
    20         usage(argv[0]);
    21         exit(1);
    22     }
    23     /*1.创建sock fd */
    24     if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    25     {
    26         perror("socket");
    27         exit(1);
    28     }
    29 
    30     port = atoi(argv[2]);
    31     if(port < 5000)
    32     {
    33         usage(argv[0]);
    34         exit(1);
    35     }
    36     /*2.连接服务器 */
    37     /*2.1 填充struct sockaddr_in结构体变量*/
    38     bzero(&sin, sizeof(sin)); //初始值置零
    39     sin.sin_family = AF_INET; //
    40     sin.sin_port = htons(port); //转化为NBD
    41 #if 0
    42     sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
    43 #else
    44     if(inet_pton(AF_INET, argv[1],(void *)&sin.sin_addr.s_addr) != 1)
    45     {
    46         perror("inet_pton");
    47         exit(1);
    48     }
    49 #endif
    50 
    51     if(connect(fd,(struct sockaddr *)&sin, sizeof(sin)) < 0)
    52     {
    53         perror("connect");
    54         exit(1);
    55     }
    56 
    57     printf("Client starting ...
    ");
    58 
    59     /*3.读写数据*/
    60     char buf[BUFSIZ];
    61     int ret = -1;
    62     while(1)
    63     {
    64         bzero(buf,BUFSIZ);
    65         if(fgets(buf, BUFSIZ-1, stdin) == NULL)
    66         {
    67             continue;
    68         }
    69         do{
    70             ret = write(fd, buf, strlen(buf));
    71         }while(ret < 0 && EINTR == errno);
    72         if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR)))
    73         {
    74             printf("Clinet is exiting!
    ");
    75             break;
    76         }
    77 
    78     }
    79 
    80     /*4.关闭套接字 */
    81     close(fd);
    82 
    83         return 0;
    84 }
    client.c
    1 all:
    2     gcc  -o client client.c 
    3     gcc  -o process_tcp process_tcp.c 
    4 
    5 clean:
    6     rm  *.elf
    Makefile

    测试结果:

  • 相关阅读:
    用iptables封杀内网的bt软件
    FreeBSD 利用IPFW实现限制局域网使用QQ
    网络安全设备Bypass功能介绍及分析
    活用Windows Server 2008系统的几种安全功能
    恢复mysql管理员密码
    远程控制Windows2003下安装Pcanywhere导致Awgina.dll出错的解决办法
    Ubuntu 11.04 LAMP+JSP环境安装过程
    hbase首次导入大批次的数据成功!
    Chubby是什么?
    DP-Triangle
  • 原文地址:https://www.cnblogs.com/y4247464/p/12182801.html
Copyright © 2020-2023  润新知