华清远见嵌入式学习day22——并发服务器

【1】poll
    函数
    引入poll的目的:
        因为select最多只能监听1024个文件描述符;
    #include <poll.h>
    int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    参数:
        fds:
             struct pollfd {
               int   fd;         /* file descriptor */  需要关心的文件描述符
               short events;     /* requested events */ 产生的事件
               short revents;    /* returned events */  
           };
           
    监听数据是从键盘来的还是从鼠标来的?用poll实现;
    1. 创建文件描述符集合; 
        struct pollfd fds[2];
    2. 把关心的文件描述符加入到集合当中;
        fds[0].fd = 0; //键盘
        fds[0].events = POLLIN;
        
        fds[1].fd = fd; //鼠标
        fds[1].events = POLLIN;
        
    3. 调用poll函数
        poll(fds,2,1000); //1000表示1秒,以毫秒为单位。不想设置超时时间 -1;

    4. if(fds[0].revents == POLLIN)
        
       if(fds[1].revents == POLLIN)
    
    
    任务:使用poll,想监听标准输入0,还想监听sockfd,acceptfd用于数据通信的套接字;
            
【2】服务器模型
    1、循环服务器:
        依次只能处理一个客户端,等这个客户端所有请求都完成后(客户端关闭),
        才能处理下一个客户端
        
        缺点: 循环服务器所处理的客户端,不能做耗时动作
        
        socket(...); 
        bind(...); 
        listen(...); 
        while(1) 
        {    
            accept(...); 
            while(1) 
            {          
                recv(...); 
                process(...); 
                send(...); 
            }     
            close(...); 
        }
    2、并发服务器:
        可以同时处理多个客户端的请求,创建子进程/子线程来处理跟客户端的请求

        流程如下:
        void handler(int sigo)
        {
            while (waitpid(-1, NULL, WNOHANG) > 0);     //一个SIGCHLD可能对应多个僵尸进程,循环收尸
        }
        int sockfd = socket(...); 
        bind(...); 
        listen(...);
        signal(SIGCHLD, handler);  //注册SIGCHLD信号,当产生SIGCHLD信号时调用handler函数
        while(1) { 
            int connfd = accept(...); 
            if (fork() == 0) { 
                close(sockfd);
                while(1) 
                { 
                    recv(...); 
                    process(...); 
                    send(...); 
                }
                close(connfd);           
                exit(...); 
            } 
            close(connfd); 
        }
        效果: 父进程--只做链接    子进程--跟客户端的通信(耗时)
    
【3】三次握手和四次挥手
    ACK: 确认包;握手
    SYN:  同步包;握手
    FIN: 结束包;挥手
        
三次握手:
    在建立连接的时候进行。客户端主动方,连接服务器;
    第一次握手(connect),有客户端向服务器发送SYN同步包,告诉服务器我要连接了
    第二次握手,服务器收到SYN后,要回复一个确认包,告诉客户端已经收到了,
                并发送同步包(确认一下服务器发的包是否能发出去);
    第三次握手,客户端发送确认包给服务器;
    
四次挥手:
    TCP套接字是全双工的;close时,两端都要关闭
    客户端和服务器都可以作为主动方;
    
    第一次挥手;客户端调用close,发送FIN包;
    第二、三次挥手;服务器端,先回复确认包,确认已经收到断开连接的请求,
            并且再发FIN结束包;
    第四次挥手;客户端再次发送ACK确认包;
    
【4】setsockopt:
NAME
       getsockopt, setsockopt - get and set options on sockets

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
        功能:获取套接字的属性;
            
            
                      
                      
       int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
        功能:设置套接字的属性;
        参数:    
            sockfd : sockfd  (socket()的返回值)
            level:  
                SOL_SOCKET  :   应用层;
                IPPROTO_IP  :  IP层/网络层
                IPPRO_TCP   :  传输层
            optname:
                SO_REUSEADDR  允许重用本地地址和端口
            optval:  
                    &值
            optlen:
                    第四个参数值的大小
    ===================================
    //设置允许重用本地地址和端口
    int on = 1;
    if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
    {
        perror("setsockopt");
        return -1;
    } 
    
【5】超时检测
    1、select poll可以通过参数设置超时时间 struct timeval ;
    
    2. 通过setsockopt设置超时时间
    SO_RCVTIMEO   接收超时  struct timeval
    
    设置接收超时时间为两秒,TCP代码来写;
    2秒  
    struct timval tv = {2,0};
    
    #if 0
        struct timeval tv = {1};
        if (0 > setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
        {
            perror("setsockopt");
            return -1;
        }
    3、    alarm(5),使用闹钟定时器实现超时检测;
    
        void handler(int sign)
        {
            printf("timeout....\n");
        }
        
        struct sigaction  act;
        sigaction(SIGALRM, NULL, &act);   //获取SIGALRM原有的处理动作
        act.sa_handler = handler;   //相当于signal第二个参数
        act.sa_flags &= ~SA_RESTART;  //关闭重启
        sigaction(SIGALRM, &act, NULL);  //设置SIGALRM的处理动作
          
        while(1)
        {
            alarm(5);
            if (recv(,,,) < 0) ……
        }
    
    
 

猜你喜欢

转载自blog.csdn.net/UemTuBXuR/article/details/89188739