简体   繁体   中英

select() and non-blocking recv with dynamic buffer on C

I'm trying to figure out why recv is blocking in the code below, if I telnet and send a 'GET / HTTP/1.1', recv keeps waiting for data and blocks another telnet connection as well. However, it works fine and doesn't block if I just use a fixed buffer instead of the do{} while , ie,

char buffer[1024];
nbytes = recv(i, buffer, sizeof buffer, 0);

What I understand so far, select() at that point is in a ready-to-read state. Do I need to set recv to O_NONBLOCK ?

char *buffer = NULL;
unsigned long LEN = 200;
unsigned long bytes_received = 0;
unsigned long cur_size = 0;
int status = 0;

FD_SET(listener, &master);
fdmax = listener;

for(;;){
    read_fds = master; // copy it
    if(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1){
        exit(4);
    }   

    for(i = 0; i <= fdmax; i++){
        if(FD_ISSET(i, &read_fds)){
            if(i == listener){
                // handle new connections
                addrlen = sizeof remoteaddr;
                newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);

                if(newfd == -1){
                    perror("accept");
                }else{
                    FD_SET(newfd, &master);
                    if(newfd > fdmax) { 
                        fdmax = newfd;
                    }
                }
            }else{
                do{
                    if(bytes_received >= cur_size){
                        char * tmp;
                        cur_size += LEN;
                        tmp = realloc(buffer, cur_size);
                        buffer = tmp;
                    }
                    status = recv(i, buffer + bytes_received, LEN, 0); 
                    if(status == 0){ 
                        printf("Done\n");
                    }
                    else if(status > 0){ 
                        bytes_received += status;
                    }
                    else{
                        fprintf(stderr, "socket error\n");
                    }
                } while (status > 0); 
                if(send(i, buffer, strlen(buffer), 0) == -1){
                    perror("send");
                }   
            }   
        }   
    }
}

First, you have to set the socket non-blocking if you don't want to block. No matter what you do, it is not possible to assure that you never block if your socket operations are blocking. This is a commonly-made mistake that has caused serious bugs with significant security implications. If you must not block, you must set the socket non-blocking.

What I understand so far, select() at that point is in a ready-to-read state.

You mean was in a ready-to-read state. The select function is a status-reporting function and, like other status-reporting functions, it reports the status as of some point in-between when you called it and when it returned. There is no guarantee that is still the status at some later point.

Do not ever think you can rule out every other possible way the status might change. History is filled with people who thought that and got burned.

But the specific reason that you are blocking is that you are calling recv without first calling select (because you have a do loop that calls recv again).

No matter what, you must correctly handle a WOULDBLOCK indication from recv . That is essential.

In addition, you may wish to call recv only once per select hit. If you fix the other issues, it won't matter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM