• 基于自定义协议的服务器高并发处理之:多线程模型


    https://www.cnblogs.com/lan0725/p/11634267.html 只是简单的处理,服务器返回客户端一个时间,然后关闭了socket。

    如果要进行双向通信,服务器势必要调用read函数,而read默认阻塞,那么如果客户端不向服务器发送数据,则主线程一直阻塞,其它客户端无法连接成功。这就需要处理高并发问题。

    服务器高并发处理的三种方式

    1. 多进程 https://www.cnblogs.com/lan0725/p/11634709.html
    2. 多线程 https://www.cnblogs.com/lan0725/p/11639142.html
    3.  I/O多路复用

     目录 makefile的设置都在多进程里面有介绍 https://www.cnblogs.com/lan0725/p/11634709.html

    多线程的效率明显高于多进程,而且因为线程共享大部分进程空间,而多进程复制空间,从而降低了系统开销。

    1. 主线程只是负责accepte,然后创建带分离属性的子线程。
    2. 每个客户端连接维护一个带分离属性的线程,在此线程里做双向通信。即有多少客户端连接,就会开辟多少子线程。

    客户端和多进程的用同一个。

    实际上只在main函数accept的时候有区别,创建了分离线程,免去了主线程join等待回收线程。具体的do_something没有区别。

    多线程服务端 echo_tcp_server_thread.c :

      1 #include <sys/socket.h>
      2 #include <sys/types.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <unistd.h>
      6 #include <signal.h>
      7 #include <time.h>
      8 #include <string.h>
      9 #include <netdb.h>
     10 #include <arpa/inet.h>
     11 #include "msg.h"
     12 #include <errno.h>
     13 #include <pthread.h>
     14 
     15 #define SERVER_PORT 8888
     16 #define LISTEN_QUEUE_SISE 10
     17 
     18 int socketfd;
     19 
     20 void signal_handler(int signo)
     21 {
     22     if (signo == SIGINT)
     23     {
     24         printf("this serveice close
    ");
     25         close(socketfd);
     26         exit(1);
     27     }
     28 }
     29 
     30 void print_clientinfo(int fd, const char* type)
     31 {
     32     struct sockaddr_in addr;
     33     memset(&addr, 0, sizeof(addr));
     34     socklen_t len = sizeof(addr);
     35 
     36     if(getpeername(fd, (struct sockaddr*)&addr, &len) < 0)
     37     {
     38         perror("getpeername error");
     39         return;
     40     }
     41 
     42     char ipstr[16];
     43     memset(ipstr, 0, sizeof(ipstr));
     44     // 将地址从网络字节序转换为点分十进制
     45     inet_ntop(AF_INET, &addr.sin_addr.s_addr, ipstr, sizeof(ipstr));
     46 
     47     printf("%s(%d) %s 
    ", ipstr, ntohs(addr.sin_port), type);
     48 }
     49 
     50 void do_something(int fd)
     51 {
     52     char buff[1024];
     53     while(1)
     54     {
     55         //读取客户端发送过来的内容
     56         memset(buff, 0, sizeof(buff));
     57         size_t size = read_msg(fd, buff, sizeof(buff));
     58         if (size < 0)
     59         {
     60             // 协议出错 跳出while 回到main 结束子进程 
     61             perror("read_msg error");
     62             break;
     63         }
     64         else if (size == 0)
     65         {
     66             /* 客户端断开了连接 挂掉了 跳出while 会结束子线程
     67              **/
     68             break;
     69         }
     70         
     71         printf("recev from client:%s
    ",buff);
     72 
     73         //将数据写回
     74         if (write_msg(fd, buff, sizeof(buff)) < 0)
     75         {
     76             if(errno == EPIPE)
     77             {
     78                 /* 客户端断开了连接  
     79                  * 产生SIGPIPE信号 
     80                  * 将error设置为EPIPE
     81                  * 所以可在此判断errno 也可捕捉SIGPIPE信号
     82                  * 俗称管道爆裂
     83                  * 跳出while 会结束子线程
     84                  **/
     85                 break;
     86             }
     87             
     88 
     89             perror("write_msg error");
     90         }
     91     }
     92 }
     93 
     94 void* threadWork(void* arg)
     95 {
     96     int fd = (int)arg;
     97     print_clientinfo(fd, "connected");
     98     do_something(fd);
     99     print_clientinfo(fd, "closed");
    100     close(fd);
    101     return NULL;
    102 }
    103 
    104 
    105 int main(int argc, char const *argv[])
    106 {
    107     if (signal(SIGINT, signal_handler) == SIG_ERR)
    108     {
    109         perror("signal error");
    110         exit(1);
    111     }
    112 
    113     // 1 sokect
    114     // AF_INET ipv4
    115     // SOCK_STREAM tcp
    116     if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    117     {
    118         perror("socket error");
    119         exit(1);
    120     }
    121 
    122     // 2 bind 绑定本地地址和端口
    123     struct sockaddr_in serveraddr;
    124     memset(&serveraddr, 0, sizeof(serveraddr));
    125     serveraddr.sin_family = AF_INET;//ipv4
    126     serveraddr.sin_port = htons(SERVER_PORT); //端口
    127     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//响应任意网卡的请求
    128     if(bind(socketfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
    129     {
    130         perror("bind error");
    131         exit(1);
    132     }
    133 
    134     // 3 listen 启动监听 通知系统接受来自客户端的连接 准备好连接队列
    135     if(listen(socketfd, LISTEN_QUEUE_SISE) < 0)
    136     {
    137         perror("listen error");
    138         exit(1);
    139     }
    140 
    141     pthread_t thread;
    142     //设置分离属性相关 不需要主线程调用pthread_join回收子线程
    143     pthread_attr_t attr;
    144     pthread_attr_init(&attr);
    145     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    146     int retno;
    147     while(1)
    148     {    
    149         // 4 accept 从队列拿出第一个
    150         int clientfd = accept(socketfd, NULL, NULL);
    151         if (clientfd  < 0)
    152         {
    153             perror("accept error");
    154             continue;
    155         }
    156         // 5 创建分离属性的子线程 在子线程中做read/write
    157         if( (retno = pthread_create(&thread, &attr, 
    158             threadWork, (void*)clientfd)) != 0 )
    159         {
    160             printf("error nuber:%d, %s
    ", retno, strerror(retno));
    161             close(clientfd);
    162             continue;
    163         }
    164 
    165     }
    166     
    167     // 销毁线程属性
    168     pthread_attr_destroy(&attr);
    169     // 6 close
    170     close(socketfd);
    171     return 0;
    172 }
  • 相关阅读:
    Docker 部署zookeeper3.4
    Redis 3.2 生产环境集群部署
    Prometheus入门到放弃(7)之redis_exporter部署
    Docker部署ELK 7.0.1集群之Kibana安装介绍
    Docker部署ELK 7.0.1集群之Logstash安装介绍
    Docker部署ELK 7.0.1集群之Elasticsearch安装介绍
    Node web 框架
    写一个简单的选择器( 方便小项目使用 )
    Node web 框架
    Node进阶
  • 原文地址:https://www.cnblogs.com/lan0725/p/11639142.html
Copyright © 2020-2023  润新知