当前位置:编程学习 > 网站相关 >>

poll2


数据包 粘包 一个包 两次read
Read :可能并没有把confd 对应的缓冲区的数据读完,那么connfd仍然是活跃的,
我们应该将读到的数据保存在connfd的应用层的缓冲区(每一次都进行追加)。如何解析协议,我们让应用层的解析协议自己来解析
 
 忙等待 :
        假设客户端关注了socket的POLLOUT事件,而此时内核缓冲区有空闲,但是应用层却没数据可写,那么内核将会处于忙等待状态(busy waitting loop),一直发送POLLOUT事件。
        解决的方法是:我们要看应用层的缓冲区,如果应用层的缓冲区有数据发,那么我们应该关注POLLOUT事件,要不然就取消POLLOUT事件的关注。
        对于客户端在读数据时,我们也应该采用相应的方法,如果应用层的空间空闲时,我们就关注POLLIN事件,要不然就取消POLLIN事件。
 
请求量大并发时
            准备一个空闲的文件描述符(备胎)。遇到这种情况,先关闭这个空闲文件,获得一个文件描述符名额;再accept(2)拿到socket连接的文件描述符;随后立刻close(2),这样就优雅地断开了与客户端的连接;最后重新打开空闲文件,把“坑”填上,以备再次出现这种情况时使用。
 
code
 
#include <unistd.h>   
#include <sys/types.h>   
#include <fcntl.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <arpa/inet.h>   
#include <signal.h>   
#include <sys/wait.h>   
#include <poll.h>   
   
#include <stdlib.h>   
#include <stdio.h>   
#include <errno.h>   
#include <string.h>   
   
#include <vector>   
#include <iostream>   
   
#define ERR_EXIT(m) \   
        do \   
        { \   
                perror(m); \   
                exit(EXIT_FAILURE); \   
        } while(0)   
   
typedef std::vector<struct pollfd> PollFdList;   
   
int main(void)   
{   
    signal(SIGPIPE, SIG_IGN);   
    signal(SIGCHLD, SIG_IGN);   
   
//备胎fd   
    int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);   
    int listenfd;   
   
    //if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)   
    if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < 0)   
        ERR_EXIT("socket");   
   
    struct sockaddr_in servaddr;   
    memset(&servaddr, 0, sizeof(servaddr));   
    servaddr.sin_family = AF_INET;   
    servaddr.sin_port = htons(5188);   
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   
   
    int on = 1;   
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)   
        ERR_EXIT("setsockopt");   
   
    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)   
        ERR_EXIT("bind");   
    if (listen(listenfd, SOMAXCONN) < 0)   
        ERR_EXIT("listen");   
   
    struct pollfd pfd;   
    pfd.fd = listenfd;   
    pfd.events = POLLIN;   
   
    PollFdList pollfds;   
    pollfds.push_back(pfd);   
   
    int nready;   
   
    struct sockaddr_in peeraddr;   
    socklen_t peerlen;   
    int connfd;   
   
    while (1)   
    {   
        nready = poll(&*pollfds.begin(), pollfds.size(), -1);   
        if (nready == -1)   
        {   
            if (errno == EINTR)   
                continue;   
   
            ERR_EXIT("poll");   
        }   
        if (nready == 0)    // nothing happended   
            continue;   
   
        if (pollfds[0].revents & POLLIN)   
        {   
            peerlen = sizeof(peeraddr);   
            connfd = accept4(listenfd, (struct sockaddr*)&peeraddr,   
                        &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);   
   
/*          if (connfd == -1)  
                ERR_EXIT("accept4");  
*/  
   
            // 这里备胎fd起到作用了   
            if (connfd == -1)   
            {   
                if (errno == EMFILE)   
                {   
                    close(idlefd);   
                    idlefd = accept(listenfd, NULL, NULL);   
                    close(idlefd);   
                    idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);   
                    continue;   
                }   
                else  
                    ERR_EXIT("accept4");   
            }   
   
            pfd.fd = connfd;   
            pfd.events = POLLIN;   
            pfd.revents = 0;   
            pollfds.push_back(pfd);   
            --nready;   
   
            // 连接成功   
            std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<   
                " port="<<ntohs(peeraddr.sin_port)<<std::endl;   
            if (nready == 0)   
                continue;   
        }   
   
        //std::cout<<pollfds.size()<<std::endl;   
        //std::cout<<nready<<std::endl;   
        for (PollFdList::iterator it=pollfds.begin()+1;   
            it != pollfds.end() && nready >0; ++it)   
        {   
                if (it->revents & POLLIN)   
                {   
                    --nready;   
                    connfd = it->fd;   
                    char buf[1024] = {0};   
                    int ret = read(connfd, buf, 1024);   
                    if (ret == -1)   
                        ERR_EXIT("read");   
                    if (ret == 0)   
                    {   
                        std::cout<<"client close"<<std::endl;   
                        it = pollfds.erase(it);   
                        --it;   
   
                        close(connfd);   
                        continue;   
                    }   
   
                    std::cout<<buf;   
                    //这里要根据业务看需不需要使用POLLOUT事件   
                    write(connfd, buf, strlen(buf));   
   
                }   
        }   
    }   
   
    return 0;   
}  

 

 
补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,