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集合的开销。

  • 相关阅读:
    Working with WordprocessingML documents (Open XML SDK)
    How to Choose the Best Way to Pass Multiple Models in ASP.NET MVC
    Azure:Manage anonymous read access to containers and blobs
    Convert HTML to PDF with New Plugin
    location.replace() keeps the history under control
    On the nightmare that is JSON Dates. Plus, JSON.NET and ASP.NET Web API
    HTTP Modules versus ASP.NET MVC Action Filters
    解读ASP.NET 5 & MVC6系列(6):Middleware详解
    Content Negotiation in ASP.NET Web API
    Action Results in Web API 2
  • 原文地址:https://www.cnblogs.com/xzxl/p/8575745.html
Copyright © 2011-2022 走看看