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; 
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值