自测之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\n", 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\n");
                        }
                        else {
                                memset (szRcv, 0, BUF_SIZE);
                                iRet = read(evs[i].data.fd, szRcv, BUF_SIZE);
                                if (iRet == 0) {
                                        /* 断开连接 */
                                        printf("Disconnect fd:%d\n", 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\n", 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\n");
                                write(clientFd, "please wait...", 15);
                                close(clientFd);
                        }
                        else {
                                printf("Connect from %s:%d success...\n", 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\n");
                }
                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\n", fdArr[i], szRcv);
                                        write(fdArr[i], "I received!", 12);
                                }
                                else if (iRet == 0) {
                                        close(fdArr[i]);
                                        printf("fd:%d disconnect...\n", 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集合的开销。

 

posted @ 2018-03-15 19:35  GGBeng  阅读(206)  评论(0编辑  收藏  举报