欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c++網(wǎng)絡(luò)編程下Linux的epoll技術(shù)和Windows下的IOCP模型

 更新時(shí)間:2021年08月19日 17:02:19   作者:aircraft  
c++ 網(wǎng)絡(luò)編程LINUX-epoll/windows-IOCP下socket opoll函數(shù)用法 優(yōu)于select方法的epoll 以及windows下IOCP 解決多進(jìn)程服務(wù)端創(chuàng)建進(jìn)程資源浪費(fèi)問題,感興趣的小伙伴一起來學(xué)習(xí)吧

一、IOCP和Epoll之間的異同

1、異

1).IOCP是WINDOWS系統(tǒng)下使用。Epoll是Linux系統(tǒng)下使用。
2).IOCP是IO操作完畢之后,通過Get函數(shù)獲得一個(gè)完成的事件通知。
Epoll是當(dāng)你希望進(jìn)行一個(gè)IO操作時(shí),向Epoll查詢是否可讀或可寫,若處于可讀或可寫狀態(tài),Epoll會(huì)通過epoll_wait進(jìn)行通知。
3).IOCP封裝了異步的消息事件的通知機(jī)制,同時(shí)封裝了部分IO操作。但Epoll僅僅封裝了一個(gè)異步事件的通知機(jī)制,并不負(fù)責(zé)IO讀寫操作。Epoll保持了事件通知和IO操作間的獨(dú)立性,更加簡單靈活。
4).基于上面的描述,我們可以知道Epoll不負(fù)責(zé)IO操作,所以它只告訴你當(dāng)前可讀可寫了,并且將協(xié)議讀寫緩沖填充,由用戶去讀寫控制,此時(shí)我們可以做出額外的許多操作。IOCP則直接將IO通道里的讀寫操作都做完了才通知用戶,當(dāng)IO通道里發(fā)生了堵塞等狀況我們是無法控制的。

2、同

1).它們都是異步的事件驅(qū)動(dòng)的網(wǎng)絡(luò)模型。
2).它們都可以向底層進(jìn)行指針數(shù)據(jù)傳遞,當(dāng)返回事件時(shí),除可通知事件類型外,還可以通知事件相關(guān)數(shù)據(jù)。

二:Epoll理解與應(yīng)用。

1、epoll是什么?

epoll是當(dāng)前在Linux下開發(fā)大規(guī)模并發(fā)網(wǎng)絡(luò)程序的熱門人選,epoll 在Linux2.6內(nèi)核中正式引入,和select相似,都是I/O多路復(fù)用(IO multiplexing)技術(shù)。

Linux下設(shè)計(jì)并發(fā)網(wǎng)絡(luò)程序,常用的模型有:
Apache模型(Process Per Connection,簡稱PPC)
TPC(Thread PerConnection)模型
select模型和poll模型。
epoll模型

2、epoll與select對(duì)比優(yōu)化

基于select的I/O復(fù)用技術(shù)速度慢的原因:
1),調(diào)用select函數(shù)后常見的針對(duì)所有文件描述符的循環(huán)語句。它每次事件發(fā)生需要遍歷所有文件描述符,找出發(fā)生變化的文件描述符。(以前寫的示例沒加循環(huán))
2),每次調(diào)用select函數(shù)時(shí)都需要向該函數(shù)傳遞監(jiān)視對(duì)象信息。即每次調(diào)用select函數(shù)時(shí)向操作系統(tǒng)傳遞監(jiān)視對(duì)象信息,至于為什么要傳?是因?yàn)槲覀儽O(jiān)視的套接字變化的函數(shù),而套接字是操作系統(tǒng)管理的。(這個(gè)才是最耗效率的)

注釋:基于這樣的原因并不是說select就沒用了,在這樣的情況下就適合選用select:1,服務(wù)端接入者少 2,程序應(yīng)具有兼容性。

3、epoll是怎么優(yōu)化select問題的

1),每次發(fā)生事件它不需要循環(huán)遍歷所有文件描述符,它把發(fā)生變化的文件描述符單獨(dú)集中到了一起。
2),僅向操作系統(tǒng)傳遞1次監(jiān)視對(duì)象信息,監(jiān)視范圍或內(nèi)容發(fā)生變化時(shí)只通知發(fā)生變化的事項(xiàng)。

實(shí)現(xiàn)epoll時(shí)必要的函數(shù)和結(jié)構(gòu)體:

函數(shù):
epoll_create:創(chuàng)建保存epoll文件描述符的空間,該函數(shù)也會(huì)返回文件描述符,所以終止時(shí),也要調(diào)用close函數(shù)。(創(chuàng)建內(nèi)存空間)
epoll_ctl:向空間注冊(cè),添加或修改文件描述符。(注冊(cè)監(jiān)聽事件)
epoll_wait:與select函數(shù)類似,等待文件描述符發(fā)生變化。(監(jiān)聽事件回調(diào))

結(jié)構(gòu)體:

struct epoll_event 
{ 
__uint32_t events; 
epoll_data_t data; 
}

typedef union epoll_data 
{ 
void *ptr; 
int fd; 
__uinit32_t u32; 
__uint64_t u64; 
} epoll_data_t;

三、epoll的幾個(gè)函數(shù)的介紹:

1、epoll_create函數(shù)

/**  
 * @brief    該函數(shù)生成一個(gè)epoll專用的文件描述符。它其實(shí)是在內(nèi)核申請(qǐng)一空間,用來存放你想關(guān)注的socket fd上是否發(fā)生以及發(fā)生了什么事件。 
 *  
 * @param    size    size就是你在這個(gè)epoll fd上能關(guān)注的最大socket fd數(shù) 
 *  
 * @return   生成的文件描述符 
 */  
int epoll_create(int size);

2、epoll_ctl函數(shù)

/**  
 * @brief    該函數(shù)用于控制某個(gè)epoll文件描述符上的事件,可以注冊(cè)事件,修改事件,刪除事件。 
 *  
 * @param    epfd    由 epoll_create 生成的epoll專用的文件描述符 
 * @param    op      要進(jìn)行的操作例如注冊(cè)事件,可能的取值EPOLL_CTL_ADD 注冊(cè)、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 刪除 
 * @param    fd      關(guān)聯(lián)的文件描述符 
 * @param    event   指向epoll_event的指針 
 *  
 * @return   0       succ 
 *           -1      fail 
 */  

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);  

其中用到的數(shù)據(jù)結(jié)構(gòu)結(jié)構(gòu)如下
op值:

  • EPOLL_CTL_ADD:注冊(cè)新的fd到epfd中;
  • EPOLL_CTL_MOD:修改已經(jīng)注冊(cè)的fd的監(jiān)聽事件;
  • EPOLL_CTL_DEL:從epfd中刪除一個(gè)fd;
typedef union epoll_data { 
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t; 
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

常用的事件類型:

  • EPOLLIN :表示對(duì)應(yīng)的文件描述符可以讀;
  • EPOLLOUT:表示對(duì)應(yīng)的文件描述符可以寫;
  • EPOLLPRI:表示對(duì)應(yīng)的文件描述符有緊急的數(shù)據(jù)可讀
  • EPOLLERR:表示對(duì)應(yīng)的文件描述符發(fā)生錯(cuò)誤;
  • EPOLLHUP:表示對(duì)應(yīng)的文件描述符被掛斷;
  • EPOLLET: 表示對(duì)應(yīng)的文件描述符有事件發(fā)生;

例:

struct epoll_event ev;    
    //設(shè)置與要處理的事件相關(guān)的文件描述符    
    ev.data.fd=listenfd;    
    //設(shè)置要處理的事件類型    
    ev.events=EPOLLIN|EPOLLET;    
    //注冊(cè)epoll事件    
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); 

3、epoll_wait函數(shù)

/**  
 * @brief    該函數(shù)用于輪詢I/O事件的發(fā)生 
 *  
 * @param    epfd        由epoll_create 生成的epoll專用的文件描述符 
 * @param    events      用于回傳代處理事件的數(shù)組 
 * @param    maxevents   每次能處理的事件數(shù) 
 * @param    timeout     等待I/O事件發(fā)生的超時(shí)值;-1相當(dāng)于阻塞,0相當(dāng)于非阻塞。一般用-1即可 
 *  
 * @return   >=0         返回發(fā)生事件數(shù) 
 *           -1          錯(cuò)誤 
 */  


int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);  

用改良的epoll實(shí)現(xiàn)回聲服務(wù)端代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define BUF_SIZE 100
#define EPOLL_SIZE 50
void error_handling(char *buf);

int main(int argc, const char * argv[]) {
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t adr_sz;
    int str_len, i;
    char buf[BUF_SIZE];

    //類似select的fd_set變量查看監(jiān)視對(duì)象的狀態(tài)變化,epoll_event結(jié)構(gòu)體將發(fā)生變化的文件描述符單獨(dú)集中到一起
    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd, event_cnt;

    if(argc != 2)
    {
        printf("Usage: %s <port> \n", argv[0]);
        exit(1);
    }

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    if(serv_sock == -1)
        error_handling("socket() error");

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if(bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)
        error_handling("bind() error");

    if(listen(serv_sock, 5) == -1)
        error_handling("listen() error");

    //創(chuàng)建文件描述符的保存空間稱為“epoll例程”
    epfd = epoll_create(EPOLL_SIZE);
    ep_events = malloc(sizeof(struct epoll_event) *EPOLL_SIZE);

    //添加讀取事件的監(jiān)視(注冊(cè)事件)
    event.events = EPOLLIN;  //讀取數(shù)據(jù)事件
    event.data.fd = serv_sock;
    epoll_ctl(epdf, EPOLL_CTL_ADD, serv_sock, &event);

    while (1)
    {
        //響應(yīng)事件,返回發(fā)生事件的文件描述符數(shù)
        event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);  //傳-1時(shí),一直等待直到事件發(fā)生
        if(event_cnt == -1)
        {
            puts("epoll_wait() error");
            break;
        }

        //服務(wù)端套接字和客服端套接字
        for (i = 0; i < event_cnt; i++) {
            if(ep_events[i].data.fd == serv_sock)//服務(wù)端與客服端建立連接
            {
                adr_sz = sizeof(clnt_adr);
                clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
                event.events = EPOLLIN;
                event.data.fd = clnt_sock;
                epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
                printf("connected client: %d \n", clnt_sock);
            }
            else  //連接之后傳遞數(shù)據(jù)
            {
                str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);
                if(str_len == 0)
                {
                    //刪除事件
                    epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
                    close(ep_events[i].data.fd);
                    printf("closed client: %d \n", ep_events[i].data.fd);
                }
                else
                {
                    write(ep_events[i].data.fd, buf, str_len);
                }
            }
        }
    }

    close(serv_sock);
    close(epfd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

epoll客戶端代碼:

#define _GNU_SOURCE
#include "sysutil.h"
#include "buffer.h"
#include <sys/epoll.h>

int main(int argc, char const *argv[])
{
    //創(chuàng)建client套接字
    int sockfd = tcp_client(0);
    //調(diào)用非阻塞connect函數(shù)
    int ret = nonblocking_connect(sockfd, "localhost", 9981, 5000);
    if(ret == -1)
    {
        perror("Connect Timeout .");
        exit(EXIT_FAILURE);
    }

    //將三個(gè)fd設(shè)置為Non-Blocking
    activate_nonblock(sockfd);
    activate_nonblock(STDIN_FILENO);
    activate_nonblock(STDOUT_FILENO);

    buffer_t recvbuf; //sockfd -> Buffer -> stdout
    buffer_t sendbuf; //stdin -> Buffer -> sockfd

    //初始化緩沖區(qū)
    buffer_init(&recvbuf);
    buffer_init(&sendbuf);

    //創(chuàng)建epoll
    int epollfd = epoll_create1(0);
    if(epollfd == -1)
        ERR_EXIT("create epoll");
    struct epoll_event events[1024];

    uint32_t sockfd_event = 0;
    uint32_t stdin_event = 0;
    uint32_t stdout_event = 0;

    epoll_add_fd(epollfd, sockfd, sockfd_event);
    epoll_add_fd(epollfd, STDIN_FILENO, stdin_event);
    epoll_add_fd(epollfd, STDOUT_FILENO, stdout_event);

    while(1)
    {
        //重新裝填epoll事件
        sockfd_event = 0;
        stdin_event = 0;
        stdout_event = 0;
        //epoll無法每次都重新裝填,所以給每個(gè)fd添加一個(gè)空事件
        
        if(buffer_is_readable(&sendbuf))
        {
            sockfd_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&sendbuf))
        {
            stdin_event |= kReadEvent;
        }
        if(buffer_is_readable(&recvbuf))
        {
            stdout_event |= kWriteEvent;
        }
        if(buffer_is_writeable(&recvbuf))
        {
            sockfd_event |= kReadEvent;
        }
        epoll_mod_fd(epollfd, sockfd, sockfd_event);
        epoll_mod_fd(epollfd, STDIN_FILENO, stdin_event);
        epoll_mod_fd(epollfd, STDOUT_FILENO, stdout_event);
       
       //監(jiān)聽fd數(shù)組
        int nready = epoll_wait(epollfd, events, 1024, 5000);
        if(nready == -1)
            ERR_EXIT("epoll wait");
        else if(nready == 0)
        {
            printf("epoll timeout.\n");
            continue;
        }
        else
        {
            int i;
            for(i = 0; i < nready; ++i)
            {
                int peerfd = events[i].data.fd;
                int revents = events[i].events;
                if(peerfd == sockfd && revents & kReadREvent)
                {
                    //從sockfd接收數(shù)據(jù)到recvbuf
                    if(buffer_read(&recvbuf, peerfd) == 0)
                    {
                        fprintf(stderr, "server close.\n");
                        exit(EXIT_SUCCESS);
                    } 
                }                 
                if(peerfd == sockfd && revents & kWriteREvent)
                {
                    buffer_write(&sendbuf, peerfd); //將sendbuf中的數(shù)據(jù)寫入sockfd
                }

                if(peerfd == STDIN_FILENO && revents & kReadREvent)
                {
                    //從stdin接收數(shù)據(jù)寫入sendbuf
                    if(buffer_read(&sendbuf, peerfd) == 0)
                    {
                        fprintf(stderr, "exit.\n");
                        exit(EXIT_SUCCESS);
                    } 
                }
                if(peerfd == STDOUT_FILENO && revents & kWriteREvent)
                {
                    buffer_write(&recvbuf, peerfd); //將recvbuf中的數(shù)據(jù)輸出至stdout
                }
            }
        }
    }
}

 4、條件觸發(fā)和邊緣觸發(fā)

什么是條件觸發(fā)和邊緣觸發(fā)?它們是指事件響應(yīng)的方式,epoll默認(rèn)是條件觸發(fā)的方式。條件觸發(fā)是指:只要輸入緩沖中有數(shù)據(jù)就會(huì)一直通知該事件,循環(huán)響應(yīng)epoll_wait。而邊緣觸發(fā)是指:輸入緩沖收到數(shù)據(jù)時(shí)僅注冊(cè)1次該事件,即使輸入緩沖中還留有數(shù)據(jù),也不會(huì)再進(jìn)行注冊(cè),只響應(yīng)一次。

邊緣觸發(fā)相對(duì)條件觸發(fā)的優(yōu)點(diǎn):可以分離接收數(shù)據(jù)和處理數(shù)據(jù)的時(shí)間點(diǎn),從實(shí)現(xiàn)模型的角度看,邊緣觸發(fā)更有可能帶來高性能。

將上面epoll實(shí)例改為邊緣觸發(fā):
1).首先改寫 event.events = EPOLLIN | EPOLLET; (EPOLLIN:讀取數(shù)據(jù)事件 EPOLLET:邊緣觸發(fā)方式)
2).邊緣觸發(fā)只響應(yīng)一次接收數(shù)據(jù)事件,所以要一次性全部讀取輸入緩沖中的數(shù)據(jù),那么就需要判斷什么時(shí)候數(shù)據(jù)讀取完了?Linux聲明了一個(gè)全局的變量:int errno; (error.h中),它能記錄發(fā)生錯(cuò)誤時(shí)提供額外的信息。這里就可以用它來判斷是否讀取完數(shù)據(jù)

str_len = read(...);
if(str_len < 0)
{
    if(errno == EAGAIN) //讀取輸入緩沖中的全部數(shù)據(jù)的標(biāo)志
        break;
}

3).邊緣觸發(fā)方式下,以阻塞方式工作的read&write有可能會(huì)引起服務(wù)端的長時(shí)間停頓。所以邊緣觸發(fā)一定要采用非阻塞的套接字?jǐn)?shù)據(jù)傳輸形式。那么怎么將套接字的read,write數(shù)據(jù)傳輸形式修改為非阻塞模式呢?

//fd套接字文件描述符,將此套接字?jǐn)?shù)據(jù)傳輸模式修改為非阻塞
void setnonblockingmode(int fd)
{
    int flag = fcntl(fd, F_GETFL,0); //得到套接字原來屬性
    fcntl(fd, F_SETFL, flag | O_NONBLOCK);//在原有屬性基礎(chǔ)上設(shè)置添加非阻塞模式
}

四、IOCP理解與應(yīng)用

1、傳統(tǒng)服務(wù)器的網(wǎng)絡(luò)IO流程

接到一個(gè)客戶端連接->創(chuàng)建一個(gè)線程負(fù)責(zé)這個(gè)連接的IO操作->持續(xù)對(duì)新線程進(jìn)行數(shù)據(jù)處理->全部數(shù)據(jù)處理完畢->終止線程。
設(shè)計(jì)代價(jià)可分為四點(diǎn):
1).每個(gè)連接創(chuàng)建一個(gè)線程,將導(dǎo)致過多的線程。
2).維護(hù)線程所消耗的堆棧內(nèi)存過大。
3).操作系統(tǒng)創(chuàng)建和銷毀線程過大。
4).線程之間切換的上下文代價(jià)過大。
這種傳統(tǒng)的服務(wù)器網(wǎng)絡(luò)結(jié)構(gòu)稱之為會(huì)話模型,為防止大量線程的維護(hù),我們可以創(chuàng)建I/O模型。

創(chuàng)建I/O模型要求:
1).允許一個(gè)線程在不同時(shí)刻給多個(gè)客戶端進(jìn)行服務(wù)。
2).允許一個(gè)客戶端在不同時(shí)間被多個(gè)線程服務(wù)。
缺點(diǎn)是會(huì)使線程大幅度減少

根據(jù)上述則要求一下兩點(diǎn):
1).客戶端狀態(tài)的分離,之前會(huì)話模式我們可以通過線程狀態(tài)得知客戶端狀態(tài),但現(xiàn)在客戶端狀態(tài)要通過其他方式獲取。
2).I/O請(qǐng)求的分離。一個(gè)線程不再服務(wù)于一個(gè)客戶端會(huì)話,則要求客戶端對(duì)這個(gè)線程提交I/O處理請(qǐng)求。

根據(jù)上要求會(huì)產(chǎn)生以下模式:
1).會(huì)話狀態(tài)管理模塊。它負(fù)責(zé)接收到一個(gè)客戶端連接,就創(chuàng)建一個(gè)會(huì)話狀態(tài)。
2).當(dāng)會(huì)話狀態(tài)發(fā)生改變,例如斷掉連接,接收到網(wǎng)絡(luò)消息,就發(fā)送一個(gè)I/O請(qǐng)求給 I/O工作模塊進(jìn)行處理。
3).I/O工作模塊接收到一個(gè)I/O請(qǐng)求后,從線程池里喚醒一個(gè)工作線程,讓該工作線程處理這個(gè)I/O請(qǐng)求,處理完畢后,該工作線程繼續(xù)掛起。
則將網(wǎng)絡(luò)連接 和I/O工作線程分離為三個(gè)部分,相互通訊僅依靠 I/O請(qǐng)求。

根據(jù)上模式給出一下介意:
1).在進(jìn)行I/O請(qǐng)求處理的工作線程是被喚醒的工作線程,一個(gè)CPU對(duì)應(yīng)一個(gè)的話,可以最大化利用CPU。所以 活躍線程的個(gè)數(shù) 建議等于 硬件CPU個(gè)數(shù)。
2).工作線程我們開始創(chuàng)建了線程池,免除創(chuàng)建和銷毀線程的代價(jià)。因?yàn)榫€程是對(duì)I/O進(jìn)行操作的,且一一對(duì)應(yīng),那么當(dāng)I/O全部并行時(shí),工作線程必須滿足I/O并行操作需求,所以 線程池內(nèi)最大工作線程個(gè)數(shù) 建議大于或者等于 I/O并行個(gè)數(shù)。
3).但是我們可知CPU個(gè)數(shù)又限制了活躍的線程個(gè)數(shù),那么線程池過大意義很低,所以按常規(guī)建議 線程池大小 等于 CPU個(gè)數(shù)*2 左右為佳。例如,8核服務(wù)器建議創(chuàng)建16個(gè)工作線程的線程池。 上面描述的依然是I/O模型并非IOCP,那么IOCP是什么呢,全稱 IO完成端口。
它是一種WIN32的網(wǎng)絡(luò)I/O模型,既包括了網(wǎng)絡(luò)連接部分,也負(fù)責(zé)了部分的I/O操作功能,用于方便我們控制有并發(fā)性的網(wǎng)絡(luò)I/O操作。

WIN32網(wǎng)絡(luò)I/O模型有如下特點(diǎn):
1).它是一個(gè)WIN32內(nèi)核對(duì)象,所以無法運(yùn)行于Linux.
2).它自己負(fù)責(zé)維護(hù)了工作線程池,同時(shí)也負(fù)責(zé)了I/O通道的內(nèi)存池。
3).它自己實(shí)現(xiàn)了線程的管理以及I/O請(qǐng)求通知,最小化的做到了線程的上下文切換。
4).它自己實(shí)現(xiàn)了線程的優(yōu)化調(diào)度,提高了CPU和內(nèi)存緩沖的使用率。

2、使用IOCP的基本步驟

1).創(chuàng)建IOCP對(duì)象,由它負(fù)責(zé)管理多個(gè)Socket和I/O請(qǐng)求。CreateIoCompletionPort需要將IOCP對(duì)象和IOCP句柄綁定。
2).創(chuàng)建一個(gè)工作線程池,以便Socket發(fā)送I/O請(qǐng)求給IOCP對(duì)象后,由這些工作線程進(jìn)行I/O操作。注意,創(chuàng)建這些線程的時(shí)候,將這些線程綁定到IOCP上。
3).創(chuàng)建一個(gè)監(jiān)聽的socket。
4).輪詢,當(dāng)接收到了新的連接后,將socket和完成端口進(jìn)行關(guān)聯(lián)并且投遞給IOCP一個(gè)I/O請(qǐng)求。注意:將Socket和IOCP進(jìn)行關(guān)聯(lián)的函數(shù)和創(chuàng)建IOCP的函數(shù)一樣,都是CreateIoCompletionPort,不過注意傳參必然是不同的。
5).因?yàn)槭钱惒降?,我們可以去做其他,等待IOCP將I/O操作完成會(huì)回饋我們一個(gè)消息,我們?cè)龠M(jìn)行處理。
其中需要知道的是:I/O請(qǐng)求被放在一個(gè)I/O請(qǐng)求隊(duì)列里面,對(duì),是隊(duì)列,LIFO機(jī)制。當(dāng)一個(gè)設(shè)備處理完I/O請(qǐng)求后,將會(huì)將這個(gè)完成后的I/O請(qǐng)求丟回IOCP的I/O完成隊(duì)列。
我們應(yīng)用程序則需要在GetQueuedCompletionStatus去詢問IOCP,該I/O請(qǐng)求是否完成。
其中有一些特殊的事情要說明一下,我們有時(shí)有需要人工的去投遞一些I/O請(qǐng)求,則需要使用PostQueuedCompletionStatus函數(shù)向IOCP投遞一個(gè)I/O請(qǐng)求到它的請(qǐng)求隊(duì)列中。

以上就是c++網(wǎng)絡(luò)編程Linux下的epoll技術(shù)和Windows下的IOCP模型的詳細(xì)內(nèi)容,更多關(guān)于c++網(wǎng)絡(luò)編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!,希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++表達(dá)式new與delete知識(shí)詳解

    C++表達(dá)式new與delete知識(shí)詳解

    這篇文章主要為大家詳細(xì)介紹了C++表達(dá)式new與delete知識(shí)點(diǎn),學(xué)習(xí)如何動(dòng)態(tài)創(chuàng)建對(duì)象,動(dòng)態(tài)創(chuàng)建的對(duì)象與一般對(duì)象的區(qū)別,動(dòng)態(tài)創(chuàng)建的對(duì)象的初始化以及釋放動(dòng)態(tài)分配的內(nèi)存等知識(shí)點(diǎn),感興趣的朋友可以參考一下
    2016-05-05
  • C++中友元的詳解及其作用介紹

    C++中友元的詳解及其作用介紹

    這篇文章主要介紹了C++中友元的詳解及其作用介紹,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • 利用簡潔的C語言代碼解決跳臺(tái)階問題與約瑟夫環(huán)問題

    利用簡潔的C語言代碼解決跳臺(tái)階問題與約瑟夫環(huán)問題

    這篇文章主要介紹了利用簡潔的C語言代碼解決跳臺(tái)階問題與約瑟夫環(huán)問題的方法,跳臺(tái)階問題與約瑟夫環(huán)問題是常見的基礎(chǔ)算法題目,需要的朋友可以參考下
    2016-02-02
  • C++實(shí)現(xiàn)圖像壓縮的示例代碼

    C++實(shí)現(xiàn)圖像壓縮的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何使用C++實(shí)現(xiàn)圖像壓縮的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • C語言實(shí)現(xiàn)代碼雨效果

    C語言實(shí)現(xiàn)代碼雨效果

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)代碼雨效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++實(shí)現(xiàn)list增刪查改模擬的示例代碼

    C++實(shí)現(xiàn)list增刪查改模擬的示例代碼

    本文主要介紹了C++實(shí)現(xiàn)list增刪查改模擬,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • C++中函數(shù)重載詳解

    C++中函數(shù)重載詳解

    大家好,本篇文章主要講的是C++中函數(shù)重載詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • C++的多態(tài)和虛函數(shù)你真的了解嗎

    C++的多態(tài)和虛函數(shù)你真的了解嗎

    這篇文章主要為大家詳細(xì)介紹了C++的多態(tài)和虛函數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • C語言實(shí)現(xiàn)xml構(gòu)造解析器

    C語言實(shí)現(xiàn)xml構(gòu)造解析器

    本文給大家分享的是使用C語言來實(shí)現(xiàn)xml構(gòu)造解析器的方法和代碼,簡單易用,推薦給大家
    2016-07-07
  • C語言中scanf與scnaf_s函數(shù)詳解

    C語言中scanf與scnaf_s函數(shù)詳解

    大家好,本篇文章主要講的是C語言中scanf與scnaf_s函數(shù)詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01

最新評(píng)論