• 循环服务器


    一、服务器模型
    • 在网络程序里面,通常都是一个服务器处理多 个客户机。
    • 为了处理多个客户机的请求, 服务器端的程序有不同的处理方式。

    1、循环服务器模型

    1. socket();
    2. bind();
    3. liste();
    4. while(1)
    5. {
    6. accept();
    7. while(1)
    8. {
    9. recv
    10. ret==0;
    11. break;
    12. }
    13. close(acceptfd);
    14. }
    15. close(sockfd);

    2、并发服务器模型

    同一时刻相应多个客户端(tcp)。多进程模型/多线程模型/IO多路复用(select、poll、epoll)

    1. socket();
    2. bind();
    3. listen()
    4. while(1)
    5. {
    6. accept();
    7. if(fork()==0)
    8. {
    9.     while(1)
    10.     {
    11.     recv
    12.     ret==0;
    13.     break;
    14.     }
    15.     close(acceptfd);
    16.     exit();
    17.     }   
    18.      else
    19.     {}
    20. }

    注意:收到客户端消息后,打印下是来自哪个客户端的数据(来电显示)

    使用SIGCHLD来处理子进程结束的信号,信号函数中回收进程资源。

    3、多进程特点总结

    1. fork之前的代码被复制,但是不会重新执行一遍;fork之后的代码被复制,并且再被执行一遍。
    2. fork之后两个进程相互独立,子进程拷贝了父进程的所有代码,但内存空间独立
    3. fork之前打开文件,fork之后拿到的是同一个文件描述符,操作的是同一个文件指针

    1. /*服务器创建代码 */
    2. #include <stdio.h>
    3. #include <sys/types.h> /* See NOTES */
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h> /* superset of previous */
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <string.h>
    11. #include <signal.h>
    12. #include <sys/types.h>
    13. #include <sys/wait.h>
    14. void handler(int arg)
    15. {
    16.     waitpid(-1NULL, WNOHANG);
    17. }
    18. int main(int argc, char const *argv[])
    19. {
    20.     if (argc < 2)
    21.     {
    22.         printf("plase input \n");
    23.         return -1;
    24.     }
    25.     //1.创建套接字,用于链接
    26.     int sockfd;
    27.     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    28.     if (sockfd < 0)
    29.     {
    30.         perror("socket err");
    31.         return -1;
    32.     }
    33.     printf("sockfd:%d\n", sockfd);
    34.     //2.绑定 ip+port 填充结构体
    35.     struct sockaddr_in saddr;
    36.     saddr.sin_family = AF_INET;                   //协议族ipv4
    37.     saddr.sin_port = htons(atoi(argv[1]));        //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    38.     saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
    39.     socklen_t len = sizeof(saddr);                //结构体大小
    40.     //bind绑定ip和端口
    41.     if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    42.     {
    43.         perror("bind err");
    44.         return -1;
    45.     }
    46.     printf("bind success\n");
    47.     //3.启动监听,把主动套接子变为被动套接字
    48.     if (listen(sockfd, 6< 0)
    49.     {
    50.         perror("listen err");
    51.         return -1;
    52.     }
    53.     printf("listen success\n");
    54.     //4.阻塞等待客户端的链接请求
    55.     int acceptfd;
    56.     char buf[64];
    57.     int ret;
    58.     while (1)
    59.     {
    60.         acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
    61.         //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
    62.         if (acceptfd < 0)
    63.         {
    64.             perror("accept err");
    65.             return -1;
    66.         }
    67.         printf("client ip:%s ,port:%d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
    68.         printf("connect success\n");
    69.         signal(SIGCHLD, handler);
    70.         pid_t pid = fork(); //创建子进程
    71.         if (pid < 0)
    72.         {
    73.             perror("fork err");
    74.             return -1;
    75.         }
    76.         else if (pid == 0)
    77.         {
    78.             while (1)
    79.             {
    80.                 ret = recv(acceptfd, buf, sizeof(buf), 0);
    81.                 if (strncmp(buf, "quit"4== 0//接收到quit退出
    82.                 {
    83.                     break;
    84.                 }
    85.                 if (ret < 0)
    86.                 {
    87.                     perror("recv err.");
    88.                     return -1;
    89.                 }
    90.                 else if (ret == 0//客户端退出
    91.                 {
    92.                     printf("client exit\n");
    93.                     break;
    94.                 }
    95.                 else
    96.                 {
    97.                     printf("buf:%s\n", buf);
    98.                 }
    99.             }
    100.             close(acceptfd);//关闭子进程的文件描述副
    101.             close(sockfd);//关闭子进程的套接字文件描述符,不影响主进程套接字
    102.             exit(0);
    103.         }
    104.         close(acceptfd);//关闭主进程打开的文件描述符,
    105.                         //为下次循环开辟的文件描述符空位置,否则只能连续开辟文件描述符到1024
    106.     }
    107.     close(sockfd);
    108.     return 0;
    109. }

    4、多线程模型

    每来一个客户端连接,开一个子线程来专门处理客户端的数据,实现简单,占用资源较少,属于使用比较广泛的模型:

    1. socket();
    2. bind();
    3. listen();
    4. while(1)
    5. {
    6. accept();
    7. pthread_creat();
    8. pthread_detach();
    9. }

    1)多线程服务器

    1. #include <stdio.h>
    2. #include <sys/types.h>
    3. #include <sys/socket.h>
    4. #include <sys/socket.h>
    5. #include <netinet/in.h>
    6. #include <netinet/ip.h>
    7. #include <arpa/inet.h>
    8. #include <unistd.h>
    9. #include <stdlib.h>
    10. #include <pthread.h>
    11. void *mythread(void *arg)
    12. {
    13.     int acceptfd= *((int *)arg);
    14.     int ret;
    15.     char buf[128];
    16.     while (1)
    17.     {
    18.         ret=recv(acceptfd,buf,sizeof(buf),0);
    19.         if (ret < 0)
    20.         {
    21.             perror("recv err.");
    22.             return -1;
    23.         }else if (ret ==0)
    24.         {
    25.             printf("%d client exit\n",acceptfd);
    26.             close(acceptfd);
    27.             break;
    28.         }else
    29.         {
    30.             printf("buf:%s\n",buf);
    31.         }
    32.     }
    33.     pthread_exit(NULL);
    34. }
    35. int main(int argc, char const *argv[])
    36. {
    37.     if (argc != 2)
    38.     {
    39.         printf("please input %s \n", argv[0]);
    40.         return -1;
    41.     }
    42.     // 1.创建流式套接字
    43.     int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 链接
    44.     if (sockfd < 0)
    45.     {
    46.         perror("socket err.");
    47.         return -1;
    48.     }
    49.     printf("sockfd:%d\n", sockfd); // 3
    50.     // 填充ipv4的通信结构体
    51.     struct sockaddr_in saddr, caddr;
    52.     saddr.sin_family = AF_INET;
    53.     saddr.sin_port = htons(atoi(argv[1])); //"8888" int a= atoi("8888")//a=8888
    54.     // saddr.sin_addr.s_addr = inet_addr(argv[1]);
    55.     // 设置服务器自动获取自己主机的ip
    56.     //  saddr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY  0x00000000 "0.0.0.0"
    57.     //  saddr.sin_addr.s_addr = INADDR_ANY;
    58.     saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    59.     socklen_t len = sizeof(caddr);
    60.     // 2.绑定套接字 ip和端口(自己)
    61.     if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    62.     {
    63.         perror("bind err.");
    64.         return -1;
    65.     }
    66.     printf("bind ok.\n");
    67.     // 3.监听
    68.     if (listen(sockfd, 5< 0)
    69.     {
    70.         perror("listen err.");
    71.         return -1;
    72.     }
    73.     printf("listen ok.\n");
    74.     while (1)
    75.     {
    76.         int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
    77.         if (acceptfd < 0// 4
    78.         {
    79.             perror("accept err.");
    80.             return -1;
    81.         }
    82.         printf("acceptfd=%d\n", acceptfd);
    83.         printf("ip=%s port=%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
    84.         pthread_t tid;
    85.         pthread_create(&tid,NULL,mythread,&acceptfd);
    86.         pthread_detach(tid);//游离态
    87.     }
    88.     close(sockfd);
    89.     return 0;
    90. }

    5、IO多路复用模型

    借助select、poll、epoll机制,将新连接的客户端描述符增加到描述符表中,只需要一个线程即可处理所有的客户端连接,在嵌入式开发中应用广泛,不过代码写起了稍显繁琐。

  • 相关阅读:
    ros2 --nav2
    什么蓝牙耳机续航强?续航好的蓝牙耳机推荐
    C++——右值引用、移动构造函数、move函数、完美转发
    【性能测试】MySQL慢查询原因/排查思路+SQL优化与性能定位思路...
    OpenAI 的 CEO Sam Altman :OpenAI 正在研发下一代 AI 模型 GPT-5 但没有承诺发布时间
    c++哈希(哈希表开散列实现)
    华为网络工程师认证有了解的吗?
    “memory“ clobber
    跳一跳
    制氢厂氢气泄漏安全监测:氢气传感器守护“氢”安全
  • 原文地址:https://blog.csdn.net/m0_74937538/article/details/134406352