zoukankan      html  css  js  c++  java
  • 自测之Lesson16:并发通信

    知识点:三个多路并发模型(select 、poll 、epoll)

    题目:以epoll模型,编写一个可供多个客户端访问的服务器程序。

    实现代码:

    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <errno.h>
    #include <stdio.h>
    #include <sys/epoll.h>
    #include <string.h>
    
    #define SRV_PORT 60000
    #define BUF_SIZE 1024
    #define MAX_CONN 10000000                               // 最大连接数 
    #define EVS_LEN  10                                     // epoll_wait()可保存的“收到数据”的fd的最大数目
    
    void startServer()
    {
            int iRet;
            char szSnd[BUF_SIZE];
            char szRcv[BUF_SIZE];
            char szBuf[BUF_SIZE];
        
            int fd; 
            fd = socket(PF_INET, SOCK_STREAM, 0); 
            if (fd == -1) {
                    perror("fail socket");
                    return;
            }
    
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = htonl(INADDR_ANY);
            addr.sin_port = htons(SRV_PORT);
            iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
            if (iRet == -1) {
                    perror("fail bind");
                    close(fd);
                    return;
            }
    
            listen(fd, MAX_CONN);
    
    /*=========================================== epoll =======================================*/
            int epfd = epoll_create(MAX_CONN);                                      // function 1
            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = STDIN_FILENO;
            epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);                      // function 2
            ev.events = EPOLLIN;
            ev.data.fd = fd;
            epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    
            int clientFd;
            struct sockaddr_in cliAddr;
            socklen_t addrLen = sizeof(cliAddr);
            struct epoll_event evs[EVS_LEN];                                        // 可容纳10个变化的fd
            int cnt;
            while(1) {
                    cnt = epoll_wait(epfd, evs, EVS_LEN, -1);                       // function 3
                    int i;
                    for (i = 0; i < cnt; i++) {
                            if (evs[i].data.fd == fd) {
                                    /* new connect */
                                    clientFd = accept(fd, (struct sockaddr*)&cliAddr, &addrLen);
                                    ev.events = EPOLLIN;
                                    ev.data.fd = clientFd;
                                    epoll_ctl(epfd, EPOLL_CTL_ADD, clientFd, &ev);
                                    printf("New connect from %s:%d
    ", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
                                    write(clientFd, "Welcome...", 11);
                            }
                            else if (evs[i].data.fd == STDIN_FILENO) {
                                    read(STDIN_FILENO, szSnd, BUF_SIZE);
                                    printf("command not found
    ");
                            }
                            else {
                                    memset (szRcv, 0, BUF_SIZE);
                                    iRet = read(evs[i].data.fd, szRcv, BUF_SIZE);
                                    if (iRet == 0) {
                                            /* 断开连接 */
                                            printf("Disconnect fd:%d
    ", evs[i].data.fd);
                                            ev.data.fd = evs[i].data.fd;
                                            epoll_ctl(epfd, EPOLL_CTL_DEL, evs[i].data.fd, NULL);
                                            close(evs[i].data.fd);
                                    }
                                    else if (iRet < 0) {
                                            perror("fail read");
                                            return;
                                    }
                                    else {
                                            memset(szBuf, 0, BUF_SIZE);
                                            printf("Recv[%d]:%s
    ", evs[i].data.fd, szRcv);
                                            memcpy(szBuf, "I have received!", 17);
                                            write(evs[i].data.fd, szBuf, strlen(szBuf));
                                    }
    
                            }
                    }
            }
            close(fd);
            return;
    }
    
    int main()
    {
            startServer();
            return 0;
    }

    题目:以select模型,编写一个可供多个客户端访问的服务器程序。

    实现代码:

    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    
    #define SRV_PORT 60000
    #define BUF_SIZE 1024
    #define MAX_CONN 10000 
    
    void startServer()
    {
            int iRet;
            char szSnd[BUF_SIZE];
            char szRcv[BUF_SIZE];
        
            int fd; 
            fd = socket(PF_INET, SOCK_STREAM, 0); 
            if (fd == -1) {
                    perror("fail socket");
                    return;
            }
    
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = htonl(INADDR_ANY);
            addr.sin_port = htons(SRV_PORT);
            iRet = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
            if (iRet == -1) {
                    perror("fail bind");
                    close(fd);
                    return;
            }
    
            listen(fd, MAX_CONN);
    /**************************************************** select *********************************************************/
            fd_set fdset;
            int i;
            int maxfd = fd;
            int clientFd;
            int fdCnt = 0;                          // 当前连接的客户端数
            int fdArr[MAX_CONN];                    // 存放文件描述符fd的数组
            struct sockaddr_in cliAddr;
            socklen_t addrLen = sizeof(cliAddr);
            while(1) {
                    /* select模型每次都要将“fd们”重新加入fdset,开销很大 */
                    FD_ZERO(&fdset);
                    FD_SET(STDIN_FILENO, &fdset);
                    FD_SET(fd, &fdset);
                    for (i = 0; i < fdCnt; i++) {
                            FD_SET(fdArr[i], &fdset);       // 将用于和客户端通信的fd都加入fdset
                    }
    
                    select(maxfd + 1, &fdset, NULL, NULL, NULL);
                    if (FD_ISSET(fd, &fdset)) {
                            clientFd = accept(fd, (struct sockaddr*)&cliAddr, &addrLen);
                            if (fdCnt == MAX_CONN) {
                                    printf("Connect over count
    ");
                                    write(clientFd, "please wait...", 15);
                                    close(clientFd);
                            }
                            else {
                                    printf("Connect from %s:%d success...
    ", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port));
                                    write(clientFd, "Welcome...", 11);
                                    fdArr[fdCnt++] = clientFd;
                                    if (clientFd > maxfd) {
                                            maxfd = clientFd;       // 更新maxfd
                                    }
                            }
                    }
                    if (FD_ISSET(STDIN_FILENO, &fdset)) {
                            memset(szSnd, 0, BUF_SIZE);
                            read(STDIN_FILENO, szSnd, 1024);
                            printf("command not found
    ");
                    }
                    for (i = 0; i < fdCnt; i++) {
                            if (FD_ISSET(fdArr[i], &fdset)) {
                                    memset(szRcv, 0, BUF_SIZE);
                                    iRet = read(fdArr[i], szRcv, BUF_SIZE);
                                    if (iRet > 0) {
                                            printf("Recv[%d]:%s
    ", fdArr[i], szRcv);
                                            write(fdArr[i], "I received!", 12);
                                    }
                                    else if (iRet == 0) {
                                            close(fdArr[i]);
                                            printf("fd:%d disconnect...
    ", fdArr[i]);
                                            int j;
                                            for(j = i; j < fdCnt - 1; j++) {
                                                    fdArr[j] = fdArr[j+1];
                                            }
                                            fdCnt--;
                                            i--;
                                    }
                                    else {
                                            perror("read fail");
                                            return;
                                    }
                            }
                    }
            }
            return;
    }
    
    int main()
    {
            startServer();
            return 0;
    }
    

      

    小结:epoll模型的优点在于:①对于客户端的数量没有限制;②内核主动将“可读”的fd写入到struct epoll_events数组内,所以节省了poll模型和select模型的每次轮询整个fd集合的开销。

  • 相关阅读:
    HDU 5852 Intersection is not allowed!(LGV定理行列式求组合数)题解
    FJNU2018低程F jq解救fuls (贪心乱搞)题解
    Gym 101775J Straight Master(差分数组)题解
    FJUT3591 侦测到在途的聚变打击(最小不可相交路径覆盖)题解
    HDU 6406 Taotao Picks Apples & FJUT3592 做完其他题后才能做的题(线段树)题解
    vijos 1907 飞扬的小鸟
    vijos 1779 国王游戏
    BZOJ 3439 Kpm的MC密码
    BZOJ 3163 Eden的新背包问题
    codevs 5429 完全背包
  • 原文地址:https://www.cnblogs.com/xzxl/p/8575745.html
Copyright © 2011-2022 走看看