zoukankan      html  css  js  c++  java
  • 利用select实现IO多路复用TCP服务端

    一、相关函数

      1.  int select(int maxfdp, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

      int maxfdp: 该参数是指集合中所有文件描述符的范围, 即所有文件描述符的最大值加1;

      fd_set *readset: 该参数是我们所关心的文件是否可读的文件描述符的集合, 如果这个集合中有个文件可读了,那select返回一个大于0的数,表示有文件可读了

      fd_set *writeset:......可写......

      fd_set *exceptset: ......异常发生......

      timeval *timeout:该参数是select的超时参数,这个参数使select处于三种状态:(1)timeout传入NULL,则select一直等到文件状态有变化时才返回,这段时间一直处于阻塞状态。(2):timeout 传入0,则select会立即返回(非阻塞),如果文件状态有变化则返回一个大于0的值没有变化则返回0;(3)timeout传入一个大于0的数,则select在timeout时间内阻塞,一旦文件状态有变化就会返回,超时后不管怎样都会返回值同样是文件状态右边话就返回一个大于0的值,无变化则返回0;

      timeval的结构:

    struct timeval{      
        long tv_sec;   /**/
        long tv_usec;  /*微秒 */   
    }

      select返回一个小于0的数则说明出错。

      

      2.fd_set

      fd_set类型变量每一位代表了一个描述符。我们也可以认为它只是一个由很多二进制位构成的数组,如下图所示:

      

      操作fd_set的宏有:

    #include <sys/select.h>   
    int FD_ZERO(int fd, fd_set *fdset);   
    int FD_CLR(int fd, fd_set *fdset);   
    int FD_SET(int fd, fd_set *fd_set);   
    int FD_ISSET(int fd, fd_set *fdset);

      FD_ZERO宏将一个 fd_set类型变量的所有位都设为 0,使用FD_SET将变量的某个位置位。清除某个位时可以使用 FD_CLR,我们可以使用FD_ISSET来测试某个位是否被置位(即状态是否发生了变化)。

      当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位

    二、TCP服务端代码

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <ctype.h>
    #include "wrap.h"
    #define MAXLINE 80
    #define SERV_PORT 8001
    int main(int argc, char **argv)
    {
        int i, maxi, maxfd, listenfd, connfd, sockfd;
        int nready, client[FD_SETSIZE];
        ssize_t n;
        fd_set rset, allset;
        char buf[MAXLINE];
        char str[INET_ADDRSTRLEN];
        socklen_t cliaddr_len;
        struct sockaddr_in
        cliaddr, servaddr;
        listenfd = Socket(AF_INET, SOCK_STREAM, 0);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(SERV_PORT);
        Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        Listen(listenfd, 20);
        maxfd = listenfd;
        maxi = -1;
        for (i = 0; i < FD_SETSIZE; i++)
            client[i] = -1; /* -1 indicates available entry */
        FD_ZERO(&allset);
        FD_SET(listenfd, &allset);
        for ( ; ; ) {
            rset = allset; /* structure assignment */
            nready = select(maxfd+1, &rset, NULL, NULL, NULL);
            if (nready < 0)
                perr_exit("select error");
            if (FD_ISSET(listenfd, &rset)) { /* new client connection */
                cliaddr_len = sizeof(cliaddr);
                connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
                printf("received from %s at PORT %d
    ",
                inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                ntohs(cliaddr.sin_port));
                for (i = 0; i < FD_SETSIZE; i++)
                    if (client[i] < 0) {
                        client[i] = connfd; /* save descriptor */
                        break;
                    }
                if (i == FD_SETSIZE) {
                    fputs("too many clients
    ", stderr);
                    exit(1);
                }
                FD_SET(connfd, &allset); /* add new descriptor to set */
                if (connfd > maxfd)
                    maxfd = connfd; /* for select */
                if (i > maxi)
                    maxi = i; /* max index in client[] array */
                if (--nready == 0)
                    continue; /* no more readable descriptors */
            }
            for (i = 0; i <= maxi; i++) {
                /* check all clients 714 for data */
                if ( (sockfd = client[i]) < 0)
                    continue;
                if (FD_ISSET(sockfd, &rset)) {
                    if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
                        Close(sockfd);
                        FD_CLR(sockfd, &allset);
                        client[i] = -1;
                    } else {
                        int j;
                        for (j = 0; j < n; j++)
                            buf[j] = toupper(buf[j]);
                        Write(sockfd, buf, n);
                    }
                    if (--nready == 0)
                        break; /* no more readable descriptors */
                }
            }
        }
    }

    备:封装原始linux函数wrap.c:

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <unistd.h>
    
    void perr_exit(const char *s)
    {
        perror(s);
        exit(1);
    }
    
    int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
    {
        int n;
    again:
        if ((n = accept(fd, sa, salenptr)) < 0) {
            if ((errno == ECONNABORTED) || (errno == EINTR))
                goto again;
            else 
                perr_exit("accept error");
        }
    
        return n;
    }
    
    void Bind(int fd, struct sockaddr *sa, socklen_t salen)
    {
        if (bind(fd, sa, salen) < 0)
            perr_exit("bind error");
    }
    
    void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
    {
        if (connect(fd, sa, salen) < 0)
            perr_exit("connent error");
    }
    
    void Listen(int fd, int backlog)
    {
        if (listen(fd, backlog) < 0) 
            perr_exit("listen error");
    }
    
    int Socket(int family, int type, int protocol)
    {
        int n;
        if ((n = socket(family, type, protocol)) < 0) 
            perr_exit("socket error");
        return n;
    }
    
    ssize_t Read(int fd, void *ptr, size_t nbytes)
    {
        ssize_t n;
    again:
        if ((n = read(fd, ptr, nbytes)) < 0) {
            if (errno == EINTR)
                goto again;
            else 
                return -1;
        }
    
        return n;
    }
    
    ssize_t Write(int fd, const void *ptr, size_t nbytes)
    {
        ssize_t n;
    again:
        if ((n = write(fd, ptr, nbytes)) == -1) {
            if (errno == EINTR)
                goto again;
            else 
                return -1;
        }
    
        return n;
    }
    
    void Close(int fd)
    {
        if (close(fd) == -1)
            perr_exit("close error");
    }
    
    ssize_t Readn(int fd, void *vptr, size_t n)
    {
        size_t nleft;
        ssize_t nread;
        char *ptr;
    
        ptr = vptr;
        nleft = n;
        while (nleft > 0) {
            if ((nread = read(fd, ptr, nleft))  < 0) {
                if (errno == EINTR)
                    nread = 0;
                else 
                    return -1;
            } else if (nread == 0) {
                break;
            }
            nleft -= nread;
            ptr += nread;
        }
    
        return n - nleft;
    }
    
    ssize_t Writen(int fd, const void *vptr, size_t n)
    {
        size_t nleft;
    
        ssize_t nwritten;
        const char *ptr;
    
        ptr = vptr;
        nleft = n;
        while (nleft > 0) {
            if ((nwritten = write(fd, ptr, nleft)) <= 0) {
                if (nwritten < 0 && errno == EINTR)
                    nwritten = 0;
                else 
                    return -1;
            }
    
            nleft -= nwritten;
            ptr += nwritten;
        }
    
        return n;
    }
    
    static ssize_t my_read(int fd, char *ptr)
    {
        static int read_cnt;
        static char *read_ptr;
        static char read_buf[100];
    
        if (read_cnt <= 0) {
        again:
            if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
                if (errno == EINTR)
                    goto again;
                return -1;
            } else if (read_cnt == 0)
                return 0;
            read_ptr = read_buf;
        }
        read_cnt--;
        *ptr = *read_ptr++;
        return 1;
    }
    
    ssize_t Readline(int fd, void *vptr, size_t maxlen)
    {
        ssize_t n, rc;
        char c, *ptr;
    
        ptr = vptr;
        for (n = 1; n < maxlen; n++) {
            if ((rc = my_read(fd, &c)) == 1) {
                *ptr++ = c;
                if (c == '
    ') 
                    break;
            } else if (rc == 0) {
                *ptr = 0;
                return n - 1;
            } else {
                return -1;
            }
        }
        *ptr = 0;
        return n;
    }
  • 相关阅读:
    二进制拆分线段树
    2017 初赛PJ 错题解析
    线段树基操
    2015 初赛PJ 错题解析
    2016 初赛TG 错题解析
    拓扑排序找最大环最小环
    长乐集训合集
    java读取网页
    java下socket传图片
    java下socket传文件
  • 原文地址:https://www.cnblogs.com/orlion/p/6119812.html
Copyright © 2011-2022 走看看