select()和非阻塞recv与C上的动态缓冲区

时间:2016-03-26 01:13:46

标签: 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");
                }   
            }   
        }   
    }
}

1 个答案:

答案 0 :(得分:2)

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

  

到目前为止我所理解的是,此时的select()处于准备读取状态。

你的意思是 处于准备阅读状态。 final JButton button = new JButton(); Action action = new AbstractAction("Bold") { Font font = (Font) UIManager.get("Button.font"); @Override public void actionPerformed(ActionEvent e) { button.setFont(font.deriveFont(Font.BOLD)); } }; button.setAction(action); 函数是一种状态报告功能,与其他状态报告功能一样,它会在您调用它和返回时报告状态。无法保证在稍后的某个时间仍然是状态。

不要以为你可以排除状态可能改变的所有其他可能方式。历史充满了那些认为并被烧毁的人。

但是你阻止的具体原因是你在没有先调用select的情况下调用recv(因为你有一个select循环再次调用do)。

无论如何,您必须正确处理来自recv的{​​{1}}指示。这很重要。

此外,您可能希望每WOULDBLOCK次点击只调用一次recv。如果你解决了其他问题,那就无所谓了。