非阻塞I/O
I/O復用(select和poll)
信號驅動I/O
異步I/O
阻塞IO

最流行的I/O模型是阻塞I/O模型,缺省時,所有的套接口都是阻塞的。

非阻塞IO

IO復用

信號驅動IO

異步IO

2 I/O復用

如果一個或多個I/O條件滿足(例如:輸入已準備好被讀,或者描述字可以承接更多輸出的時候)我們就能夠被通知到,這樣的能力被稱為I/O復用,是由函數select和poll支持的。

I/O復用網絡應用場合
當客戶處理多個描述字
一個客戶同時處理多個套接口
如果一個tcp服務器既要處理監聽套接口,又要處理連接套接口
如果一個服務器既要處理TCP,又要處理UDP

select

         /* According to POSIX.1-2001 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       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);//清空描述字集合

作用:函數允許進程指示內核等待多個事件中的任一個發生,并僅在一個或多個事件發生或經過某指定的時間后才喚醒進程
提供了即時響應多個套接的讀寫事件
參數:
nfds:集合中最大的文件描述符 1 (指定被測試的描述字個數,它的值是要被測試的最大描述字加1,描述字0、1、2…….一直到nfds均被測試)
readfds:要檢查讀事件的容器
writefds:要檢查寫事件的容器
timeout:超時時間
返回值:返回觸發套接字的個數
中間的三個參數readset、writeset和exceptset指定我們要讓內核測試讀、寫和異常條件所需的描述字
如果我們對某個條件不感興趣,這三個參數中相應的參數就可以設為空指針

timeout參數

時間的結構體如下:

            struct timeval(
                long tv_sec;  //秒
                long tv_usec;//微秒
            );

timeout參數有三種可能

永遠等待下去:僅在有一個描述字準備好I/O時才返回,為此,我們將timeout設置為空指針
等待固定時間:在有一個描述字準備好I/O是返回,但不超過由timeout參數所指timeval結構中指定的秒數和微秒數

根本不等待:檢查描述字后立即返回,這稱為輪詢。定時器的值必須為0

fd_set參數

select使用描述字集,它一般是一個整數數組,每個數中的每一位對應一個描述字。

使用流程

使用select完成之前socket的測試,流程如下:

客戶端代碼不變。

#include < sys/types.h>     
#include < sys/socket.h>
#include < netinet/in.h>    //sockaddr_in
#include < stdio.h>
#include < string.h>

//TCP
int main()
{
    int fd;
    int ret;
    int addrLen;
    char acbuf[20] = ;
    struct sockaddr_in serAddr = {0};
    struct sockaddr_in myAddr = {0};

    //1.socket();
    fd = socket(PF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        perror(socket);
        return -1;
    }

    //2.連接connect() 服務器的地址
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(1234);
    serAddr.sin_addr.s_addr = inet_addr(192.168.159.5);
    ret = connect(fd,(struct sockaddr *)&serAddr,sizeof(struct sockaddr_in));
    if(ret == -1)
    {
        perror(connect);
        return -1;
    }

    //獲取自己的地址
    addrLen = sizeof(struct sockaddr_in);
    ret = getsockname(fd,(struct sockaddr *)&myAddr,&addrLen);
    if(ret == -1)
    {
        perror(getsockname);
        return -1;
    }
    printf(client---ip: %s , port: %d\\\\n,\\\\
                inet_ntoa(myAddr.sin_addr),ntohs(myAddr.sin_port));
    //3.通信
    while(1)
    {
        printf(send: );
        fflush(stdout);
        scanf(%s,acbuf);
        if(strcmp(acbuf,exit) == 0)
        {
            break;
        }
        write(fd,acbuf,strlen(acbuf));
    }

    //4.close()
    close(fd);
    return 0;
}

服務器端:
select.c

    #include < sys/types.h>     
    #include < sys/socket.h>
    #include < netinet/in.h>    //sockaddr_in
    #include < stdio.h>
    #include < string.h>
    #include < signal.h>
    #include < sys/select.h>
    #include < unistd.h>
    #include < sys/time.h>
    //TCP
    int main()
    {
        int fd;
        int clientfd;
        int ret;
        pid_t pid;

        int i;
        int maxfd;          //當前最大套接字
        int nEvent;
        fd_set set = {0};   //監聽集合
        fd_set oldset = {0};    //存放所有要監聽的文件描述符
        struct timeval time = {0};

        int reuse = 0;
        char acbuf[20] = ;
        char client_addr[100] = ;
        struct sockaddr_in addr = {0};  //自己的地址
        struct sockaddr_in clientAddr = {0};    //連上的客戶端的地址
        int addrLen = sizeof(struct sockaddr_in);

        signal(SIGCHLD,SIG_IGN);

        //1.socket()
        fd = socket(PF_INET,SOCK_STREAM,0);
        if(fd == -1)
        {
            perror(socket);
            return -1;
        }

        //會出現沒有活動的套接字仍然存在,會禁止綁定端口,出現錯誤:address already in use .
        //由TCP套接字TIME_WAIT引起,bind 返回 EADDRINUSE,該狀態會保留2-4分鐘
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
            {
            perror(setsockopet error\\\\n);
            return -1;
            }

        //2.bind()
        addr.sin_family = AF_INET;
        addr.sin_port = htons(1234);
        addr.sin_addr.s_addr = inet_addr(192.168.159.5);
        ret = bind(fd,(struct sockaddr *)&addr,addrLen);
        if(ret == -1)
        {
            perror(bind);
            return -1;
        }

        //3.listen()
        ret = listen(fd,10);
        if(ret == -1)
        {
            perror(listen);
            return -1;
        }

        //創建監聽集合
        FD_ZERO(&oldset);
        FD_SET(fd,&oldset);
        //maxfdp1:當前等待的最大套接字。比如:當前fd的值=3,則最大的套接字就是3
        //所以每當有客戶端連接進來,就比較一下文件描述符
        maxfd = fd;
        //select
        //select之前,set放的是所有要監聽的文件描述符;{3,4,5}
        //select之后,set只剩下有發生事件的文件描述符。{3}

        while(1)
        {
            set = oldset;
            printf(before accept.\\\\n);
            time.tv_sec = 5;
            nEvent = select(maxfd   1,&set,NULL,NULL,&time);    //返回文件描述符的個數(即事件的個數)
            printf(after accept.%d\\\\n,nEvent);
            if(nEvent == -1)
            {
                perror(select);
                return -1;
            }
            else if(nEvent == 0)    //超時
            {
                printf(time out);
                return 1;
            }
            else
            {           
                //有事件發生
                //判斷是否是客戶端產生的事件
                for(i = 0 ; i <= maxfd ; i  )
                {
                    if(FD_ISSET(i,&set))
                    {
                        if(i == fd)
                        {
                            clientfd = accept(fd,(struct sockaddr *)&clientAddr,&addrLen);
                            FD_SET(clientfd,&oldset);
                            printf(client ip:%s ,port:\\n,inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
                            if(clientfd > maxfd)
                            {
                                maxfd = clientfd;
                            }
                        }
                        else
                        {
                            memset(acbuf,0,20);
                            if(read(i,acbuf,20) == 0) //客戶端退出
                            {
                                close(i);
                                //還要從集合里刪除
                                FD_CLR(i,&oldset);
                            }
                            else
                                printf(receive: %s\\\\n,acbuf);
                        }
                    }
                }
            }
        }
        return 0;
    }

epoll

epoll用到的函數有以下幾個:

                #include <sys/epoll.h>
       int epoll_create(int size);//創建epoll
             int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);//操作函數
             int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);   

事件集合的結構體:

(這里 ,還要注意,epoll的超時參數是int,單位是us)

使用流程

        #include <sys/types.h>     
        #include <sys/socket.h>
        #include <netinet/in.h> //sockaddr_in
        #include <stdio.h>
        #include <string.h>
        #include <signal.h>
        #include <sys/epoll.h>

        //epoll
        //epoll_wait() epoll_creat() epoll_ctl()

        //TCP
        int main()
        {
            int fd;
            int clientfd;
            int ret;
            pid_t pid;

            int i;
            int epfd;
            int nEvent;
            struct epoll_event event = {0};
            struct epoll_event rtl_events[20] = {0};    //事件結果集

            int reuse = 0;
            char acbuf[20] = ;
            char client_addr[100] = ;
            struct sockaddr_in addr = {0};  //自己的地址
            struct sockaddr_in clientAddr = {0};    //連上的客戶端的地址
            int addrLen = sizeof(struct sockaddr_in);

            signal(SIGCHLD,SIG_IGN);

            //1.socket()
            fd = socket(PF_INET,SOCK_STREAM,0);
            if(fd == -1)
            {
                perror(socket);
                return -1;
            }

            //會出現沒有活動的套接字仍然存在,會禁止綁定端口,出現錯誤:address already in use .
            //由TCP套接字TIME_WAIT引起,bind 返回 EADDRINUSE,該狀態會保留2-4分鐘
            if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
                {
                perror(setsockopet error\\\\n);
                return -1;
                }

            //2.bind()
            addr.sin_family = AF_INET;
            addr.sin_port = htons(1234);
            addr.sin_addr.s_addr = inet_addr(192.168.159.5);
            ret = bind(fd,(struct sockaddr *)&addr,addrLen);
            if(ret == -1)
            {
                perror(bind);
                return -1;
            }

            //3.listen()
            ret = listen(fd,10);
            if(ret == -1)
            {
                perror(listen);
                return -1;
            }

            epfd = epoll_create(1000);  //同時監聽的文件描述符
            event.data.fd = fd;
            event.events = EPOLLIN;  //讀
            epoll_ctl(epfd,EPOLL_CTL_ADD,fd, &event);
            while(1)
            {
        //      nEvent = epoll_wait(epfd,rtl_events,20,-1);  //-1:阻塞    0:非阻塞
                nEvent = epoll_wait(epfd,rtl_events,20,5000);
                if(nEvent == -1)
                {
                    perror(epoll_wait);
                    return -1;
                }
                else if(nEvent == 0)
                {
                    printf(time out.);
                }
                else
                {
                    //有事件發生,立即處理
                    for(i = 0; i < nEvent;i  )
                    {
                        //如果是 服務器fd
                        if( rtl_events[i].data.fd == fd )
                        {
                            clientfd = accept(fd,(struct sockaddr *)&clientAddr,&addrLen);
                            //添加
                            event.data.fd = clientfd;
                            event.events = EPOLLIN;  //讀
                            epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&event);
                            printf(client ip:%s ,port:\\n,inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
                        }
                        else
                        {
                            //否則 客戶端fd 
                            memset(acbuf,0,20);
                            ret = read(rtl_events[i].data.fd,acbuf,20);
                            printf(%d\\\\n,ret);
                            if( ret == 0) //客戶端退出
                            {
                                close(rtl_events[i].data.fd);
                                //從集合里刪除
                                epoll_ctl(epfd,EPOLL_CTL_DEL,rtl_events[i].data.fd,NULL);
                            }
                            else
                                printf(receive: %s\\\\n,acbuf);
                        }

                    }
                }
            }

            return 0;
        }

運行結果如前,正常收發。

更多關于云服務器,域名注冊,虛擬主機的問題,請訪問三五互聯官網:m.shinetop.cn

贊(0)
聲明:本網站發布的內容(圖片、視頻和文字)以原創、轉載和分享網絡內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。郵箱:3140448839@qq.com。本站原創內容未經允許不得轉載,或轉載時需注明出處:三五互聯知識庫 » Socket-IO復用技術

登錄

找回密碼

注冊

主站蜘蛛池模板: 大地资源网中文第五页| 成人午夜激情在线观看| 男女扒开双腿猛进入爽爽免费看| 少妇被粗大的猛烈进出69影院一| 久久精品熟女亚洲av麻| 国产午夜福利精品视频| 免费无码一区无码东京热| 中文字幕日韩有码一区| 亚洲一区二区三区播放| 免费国产一级特黄aa大片在线| 九九热视频在线免费观看| 亚洲av一本二本三本| 亚洲精品无amm毛片| 老师破女学生处特级毛ooo片| 乱色精品无码一区二区国产盗 | 国产做无码视频在线观看| 丁香五月婷激情综合第九色| 国产美女被遭强高潮免费一视频| 久久亚洲精品情侣| 久久一日本综合色鬼综合色| 日韩av综合免费在线| 欧美亚洲综合成人a∨在线| 日韩AV高清在线看片| 国产一级小视频| 成人午夜大片免费看爽爽爽| WWW丫丫国产成人精品| 国产精品无码久久久久| 国产一区二区三区AV在线无码观看| 久久精品色一情一乱一伦| 国产成人高清在线重口视频| 无码人妻精品一区二区三区下载 | 超碰成人精品一区二区三| 奉化市| 国产无遮挡猛进猛出免费| 人妻内射视频麻豆| 天堂网亚洲综合在线| 永兴县| 国产黄色精品一区二区三区| 日韩人妻无码一区二区三区99| 日韩精品国产中文字幕| 亚洲天堂网中文在线资源|