c - select() 和 C 上带有动态缓冲区的非阻塞 recv

标签 c sockets blocking nonblocking

我试图弄清楚为什么 recv 在下面的代码中被阻塞,如果我 telnet 并发送“GET/HTTP/1.1”,recv 会一直等待数据并阻塞另一个 telnet 连接。但是,如果我只使用固定缓冲区而不是 do{} while,它工作正常并且不会阻塞,即

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

据我了解,此时 select() 处于可读状态。我需要将 recv 设置为 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");
                }   
            }   
        }   
    }
}

最佳答案

首先,如果不想阻塞,就必须将套接字设置为非阻塞。无论您做什么,如果您的套接字操作阻塞,则无法确保您永远不会阻塞。这是一个常见的错误,它导致了具有重大安全隐患的严重错误。如果一定不能阻塞,则必须将套接字设置为非阻塞。

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

您的意思是处于可读状态。 select 函数是一个状态报告函数,与其他状态报告函数一样,它会报告您调用它和返回它之间的某个时间点的状态。不能保证在以后的某个时间点仍然是该状态。

永远不要认为您可以排除状态可能发生变化的所有其他可能方式。历史上到处都是这样想并被烧毁的人。

但是您阻塞的具体原因是您在调用 recv 时没有先调用 select (因为您有一个 do 循环再次调用 recv

无论如何,您必须正确处理来自recvWOULDBLOCK 指示。这是必不可少的。

此外,您可能希望每次 select 命中时仅调用一次 recv。如果您解决了其他问题,则没有关系。

关于c - select() 和 C 上带有动态缓冲区的非阻塞 recv,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36230500/

相关文章:

javascript - 将 socket.io 服务器包含到我的 html 中

sockets - LuaSocket 用于测试互联网连接是否存在

java - 从 InputStream 读取 - 忙等待的非阻塞 VS 超时的阻塞?

php inotify 阻塞但超时

java - BufferedInputStream 说它不可用,但仍然可以工作

c - 双指针和字符

c - 给结构体数组赋值的问题!

c - 我的代码中有一个谜团,在 C 中以递归方式使用过程进行合并排序

c++ - 如何为局部变量分配内存?

Android 服务通信,通过 SSL 使用原始字节