zoukankan      html  css  js  c++  java
  • socket编程之并发回射服务器2

    承接上文socket编程之并发回射服务器

    为了让服务器进程的终止一经发生,客户端就能检测到,客户端需要能够同时处理两个描述符:套接字和用户输入。

    可以使用select达到这一目的:

    void str_cli(FILE *fp, int sockfd) {
        int maxfdp1;
        fd_set rset;
        char sendline[MAXLINE], recvline[MAXLINE];
        
        FD_ZERO(&rset);
        for (;;) {
            FD_SET(fileno(fp), &rset);
            FD_SET(sockfd, &rset);
            maxfdp1 = max(fileno(fp), sockfd) + 1;
            select(maxfdp1, &rset, NULL, NULL, NULL);
            
            /* socket is readable */
            if (FD_ISSET(sockfd, &rset)) {
                memset(recvline, 0, sizeof(recvline));
                if (read(sockfd, recvline, MAXLINE) == 0) {
                    printf("server terminated prematurely
    ");
                    exit(1);
                }
                fputs(recvline, stdout);
            }
            /* input is readable */
            if (FD_ISSET(fileno(fp), &rset)) {
                memset(sendline, 0, sizeof(sendline));
                if (fgets(sendline, MAXLINE, fp) == NULL) {
                    return;
                }
                write(sockfd, sendline, strlen(sendline));
            }
        }
    }

    函数声明如下:

    // 返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
    int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

    之前的服务器程序通过多进程的方式来处理并发连接,我们也可以使用select对其进行改造:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <sys/select.h>
    
    #define MAXLINE 4096
    #define LISTENQ 10
    
    int main(int argc, char **argv) {
        
        int listenfd, connfd, sockfd;
        
        char buf[MAXLINE];
        socklen_t clilen;
        struct sockaddr_in servaddr, cliaddr;
        
        if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket error");
            exit(1);
        }
        
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(13);  /* daytime server */
        
        if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
            perror("bind error");
            exit(1);
        }
        
        if ( listen(listenfd, LISTENQ) < 0) {
            perror("listen error");
            exit(1);
        }
        
        int maxfd = listenfd;
        int maxi = -1;
        int nready, client[FD_SETSIZE];
        int i;
        for (i = 0; i < FD_SETSIZE; i++) {
            client[i] = -1;
        }
        fd_set rset, allset;
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);
        
        for ( ; ; ) {
            rset = allset;
            nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
            // new client connection
            if (FD_ISSET(listenfd, &rset)) {
                clilen = sizeof(cliaddr);
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
                
                for (i = 0; i < FD_SETSIZE; i++) {
                    if (client[i] < 0) {
                        client[i] = connfd;
                        break;
                    }
                }
                if (i == FD_SETSIZE) {
                    printf("too many clients
    ");
                    exit(1);
                }
                FD_SET(connfd, &allset);
                if (connfd > maxfd)
                    maxfd = connfd;
                if (i > maxi)
                    maxi = i;
                if (--nready <= 0)
                    continue;
            }
            // check all clients for data
            for (i = 0; i < FD_SETSIZE; i++) {
                if ( (sockfd = client[i]) < 0)
                    continue;
                if (FD_ISSET(sockfd, &rset)) {
                    memset(buf, 0, sizeof(buf));
                    ssize_t n;
                    if ( (n = read(sockfd, buf, MAXLINE)) == 0) {
                        close(sockfd);
                        FD_CLR(sockfd, &allset);
                        client[i] = -1;
                    } else {
                        write(sockfd, buf, n);
                    }
                    
                    if (--nready <= 0)
                        break;
                }
            }
        }
    }
    View Code

    当然了,也可以使用poll对其进行改造:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <sys/poll.h>
    #include <sys/errno.h>
    
    #define MAXLINE 4096
    #define LISTENQ 10
    #define OPEN_MAX 1024
    
    int main(int argc, char **argv) {
        
        int listenfd, connfd, sockfd;
        
        char buf[MAXLINE];
        socklen_t clilen;
        struct sockaddr_in servaddr, cliaddr;
        
        if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("socket error");
            exit(1);
        }
        
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(13);  /* daytime server */
        
        if ( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
            perror("bind error");
            exit(1);
        }
        
        if ( listen(listenfd, LISTENQ) < 0) {
            perror("listen error");
            exit(1);
        }
        
        struct pollfd client[OPEN_MAX];
        client[0].fd = listenfd;
        client[0].events = POLLRDNORM;
        int i, maxi, nready;
        for (i = 1; i < OPEN_MAX; i++) {
            client[i].fd = -1;
        }
        maxi = 0;
        
        for ( ; ; ) {
            nready = poll(client, maxi + 1, -1);
            // new client connection
            if (client[0].revents & POLLRDNORM) {
                clilen = sizeof(cliaddr);
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
                
                for (i = 1; i < OPEN_MAX; i++) {
                    if (client[i].fd < 0) {
                        client[i].fd = connfd;
                        break;
                    }
                }
                if (i == OPEN_MAX) {
                    printf("too many clients
    ");
                    exit(1);
                }
                
                client[i].events = POLLRDNORM;
                if (i > maxi)
                    maxi = i;
                if (--nready <= 0)
                    continue;
            }
        
            // check all clients for data
            for (i = 1; i <= maxi; i++) {
                if ( (sockfd = client[i].fd) < 0)
                    continue;
                if (client[i].revents & (POLLRDNORM | POLLERR)) {
                    memset(buf, 0, sizeof(buf));
                    ssize_t n;
                    if ( (n = read(sockfd, buf, MAXLINE)) < 0) {
                        if (errno == ECONNRESET) {
                            printf("connection reset by client
    ");
                            close(sockfd);
                            client[i].fd = -1;
                        } else {
                            perror("read error");
                            exit(1);
                        }
                    } else if (n == 0){
                        printf("connection closed by client
    ");
                        close(sockfd);
                        client[i].fd = -1;
                    } else {
                        write(sockfd, buf, n);
                    }
                    
                    if (--nready <= 0)
                        break;
                }
            }
        }
    }
    View Code

    函数声明如下:

    /*
     * 返回:若有就绪描述符则为其数目,若超时则为0,若出错则为-1
     * nfds:结构数组fds中元素个数
     */
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    struct pollfd {
        int   fd;             /* file descriptor */
        short events;     /* requested events */
        short revents;    /* returned events */
    };

    最后附上一张图吧:

    参考文章:

    也谈IO模型

  • 相关阅读:
    16进制与10进制
    npm模块管理器
    Vue2+VueRouter2+webpack 构建项目实战(四)接通api,先渲染个列表
    cross-env使用笔记
    webpack 运行提示“The ‘mode‘ option has not been set”的原因和解决方法
    cnpm install -S 与cnpm install -D (dependencies和devDependencies的区别)
    Webpack基础学习
    webpack入门——webpack的安装与使用
    npm init 之package.json
    入门 Webpack,看这篇就够了
  • 原文地址:https://www.cnblogs.com/gattaca/p/6403161.html
Copyright © 2011-2022 走看看