sendfile 和epool

来源:互联网 发布:php静态调用方法 编辑:程序博客网 时间:2024/05/24 22:43


ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

linux下支持sendfile,这样发送文件,可以直接通过内核调用,减少了到应用程序读写两道操作程序,并降低了内存的使用。
看man文档,sendfile在2.6.9以后,infd只能是普通文件,outfd必须是socket。
经过试验,在linux9,as4,as5下nfs挂载的文件,都可以使用sendfile发送。
sendfile,对阻塞的sockfd发送,也会阻塞程序。
当设置outfd为非阻塞模式的时候,测试sendfile 只发送了106496字节(可以more /proc/sys/net/core/wmem_default ),而不能完成整个文件的发送。
因此,在epoll模式边缘触发非阻塞模式下,必须监听outfd可写,记录sendfile当前发送的字节,下一次调用时,从上次发送完成的地址开始。
offset为空,则sendfile将根据所读取的字节数来自动设定infd的文件偏移,下一次从偏移读取。如果设置有offset,则不会改变文件偏移指针。
使用epoll测试的时候,设置边缘触发,每次epoll_wait返回,sendfile只发送小量字节,在没有达到EAGAIN返回前(写阻塞前),继续epoll_wait,发现可以获得下一次的epoll写事件。
但是反过来,如果监听的是读取事件,只读取小量字节,剩余少量字节没有完全读取的话,epoll将不会再次触发读事件。因此对待读操作,一般epoll_wait之后,都要while(没有EAGIN或中断){读},只要是EAGAIN或中断,就接着读!
sendfile,在对方关闭socket之后,如果还处在发送的过程中,则产生SIGPIPE信号。和write一样。
最新测试发现:
针对tcp socket,获取epoll的write通知后,如果不写到EAGAIN,epoll_wait将不会再获得可写事件触发!
因此sendfile必须不断写,直到EAGAIN为止!


测试代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/epoll.h>


int main(int argc,char*argv[])
{
    if (argc!=3)
    {
        perror("Usage:./a.out pathname unixsckname\n");
        return -1;
    }
    pid_t pid;
    pid=fork();
    if (pid>0){//parent process
        int outfd,infd;
        infd=open(argv[1],O_RDONLY);
        if (infd ==-1){
            printf("open %s fail\n",argv[1]);
            return -1;
        }
        sleep(1);
        outfd=socket(AF_UNIX,SOCK_STREAM,0);
        int on=1;
        if (setsockopt(outfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){
            perror("set sockopt outfd fail");
            exit(EXIT_FAILURE);
        }
        struct sockaddr_un sckaddr;
        bzero(&sckaddr,sizeof(struct sockaddr));
        sckaddr.sun_family=AF_UNIX;
        strncpy(sckaddr.sun_path,argv[2],sizeof(sckaddr.sun_path)-1);
        if(connect(outfd,(struct sockaddr *)&sckaddr,sizeof(struct sockaddr_un))==-1){
            printf("connect socket failer,%s\n",strerror(errno));
            return -1;
        }


        if (outfd ==-1){
            printf("open %s fail\n",argv[1]);
            return -1;
        }
        struct stat stabuf;
        if( fstat(infd,&stabuf)!=0 ){
            perror("fstat failure");
            exit(-1);
        }
        printf("file size is%d\n",stabuf.st_size);
        ssize_t ssize;
        fcntl(outfd, F_SETFL, fcntl(outfd, F_GETFL, 0)|O_NONBLOCK);
        //fcntl(infd, F_SETFL, fcntl(infd, F_GETFL, 0)|O_NONBLOCK);
        struct epoll_event ev;
        struct epoll_event events[1];
        int epfd;
        epfd=epoll_create(10);
        ev.events = EPOLLOUT | EPOLLET;
        ev.data.fd=outfd;
        epoll_ctl(epfd,EPOLL_CTL_ADD,outfd,&ev);
        int totalsend=0;
        while(1){
            int nfds;
            nfds=epoll_wait(epfd,events,10,-1);
            if (nfds==-1){
                perror("epoll_wait");
                fprintf(stderr,"epoll_wait\n");
                break;
            }
            fprintf(stderr,"epoll_wait ok\n");
            if (events[0].data.fd == outfd){
                //ssize=sendfile(outfd,infd,0,stabuf.st_size);
                ssize=sendfile(outfd,infd,0,1024);
                fprintf(stderr,"send file %d bytes\n",ssize);
                if (ssize<0){
                    printf("sendfile error is %s",strerror(errno));
                    close(infd);
                    close(outfd);
                    break;
                }else if(errno==EAGAIN){
                    fprintf(stderr,"egain write %d bytes\n",ssize);
                    //break;
                    continue;
                }else{
                    fprintf(stderr,"error is %s\n",strerror(errno));
                    totalsend+=ssize;
                }
            }
        }
/*
        ssize=sendfile(outfd,infd,0,stabuf.st_size);
        if (ssize<0){
            printf("sendfile error is %s",strerror(errno));
            close(infd);
            close(outfd);
        }
*/
        close(outfd);
        close(infd);
        printf("file size is%d,sended %d\n",stabuf.st_size,ssize);
    }else{
        int sockfd;
        sockfd=socket(PF_UNIX,SOCK_STREAM,0);
        if (sockfd<0){
            perror("create socket fail\n");
            return;
        }
        int on=1;
        if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<0){
            perror("set sockopt fail");
            exit(EXIT_FAILURE);
        }
        struct sockaddr_un sckaddr;
        memset(&sckaddr,0,sizeof(struct sockaddr_un));
        sckaddr.sun_family=AF_UNIX;
        strncpy(sckaddr.sun_path,argv[2],sizeof(sckaddr.sun_path)-1);
        if (bind(sockfd,(struct sockaddr *)&sckaddr,sizeof(struct sockaddr_un))==-1){
            printf("bind failure %s\n",strerror(errno));
            exit(EXIT_FAILURE);
        }
        listen(sockfd,5);
        int fdacc;
        char buf[102400];
        int totalread=0;
        while (1){
            if ((fdacc=accept(sockfd,NULL,NULL))>0){
        sleep(3);
                int nread=0;
                nread=read(fdacc,buf,sizeof(buf));
                printf("read %d\n",nread);
                while( nread!=0) {
                    if (nread>0){
                        totalread += nread;
                        printf("nread is %d,totalread is %d\n",nread,totalread);
                    }
                    nread=read(fdacc,buf,sizeof(buf));
                    printf("read %d\n",nread);
                }
                sleep(1);
                printf("read total %d bytes\n",nread);
                close(fdacc);
                close(sockfd);
                return;
            }else{
                printf("accept error %s\n",strerror(errno));
                return;
            }
        }
    }
原创粉丝点击