嵌入式学习日记(35)TCP并发服务器构建

发布于:2025-08-28 ⋅ 阅读:(13) ⋅ 点赞:(0)

 单循环服务器:服务端同一时刻只能处理一个客户端的任务

并发服务器:服务端同一时刻可以处理多个客户端的任务


TCP并发服务器构建:

TCP:
1. 建立连接,一对一

TCP服务端并发模型:

1.  多进程
      进程资源开销大;安全性高
2.  多线程
      线程相对与进程资源开销小,相同资源环境下,并发量比进程大。

3.  线程池
     为了解决多线程或者多进程模型,在服务器运行过程,频繁创建和销毁线程(进程)带来的时间消耗问题。
     基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程框架。

4.  IO多路复用
      I-->O:fd
      对多个文件描述符的读写可以复用一个进程。

      在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时监测。

      fgets(stdin);
      recv(connfd);
      阻塞IO模式:
               1. 多个任务之间是同步的效果

      1)select
      2)poll
      3)epoll
      

select实现IO多路复用:

      1. 创建文件描述符集合                  fd_set
      2. 添加关注的文件描述符到集合   FD_SET();
      3. 使用select传递集合表给内核,内核开始监测事件  select()
      4. 当内核监测到事件时,应用层select将解除阻塞,并获得相关的事件结果
      5. 根据select返回的结果做不同的任务处理


       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);


       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
       功能:传递文件描述符结合表给内核并等待获取事件结果
       参数:
               nfds : 关注的最大文件描述符+1
               readfds:读事件的文件描述符集合
              writefds:写事件的文件描述符集合
              exceptfds:其他事件的文件描述符集合
              timeout:设置select监测时的超时时间
                               NULL : 不设置超时时间(select一直阻塞等待)

        返回值:
            成功:返回内核监测的到达事件的个数
            失败:-1
            0 : 超时时间到达,但没有事件发生,则返回0

利用select搭建并发服务器
 

#include "head.h"

#define SER_PORT 50001
#define SER_IP "192.168.0.180"

int init_tcp_ser()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket error");
        return -1;
    }
    
    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SER_PORT);
    seraddr.sin_addr.s_addr = inet_addr(SER_IP);

    int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("bind error");
        close(sockfd);
        return -1;
    }
    ret = listen(sockfd,5);
    if(ret < 0)
    {
        perror("listen error");
        close(sockfd);
        return -1;
    }
    return sockfd;
}

int main()
{
    struct sockaddr_in cliaddr;
    socklen_t clilen = sizeof(cliaddr);
    int sockfd = init_tcp_ser();
    if(sockfd < 0)
    {
        return -1;
    }
    fd_set sockfds;
    fd_set sockfdstmp;
    FD_ZERO(&sockfds);
    FD_SET(sockfd,&sockfds);
    int maxfd = sockfd;
    char buff[1024] = {0};
    while(1)
    {
        sockfdstmp = sockfds;
        int cnt = select(maxfd + 1,&sockfdstmp,NULL,NULL,NULL);
        if(cnt < 0)
        {
            perror("select error:");
            return -1;
        }
        if(FD_ISSET(sockfd,&sockfdstmp))
        {
            int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clilen);
            if(connfd < 0)
            {
                perror("accept error:");
                return -1;
            }
            FD_SET(connfd,&sockfds);
            maxfd = maxfd > connfd ? maxfd : connfd;
        }
        for(int i = sockfd + 1;i <= maxfd;++i)
        {
            if (FD_ISSET(i, &sockfdstmp))
            {
                memset(buff,0,sizeof(buff));
                ssize_t cnt = recv(i,buff,sizeof(buff),0);
                if(cnt < 0)
                {
                    perror("receive error:");
                    FD_CLR(i,&sockfdstmp);
                    close(i);
                    continue;
                }
                else if(cnt == 0)
                {
                    printf("[%s:%d]:offline\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
                    FD_CLR(i, &sockfds);
					close(i);
					continue;
                }
                printf("[%s:%d]:%s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);
                strcat(buff,"->ok");
                cnt = send(i,buff,strlen(buff),0);
                if(cnt < 0)
                {
                    perror("send error:");
                    FD_CLR(i,&sockfdstmp);
                    close(i);
                    continue;
                }
            }
        }
    }
    close(sockfd);
    return 0;
}