主頁 > 後端開發 > 網路編程-I/O復用

網路編程-I/O復用

2021-01-28 06:33:56 後端開發

I/O模型

Unix下可用的I/O模型有五種:

  • 阻塞式I/O
  • 非阻塞式I/O
  • I/O復用(select和poll、epoll)
  • 信號驅動式I/O(SIGIO)
  • 異步I/O(POSIX的aio_系列函式)

詳見Unix網路編程卷一第六章

select()和poll()在Unix系統中存在時間長,主要優勢在于可移植性,主要缺點在于當同時檢查大量的檔案描述符時性能拓展性不佳,

epoll API的關鍵優勢在于能讓應用高效地檢查大量的檔案描述符,主要缺點是專屬于Linux系統的API,

I/O復用-select

select()首次出現在BSD系統的套接字API中,

select()系統呼叫的用途:在一段指定的時間內,監聽用戶感興趣的檔案描述符上的可讀、可寫和例外事件,

系統呼叫select()會一直阻塞,直到一個或多個檔案描述符集合成為就緒態,

#include <sys/select.h>
#include <sys/time.h>

//若有就緒描述符則回傳其數目,若超時則回傳0,若出錯則回傳-1
int select(int maxfdp1, fd_set *readset, fd_set *writeset, 
           fd_set *exceptset, const struct timeval *timeout);

探究下fd_set的結構

/*typesizes.h*/
#define __FD_SETSIZE		1024

/*select.h*/
typedef long int __fd_mask;

//long int型別共有多少bits
#define __NFDBITS	(8 * (int) sizeof (__fd_mask))

typedef struct
{
    //long int型陣列,陣列大小 = 描述符最大數 / long int的位數
    //陣列大小為 __FD_SETSIZE bits
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
} fd_set;

select()程式示例:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdarg.h>

static void usageError(const char* progName){
    fprintf(stderr, "Usage: %s {timeout | -} fd-num[rw]...\n", progName);
    fprintf(stderr, "    - means infinite timeout; \n");
    fprintf(stderr, "    r = monitor for read\n");
    fprintf(stderr, "    w = monitor for wirite\n\n");
    fprintf(stderr, "    e.g.: %s - 0rw 1w\n", progName);
    exit(1);
}
void cmdLineErr(const char *format, ...)
{
    va_list argList;

    fflush(stdout);           /* Flush any pending stdout */

    fprintf(stderr, "Command-line usage error: ");
    va_start(argList, format);
    vfprintf(stderr, format, argList);
    va_end(argList);

    fflush(stderr);           /* In case stderr is not line-buffered */
    exit(EXIT_FAILURE);
}

int main(int argc, char* argv[]){
    fd_set readfds, writefds;
    int ready, nfds, fd, numRead, j;
    struct timeval timeout;
    struct timeval *pto;
    char buf[10];

    if(argc < 2 || strcmp(argv[1], "--help") == 0){
        usageError(argv[0]);
    }

    if(strcmp(argv[1], "-") == 0){
        pto = NULL;
    }
    else{
        pto = &timeout;
        timeout.tv_sec = strtol(argv[1], NULL, 0);
        timeout.tv_usec = 0;
    }

    nfds = 0;
    FD_ZERO(&readfds);
    FD_ZERO(&writefds);

    for(j = 2; j < argc; j++){
        numRead = sscanf(argv[j], "%d%2[rw]", &fd, buf);
        if(numRead != 2){
            usageError(argv[0]);
        }
        if(fd >= FD_SETSIZE){
            cmdLineErr("file descriptor exceeds limit (%d)\n", FD_SETSIZE);
        }
        if(fd >= nfds){
            nfds = fd + 1;
        }
        if(strchr(buf, 'r') != NULL){
            FD_SET(fd, &readfds);
        }
        if(strchr(buf, 'w') != NULL){
            FD_SET(fd, &writefds);
        }
    }
    ready = select(nfds, &readfds, &writefds, NULL, pto);
    if(ready == -1){
        printf("errExit(select)");
        exit(1);
    }
    printf("ready = %d\n", ready);
    for(fd = 0; fd < nfds; fd++){
        printf("%d: %s%s\n",fd, FD_ISSET(fd, &readfds) ? "r" : "", 
        FD_ISSET(fd, &writefds) ? "w" : "");
    }
    if(pto != NULL){
        printf("timeout after select(): %ld.%03ld\n",
               (long) timeout.tv_sec, (long) timeout.tv_usec / 1000);
    }
    exit(0);
}

select處理正常資料和帶外資料:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>

int main(int argc, char* argv[]){
    if(argc <= 2){
        printf("usage: %s ip_adress port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret = 0;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenfd >= 0);
    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);
    ret = listen(listenfd, 5);
    assert(ret != -1);

    struct sockaddr_in client_address;
    socklen_t client_addrlength = sizeof(client_address);
    int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
    if(connfd < 0){
        printf("error is: %d\n", errno);
        close(listenfd);
    }

    char buf[1024];
    fd_set read_fds;
    fd_set exception_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&exception_fds);

    while(true){
        memset(buf, '\0', sizeof(buf));
        FD_SET(connfd, &read_fds);
        FD_SET(connfd, &exception_fds);
        ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);
        if(ret < 0){
            printf("selection failure\n");
            break;
        }
        if(FD_ISSET(connfd, &read_fds)){
            ret = recv(connfd, buf, sizeof(buf)-1, 0);
            if(ret <= 0){
                break;
            }
            printf("get %d bytes of normal data: %s\n", ret, buf);
        }
        else if(FD_ISSET(connfd, &exception_fds)){
            ret = recv(connfd, buf, sizeof(buf)-1, MSG_OOB);
            if(ret <= 0){
                break;
            }
            printf("get %d bytes of oob data: %s\n", ret, buf);
        }
    }
    close(connfd);
    close(listenfd);
    return 0;
}

I/O復用-poll

poll函式起源于SVR3,最初局限于流設備,SVR4取消了這種限制,允許poll作業在任何描述符上,

poll提供的功能與select類似,不過在處理流設備時,它能夠提供額外的資訊,

#include <poll.h>

struct pollfd{
    int		fd;
    short	events;	//指定要測驗的條件
    short	revents;//回傳描述符的狀態
}
//若有就緒描述符回傳其數目,超時回傳0,出錯回傳-1
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

select()同poll()回傳正整數的區別:如果一個檔案描述符在回傳的集合中出現了不止一次,系統呼叫select()會將同一個檔案描述符計數多次,而系統呼叫poll()回傳的是就緒態檔案描述符個數,且一個檔案描述符只會統計一次,就算在相應的revents欄位中設定了多個位掩碼也是如此,

poll示例程式:

#include <time.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdarg.h>

static void usageError(const char* progName){
    fprintf(stderr, "Usage: %s {timeout | -} fd-num[rw]...\n", progName);
    fprintf(stderr, "    - means infinite timeout; \n");
    fprintf(stderr, "    r = monitor for read\n");
    fprintf(stderr, "    w = monitor for wirite\n\n");
    fprintf(stderr, "    e.g.: %s - 0rw 1w\n", progName);
    exit(1);
}

int main(int argc, char* argv[]){
    int numPipes, j, ready, randPipe, numWrites;
    int (*pfds)[2];//指向陣列的指標
    struct pollfd *pollFd;

    if(argc < 2 || strcmp(argv[1], "--help") == 0){
        printf("%s num-pipes [num-writes]\n", argv[0]);
        exit(1);
    }

    numPipes = strtol(argv[1], NULL, 10);

    pfds = (int (*)[2])calloc(numPipes, sizeof(int [2]));
    if(pfds == NULL){
        printf("error malloc");
        exit(1);
    }
    pollFd = (pollfd*)calloc(numPipes, sizeof(struct pollfd));
    if(pollFd == NULL){
        printf("error malloc");
        exit(1);
    }

    for(j = 0; j < numPipes; j++){
        if(pipe(pfds[j]) == -1){
            printf("error pipe %d", j);
            exit(1);
        }
    }

    numWrites = (argc > 2) ? strtol(argv[2], NULL, 10) : 1;
    srandom((int)time(NULL));
    for(j = 0; j < numWrites; j++){
        randPipe = random() % numPipes;
        printf("Writing to fd: %3d (read fd: %3d)\n",
                pfds[randPipe][1], pfds[randPipe][0]);
        if (write(pfds[randPipe][1], "a", 1) == -1){
            printf("write %d", pfds[randPipe][1]);
            exit(1);
        }
    }

    for(j = 0; j < numPipes; j++){
        pollFd[j].fd = pfds[j][0];
        pollFd[j].events = POLLIN;
    }

    ready = poll(pollFd, numPipes, -1);
    if(ready == -1){
        printf("poll error");
        exit(1);
    }

    printf("poll() returned: %d\n", ready);

    for(j = 0; j < numPipes; j++){
        if(pollFd[j].revents & POLLIN){
            printf("Readable: %d %3d\n", j, pollFd[j].fd);
        }
    }
    return 0;
}

I/O復用-epoll

epoll API由三組系統呼叫組成;

  • epoll_create()創建一個epoll實體
  • epoll_ctl()操作同epoll實體相關聯的興趣串列
  • epoll_wait()回傳與epoll相關聯的就緒串列中的成員

epoll實體:epoll API的核心資料結構,和一個打開的檔案描述符相關聯,這個檔案描述符不用來做IO操作,相反它是內核資料結構的句柄,這些內核資料結構實作了兩個目的:

  • 記錄興趣串列
  • 維護就緒串列

epoll_create

#include <sys/epoll.h>
int epoll_create(int size);

引數size指定我們想要通過epoll實體來檢查的描述符個數,不是上限,只是告知內核應該如何為內部資料結構劃分初始大小,

函式回傳epoll實體的檔案描述符,該檔案描述符不需要時需要close(),

當所有與epoll實體相關的檔案描述符都被關閉時,實體被銷毀,相關資源釋放,(多個檔案描述符可能參考到相同的epoll實體,這是由于呼叫了fork()或dup()這樣的類似函式所致),

linux2.6.8版以來,size引數被忽略不用,

linux2.6.27以來,Linux支持一個新的系統呼叫epoll_create1():

  • 去掉了無用的引數size
  • 增加了一個可用來修改系統呼叫行為的flags引數
    • flag目前只支持一個標志:EPOLL_CLOEXEC,使內核在新的檔案描述符上啟動了執行即關閉(close-on-exec)標志(FD_CLOEXEC)

epoll_ctl

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll *ev);

成功回傳0,失敗回傳-1并設定errno,

引數fd:指明修改興趣串列中哪一個檔案描述符的設定

引數op:指定需要執行的操作

  • EPOLL_CTL_ADD:添加
  • EPOLL_CTL_MOD:修改
  • EPOLL_CTL_DEL:洗掉

引數ev:

struct epoll_event{
    uint32_t	 events;//epoll事件,位掩碼
    epoll_data_t data;	//用戶資料 
}
typedef union epoll_data{
    void		*ptr;
    int			fd;
    uint32_t	u32;
    uint64_t	u64;
}epoll_data_t;
  • 結構體epoll_event在的events欄位是一個位掩碼,指定待檢查的描述符fd上感興趣的事件集合
  • data欄位是一個聯合體,當描述符fd成為就緒態時,聯合體的成員可用來指定傳回給呼叫行程的資訊
    • 聯合體成員不能一起使用,常用fd
    • 想要將檔案描述符和用戶資料關聯起來,以實作快速的資料訪問,只能使用其它手段,比如放棄使用fd,而在ptr指向的用戶資料中包含fd

max_user_watches上限

每個注冊到epoll實體上的檔案描述符需要占用一小段不能被交換的內核記憶體空間,因此內核提供了一個介面用來定義每個用戶可以注冊到epoll實體上的檔案描述符總數,

這個上限值可以通過max_user_watches來查看和修改,max_user_watches是專屬于Linux系統的/proc/sys/fd/epoll目錄下的一個檔案,默認上限值根據可用系統記憶體計算得出,

epoll_wait

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);

成功回傳就緒態的檔案描述符的個數,失敗回傳-1并設定errno

引數evlist指向的結構體陣列中回傳的是有關就緒態檔案描述符的資訊,陣列evlist的空間由呼叫者負責申請,所包含的元素個數在引數maxevents中指定,

在陣列evlist中每個元素回傳的都是單個就緒態檔案描述符的資訊:

  • events欄位回傳在該描述符上已經發生的事件掩碼
  • data欄位回傳的是適用epoll_ctl()注冊監聽事件時在ev.data中所指定的值,data欄位是唯一可獲知同這個事件相關的檔案描述符號的途徑,因此,在呼叫epoll_ctl()時要么將ev.data.fd設為檔案描述符號,要么將ev.data.ptr設為指向包含檔案描述符號的結構體

引數timeout用來確定epoll_wait()的阻塞行為:

  • timeout為-1,呼叫將一直阻塞,直到興趣串列中的檔案描述符上有事件發生,或者直到捕獲到一個信號為止
  • timeout為0,執行一次非阻塞式的檢查
  • timeout大于0,呼叫將阻塞至多timeout毫秒,直到檔案描述符上有事件發生,或者直到捕獲到一個信號為止

在多執行緒程式中,可以在一個執行緒中使用epoll_ctl()將檔案描述符添加到另一個執行緒中由epoll_wait()所監視的epoll實體的興趣串列中去,這些對興趣串列的修改將立刻得到處理,而epoll_wait()呼叫將回傳有關新添加的檔案描述符的就緒資訊,

epoll事件:除了有一個額外的前綴E外,大多數位掩碼的名稱同poll中對應的事件掩碼名稱相同,例外情況:

  • EPOLLET:epoll支持邊緣觸發
  • EPOLLONESHOT:只觸發一次,觸發完標記為非激活狀態,需要使用EPOLL_CTL_MOD操作重新激活對這個檔案描述符的檢查

epoll程式示例:

#include <sys/epoll.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>


#define MAX_BUF     1000
#define MAX_EVENTS  5

int main(int argc, char* argv[]){
    int epfd, ready, fd, s, j, numOpenFds;
    struct epoll_event ev;
    struct epoll_event evlist[MAX_EVENTS];
    char buf[MAX_BUF];

    if(argc < 2 || strcmp(argv[1], "--help")==0){
        printf("usage: %s file...\n", argv[0]);
        exit(1);
    }

    epfd = epoll_create(argc - 1);
    if(epfd == -1){
        printf("error epoll_create");
        exit(1);
    }

    for(j = 1; j < argc; j++){
        fd = open(argv[j], O_RDONLY);
        if(fd == -1){
            printf("error open");
            exit(1);
        }
        printf("Opened \"%s\" on fd %d\n", argv[j], fd);

        ev.events = EPOLLIN;
        ev.data.fd = fd;
        if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1){
            printf("error epoll_ctl");
            exit(1);
        }
    }

    numOpenFds = argc - 1;

    while(numOpenFds > 0){
        printf("About to epoll_wait()\n");
        ready = epoll_wait(epfd, evlist, MAX_EVENTS, -1);
        if(ready == -1){
            if(errno == EINTR)continue;
            else{
                printf("error epoll_wait");
                exit(1);
            }
        }
        printf("Ready: %d\n", ready);

        for(j = 0; j < ready; j++){
            printf("  fd = %d; events: %s%s%s\n", evlist[j].data.fd,
                (evlist[j].events & EPOLLIN)  ? "EPOLLIN ":"",
                (evlist[j].events & EPOLLHUP) ? "EPOLLHUP":"",
                (evlist[j].events & EPOLLERR) ? "EPOLLERR":"");
            if(evlist[j].events & EPOLLIN){
                s = read(evlist[j].data.fd, buf, MAX_BUF);
                if(s == -1){
                    printf("error read");
                }
                printf("    read %d bytes : %.*s",s,s,buf);
            }
            else if(evlist[j].events & (EPOLLHUP | EPOLLERR)){
                printf("    closing fd %d\n", evlist[j].data.fd);
                if(close(evlist[j].data.fd) == -1){
                    printf("error close");
                    exit(1);
                }
                numOpenFds--;
            }
        }
    }
    printf("All file descriptors closed; bye\n");
    exit(0);
}

ET模式比LT模式觸發事件的次數更少:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

int setnonblocking(int fd){
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

void addfd(int epollfd, int fd, bool enable_et){
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN;
    if(enable_et){
        event.events |= EPOLLET;
    }
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

void lt(epoll_event *events, int number, int epollfd, int listenfd){
    char buf[BUFFER_SIZE];
    for(int i = 0; i < number; i++){
        int sockfd = events[i].data.fd;
        if(sockfd == listenfd){
            struct sockaddr_in client_address;
            socklen_t client_addrlength = sizeof(client_address);
            int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
            addfd(epollfd, connfd, false);
        }
        else if(events[i].events & EPOLLIN){
            printf("event trigger once\n");
            memset(buf, '\0', BUFFER_SIZE);
            int ret = recv(sockfd, buf, BUFFER_SIZE-1,0);
            if(ret <= 0){
                close(sockfd);
                continue;
            }
            printf("get %d bytes of content: %s\n", ret, buf);
        }
        else{
            printf("something else happened \n");
        }
    }
}

void et(epoll_event* events, int number, int epollfd, int listenfd){
    char buf[BUFFER_SIZE];
    for(int i = 0; i < number; i++){
        int sockfd = events[i].data.fd;
        if(sockfd == listenfd){
            struct sockaddr_in client_address;
            socklen_t client_addrlength = sizeof(client_address);
            int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
            addfd(epollfd, connfd, true);
        }
        else if(events[i].events & EPOLLIN){
            printf("event trigger once\n");
            while(true){
                memset(buf, '\0',BUFFER_SIZE);
                int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);
                if(ret < 0){
                    if((errno == EAGAIN) || (errno == EWOULDBLOCK)){
                        printf("read later\n");
                        break;
                    }
                    close(sockfd);
                    break;
                }
                else if(ret == 0){
                    close(sockfd);
                }
                else{
                    printf("get %d bytes of content: %s\n",ret, buf);
                }
            }
        }
        else{
            printf("something else happend \n");
        }
    }
}

int main(int argc, char* argv[]){
    if(argc <= 2){
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char *ip = argv[1];
    int port  = atoi(argv[2]);

    int ret = 0;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenfd >= 0);

    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(listenfd, 5);
    assert(ret != -1);

    epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    assert(epollfd != -1);
    addfd(epollfd, listenfd, true);

    while(true){
        int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        if(ret < 0){
            printf("epoll failure\n");
            break;
        }
        lt(events, ret, epollfd, listenfd);
        //et(events, ret, epollfd, listenfd);
    }
    close(listenfd);
    return 0;
}
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>

#define MAX_EVENT_NUMBER    1024
#define BUFFER_SIZE         1024
struct fds{
    int epollfd;
    int sockfd;
};

int setnonblocking(int fd){
    int old_option = fcntl(fd, F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_option);
    return old_option;
}

void addfd(int epollfd, int fd, bool oneshot){
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    if(oneshot){
        event.events |= EPOLLONESHOT;
    }
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

void reset_oneshot(int epollfd, int fd){
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
    epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

void *worker(void *arg){
    int sockfd = ((fds*)arg)->sockfd;
    int epollfd = ((fds*)arg)->epollfd;
    printf("start new thread to receive data on fd: %d\n", sockfd);
    char buf[BUFFER_SIZE];
    memset(buf, '\0', BUFFER_SIZE);
    while(1){
        int ret = recv(sockfd, buf, BUFFER_SIZE-1, 0);
        if(ret == 0){
            close(sockfd);
            printf("foreiner closed the connection\n");
            break;
        }
        else if(ret < 0){
            if(errno == EAGAIN){
                reset_oneshot(epollfd, sockfd);
                printf("read later\n");
                break;
            }
        }
        else{
            printf("get content: %s\n", buf);
            sleep(5);
        }
    }
    printf("end thread receving data on fd : %d\n", sockfd);
}

int main(int argc, char* argv[]){
    if(argc < 2){
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret = 0;
    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenfd >= 0);

    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
    assert(ret != -1);

    ret = listen(listenfd, 5);
    assert(ret != -1);

    epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    assert(epollfd != -1);

    addfd(epollfd, listenfd, false);

    while(1){
        int ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        if(ret < 0){
            printf("epoll failure\n");
            break;
        }
        for(int i = 0; i < ret; i++){
            int sockfd = events[i].data.fd;
            if(sockfd == listenfd){
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof(client_address);
                int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
                addfd(epollfd, connfd, true);
            }
            else if(events[i].events & EPOLLIN){
                pthread_t thread;
                fds fds_for_new_worker;
                fds_for_new_worker.epollfd = epollfd;
                fds_for_new_worker.sockfd = sockfd;
                pthread_create(&thread, NULL, worker, (void*)&fds_for_new_worker);
            }
            else{
                printf("something else happened \n");
            }
        }
    }
    close(listenfd);
    return 0;
}xxxxxxxxxx c

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/253410.html

標籤:其他

上一篇:ES入門及安裝軟體

下一篇:Soul原始碼中dubbo和sofa的執行程序

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more