zoukankan      html  css  js  c++  java
  • Using select() for non-blocking sockets

    原问题

    I am trying to use the select function to have non-blocking i/o between a server and 1 client (no more)

    where the communication flows nicely (can send at any time and the other will receive without waiting to send).

    I found a tutorial with some code and tried to adapt it to mine. This is what I have -

    Server

    #define PORT "4950"
    #define STDIN 0
    
    struct sockaddr name;
    
    void set_nonblock(int socket) {
        int flags;
        flags = fcntl(socket,F_GETFL,0);
        assert(flags != -1);
        fcntl(socket, F_SETFL, flags | O_NONBLOCK);
    }
    
    
    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa) {
        if (sa->sa_family == AF_INET)
            return &(((struct sockaddr_in*)sa)->sin_addr);
    
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main(int agrc, char** argv) {
        int status, sock, adrlen, new_sd;
    
        struct addrinfo hints;
        struct addrinfo *servinfo;  //will point to the results
    
        //store the connecting address and size
        struct sockaddr_storage their_addr;
        socklen_t their_addr_size;
    
        fd_set read_flags,write_flags; // the flag sets to be used
        struct timeval waitd;          // the max wait time for an event
        int sel;                      // holds return value for select();
    
        //socket infoS
        memset(&hints, 0, sizeof hints); //make sure the struct is empty
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM; //tcp
        hints.ai_flags = AI_PASSIVE;     //use local-host address
    
        //get server info, put into servinfo
        if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo error: %s
    ", gai_strerror(status));
            exit(1);
        }
    
        //make socket
        sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
        if (sock < 0) {
            printf("
    server socket failure %m", errno);
            exit(1);
        }
    
        //allow reuse of port
        int yes=1;
        if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }
    
        //unlink and bind
        unlink("127.0.0.1");
        if(bind (sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
            printf("
    Bind error %m", errno);
            exit(1);
        }
    
        freeaddrinfo(servinfo);
    
        //listen
        if(listen(sock, 5) < 0) {
            printf("
    Listen error %m", errno);
            exit(1);
        }
        their_addr_size = sizeof(their_addr);
    
        //accept
        new_sd = accept(sock, (struct sockaddr*)&their_addr, &their_addr_size);
        if( new_sd < 0) {
            printf("
    Accept error %m", errno);
            exit(1);
        }
    
        set_nonblock(new_sd);
        cout<<"
    Successful Connection!";
    
        char* in = new char[255];
        char* out = new char[255];
        int numSent;
        int numRead;
    
    
        while(1) {
    
            waitd.tv_sec = 10;
            FD_ZERO(&read_flags);
            FD_ZERO(&write_flags);
            FD_SET(new_sd, &read_flags);
    
            if(strlen(out) != 0)
                FD_SET(new_sd, &write_flags);
    
            sel = select(new_sd+1, &read_flags, &write_flags, (fd_set*)0, &waitd);
            if(sel < 0)
                continue;
    
            //socket ready for reading
            if(FD_ISSET(new_sd, &read_flags)) {
                FD_CLR(new_sd, &read_flags);
    
                memset(&in, 0, sizeof(in));
    
                if(recv(new_sd, in, sizeof(in), 0) <= 0) {
                    close(new_sd);
                    break;
                }
                else
                    cout<<"
    "<<in;
            }   //end if ready for read
    
            //socket ready for writing
            if(FD_ISSET(new_sd, &write_flags)) {
    
                FD_CLR(new_sd, &write_flags);
                send(new_sd, out, strlen(out), 0);
                memset(&out, 0, sizeof(out));
            }
        }   //end while
    
        cout<<"
    
    Exiting normally
    ";
        return 0;
    }

    Client

    #define PORT "4950"
    
    struct sockaddr name;
    
    void set_nonblock(int socket) {
        int flags;
        flags = fcntl(socket,F_GETFL,0);
        assert(flags != -1);
        fcntl(socket, F_SETFL, flags | O_NONBLOCK);
    }
    
    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa) {
        if (sa->sa_family == AF_INET)
            return &(((struct sockaddr_in*)sa)->sin_addr);
    
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main(int agrc, char** argv) {
        int status, sock, adrlen;
    
        struct addrinfo hints;
        struct addrinfo *servinfo;  //will point to the results
    
        fd_set read_flags,write_flags; // the flag sets to be used
        struct timeval waitd;          // the max wait time for an event
        int sel;                      // holds return value for select();
    
        memset(&hints, 0, sizeof hints); //make sure the struct is empty
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM; //tcp
        hints.ai_flags = AI_PASSIVE;     //use local-host address
    
        //get server info, put into servinfo
        if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo error: %s
    ", gai_strerror(status));
            exit(1);
        }
    
        //make socket
        sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
        if (sock < 0) {
            printf("
    server socket failure %m", errno);
            exit(1);
        }
    
        if(connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
            printf("
    client connection failure %m", errno);
            exit(1);
        }
    
        cout<<"
    Successful connection!";
    
        set_nonblock(sock);
    
        char* out = new char[255];
        char* in = new char[255];
        int numRead;
        int numSent;
    
        while(1) {
    
            waitd.tv_sec = 10;
            FD_ZERO(&read_flags);
            FD_ZERO(&write_flags);
            FD_SET(sock, &read_flags);
    
            if(strlen(out) != 0)
                FD_SET(sock, &write_flags);
    
    
            sel = select(sock+1, &read_flags, &write_flags, (fd_set*)0, &waitd);
            if(sel < 0)
                continue;
    
            //socket ready for reading
            if(FD_ISSET(sock, &read_flags)) {
                FD_CLR(sock, &read_flags);
    
                memset(&in, 0, sizeof(in));
    
                if(recv(sock, in, sizeof(in), 0) <= 0) {
                    close(sock);
                    break;
                }
                else
                    cout<<"
    "<<in;
            }   //end if ready for read
    
            //socket ready for writing
            if(FD_ISSET(sock, &write_flags)) {
                FD_CLR(sock, &write_flags);
                send(sock, out, strlen(out), 0);
                memset(&out, 0, sizeof(out));
            }
        }   //end while
    
        cout<<"
    
    Exiting normally
    ";
        return 0;
    }

    The problem is that when I run them, nothing happens. I can type into one and hit enter and nothing shows up on the other screen (and vice versa).

    Thats not a whole of information for me to debug and this is my first real attempt at using select so I thought maybe I am just unaware of something simple.

    If anything can be spotted as wrong or weird please point it out, any help is appreciated.

    Sterling 自问自答 https://stackoverflow.com/questions/6715736/using-select-for-non-blocking-sockets

    #define PORT "4950"
    #define STDIN 0
    
    struct sockaddr name;
    
    void set_nonblock(int socket) {
        int flags;
        flags = fcntl(socket,F_GETFL,0);
        assert(flags != -1);
        fcntl(socket, F_SETFL, flags | O_NONBLOCK);
    }
    
    
    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa) {
        if (sa->sa_family == AF_INET)
            return &(((struct sockaddr_in*)sa)->sin_addr);
    
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    
    int main(int agrc, char** argv) {
        int status, sock, adrlen, new_sd;
    
        struct addrinfo hints;
        struct addrinfo *servinfo;  //will point to the results
    
        //store the connecting address and size
        struct sockaddr_storage their_addr;
        socklen_t their_addr_size;
    
        fd_set read_flags,write_flags; // the flag sets to be used
        struct timeval waitd = {10, 0};          // the max wait time for an event
        int sel;                      // holds return value for select();
    
    
        //socket infoS
        memset(&hints, 0, sizeof hints); //make sure the struct is empty
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM; //tcp
        hints.ai_flags = AI_PASSIVE;     //use local-host address
    
        //get server info, put into servinfo
        if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo error: %s
    ", gai_strerror(status));
            exit(1);
        }
    
        //make socket
        sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
        if (sock < 0) {
            printf("
    server socket failure %m", errno);
            exit(1);
        }
    
        //allow reuse of port
        int yes=1;
        if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }
    
        //unlink and bind
        unlink("127.0.0.1");
        if(bind (sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
            printf("
    Bind error %m", errno);
            exit(1);
        }
    
        freeaddrinfo(servinfo);
    
        //listen
        if(listen(sock, 5) < 0) {
            printf("
    Listen error %m", errno);
            exit(1);
        }
        their_addr_size = sizeof(their_addr);
    
        //accept
        new_sd = accept(sock, (struct sockaddr*)&their_addr, &their_addr_size);
        if( new_sd < 0) {
            printf("
    Accept error %m", errno);
            exit(1);
        }
    
        //set non blocking
        set_nonblock(new_sd);
        cout<<"
    Successful Connection!
    ";
    
        char in[255];
        char out[255];
        memset(&in, 0, 255);
        memset(&out, 0, 255);
        int numSent;
        int numRead;
    
        while(1) {
    
            FD_ZERO(&read_flags);
            FD_ZERO(&write_flags);
            FD_SET(new_sd, &read_flags);
            FD_SET(new_sd, &write_flags);
            FD_SET(STDIN_FILENO, &read_flags);
            FD_SET(STDIN_FILENO, &write_flags);
    
            sel = select(new_sd+1, &read_flags, &write_flags, (fd_set*)0, &waitd);
    
            //if an error with select
            if(sel < 0)
                continue;
    
            //socket ready for reading
            if(FD_ISSET(new_sd, &read_flags)) {
    
                //clear set
                FD_CLR(new_sd, &read_flags);
    
                memset(&in, 0, 255);
    
                numRead = recv(new_sd, in, 255, 0);
                if(numRead <= 0) {
                    printf("
    Closing socket");
                    close(new_sd);
                    break;
                }
                else if(in[0] != '')
                    cout<<"
    Client: "<<in;
    
            }   //end if ready for read
    
            //if stdin is ready to be read
            if(FD_ISSET(STDIN_FILENO, &read_flags))
                fgets(out, 255, stdin);
    
    
            //socket ready for writing
            if(FD_ISSET(new_sd, &write_flags)) {
                //printf("
    Socket ready for write");
                FD_CLR(new_sd, &write_flags);
                send(new_sd, out, 255, 0);
                memset(&out, 0, 255);
            }   //end if
        }   //end while
    
       cout<<"
    
    Exiting normally
    ";
        return 0;
    }

    评论


    "I'm too tired to explain it, so I'll just post the code to compare with the above code if anyone in the future needs the info" that's not the way to write a helpful answer -1

    – Manu343726 Sep 30 '15 at 7:42


    In this section: 

    FD_SET(new_sd, &read_flags);         
    FD_SET(new_sd, &write_flags);         
    
    FD_SET(STDIN_FILENO, &read_flags);         
    FD_SET(STDIN_FILENO, &write_flags);

    The first 2 FD_SETs are overridden by the 2nd two. Why bother?

    – EntangledLoops Oct 12 '15 at 19:19


    @EntangledLoops I disagree - these four lines will add new_sd and STDIN_FILENO to the sets read_flags and write_flags.

    From fd_set man pages: FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set.

    – pepan May 12 '16 at 11:23


    @pepan You're obviously correct as I look at it now; don't remember what I was thinking when it read it in October. +1 for a well-bodied example

    – EntangledLoops May 12 '16 at 23:13

    另一个回答

    The real problem is that people have been pasting stuff from Beej for years without understanding it.

    That's why I don't really like that guide; it gives large blocks of code without really explaining them in detail.

    You're not reading anything and not sending anything; no fgets, scanf, cin, etc. Here's what I would do:

    FD_SET(sock, &read_flags);
    FD_SET(STDIN_FILENO, &read_flags);
    
    /* .. snip .. */
    
    if(FD_ISSET(STDIN_FILENO, &read_flags)) {
        fgets(out, len, stdin);
    }

    This will monitor stdin and read from it when input is available; then, when the socket is writeable (FD_ISSET(sock, &write_flags)), it will send the buffer.

    By  cnicutar

    评论


    Thank you for the reply. Yes, I have been reading Beej's tutorial for the last few days and am barely getting by.

    I tried adding that to my code, but now I get a segmentation fault whenever I type something into either terminal on that terminal (type 'hi' into server, server seg faults, client closes fine, vice versa).

    I tried getline to make sure it wasn't just fgets. GDB doesn't really help - tells me the backtrace is just #0 0x08048c93 in main (). Sorry to ask (little desperate at this point), but do you know what could be causing this?

    – Sterling Jul 16 '11 at 6:54


    @Sterling compile using -g. Also, for starters declare out and in like so: char out[255];.

    – cnicutar Jul 16 '11 at 7:03


    weird...when I just change those declarations the program doesn't seg fault, but it goes straight through the while loop and exits.

    – Sterling Jul 16 '11 at 7:06


    @Sterling Try to debug it. Add printf lines if you need to. Add a printf line right after the "select" printf("Select fired "); etc.

    – cnicutar Jul 16 '11 at 7:09


    Will do. Another weird thing maybe worth noting...select fires a different number of times for the two programs and a different number of times each time I run it.

    Sometimes 10, sometimes 20, sometimes 5, then closes the socket. Somehow the recv call is <= 0 without me inputting anything...also doesn't change when I leave the sockets as blocking.

    – Sterling Jul 16 '11 at 7:27

  • 相关阅读:
    今天实现了 沿路径移动
    enum类型的本质(转)
    (转)成为优秀技术人员的两点建议
    深入理解 C# 协变和逆变
    web通信
    ajax入门(复习)
    git版本管理工具的使用
    在asp.net 中使用httpmodules对网页进行处理
    asp.net http概念原理复习
    web page复习笔记
  • 原文地址:https://www.cnblogs.com/liujx2019/p/14371096.html
Copyright © 2011-2022 走看看