Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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\n", gai_strerror(status));
        exit(1);
    }

    //make socket
    sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
    if (sock < 0) {
        printf("\nserver 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("\nBind error %m", errno);
        exit(1);
    }

    freeaddrinfo(servinfo);

    //listen
    if(listen(sock, 5) < 0) {
        printf("\nListen 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("\nAccept error %m", errno);
        exit(1);
    }

    set_nonblock(new_sd);
    cout<<"\nSuccessful 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<<"\n"<<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<<"\n\nExiting normally\n";
    return 0;
}

Client (basically the same just minus an accept call) -

#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\n", gai_strerror(status));
        exit(1);
    }

    //make socket
    sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
    if (sock < 0) {
        printf("\nserver socket failure %m", errno);
        exit(1);
    }

    if(connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
        printf("\nclient connection failure %m", errno);
        exit(1);
    }

    cout<<"\nSuccessful 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<<"\n"<<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<<"\n\nExiting normally\n";
    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.

like image 245
Sterling Avatar asked Jul 16 '11 06:07

Sterling


People also ask

Is Select function is blocking?

By using the select() call, you do not issue a blocking call until you know that the call cannot block. The select() call can itself be blocking, nonblocking, or, for the macro API, asynchronous.

How do I make a socket non-blocking?

To mark a socket as non-blocking, we use the fcntl system call. Here's an example: int flags = guard(fcntl(socket_fd, F_GETFL), "could not get file flags"); guard(fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK), "could not set file flags"); Here's a complete example.

How do you accept non-blocking?

What would work well, if possible, is to set the socket to non-blocking mode (using fcntl() and O_NONBLOCK ) from another thread, while the socket is blocked on an accept() call. The expected behaviour is that the accept() call will return with EAGAIN or EWOULDBLOCK in errno .


1 Answers

The problem is that when I run them, nothing happens.

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.

like image 186
cnicutar Avatar answered Oct 10 '22 11:10

cnicutar