【问题标题】:setting up select() and write_fds in C在 C 中设置 select() 和 write_fds
【发布时间】:2016-04-05 01:37:21
【问题描述】:

我在设置select()write_fds 时一定做错了。目前,当我第一次调用sendData() 时,它会从缓冲区发送 117 个字节(全部)。然后立即再次调用它,即使缓冲区中没有更多数据要发送,它也会一直调用sendData()

知道我在这里做错了什么吗?

int readData(int j){
    return recv(j, buf , nbytes , 0);
}   
int sendData(int j){
    unsigned v = fcntl(j, F_GETFL, 0);
    fcntl(j, F_SETFL, v | O_NONBLOCK);

    return send(j, buf, nbytes, 0);
}   

fd_set master;   
fd_set read_fds; 
fd_set write_fds;   
int fdmax;       
...
FD_ZERO(&master);   
FD_SET(socket, &master);
fdmax = socket;

for(;;){
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);

    read_fds = master; 
    write_fds = master;
    if(select(fdmax+1, &read_fds, &write_fds, NULL, NULL) == -1){
        exit(4);
    }   
    for(i = 0; i <= fdmax; i++){
        if(FD_ISSET(i, &read_fds)){
            if(i == socket){
                // handle new connections
                addrlen = sizeof remoteaddr;
                newfd = accept(socket, (struct sockaddr *)&addr, &addrlen);
                FD_SET(newfd, &master);
                if(newfd > fdmax) fdmax = newfd;
            }else{  
                // we got some data from a client
                readData(i); 
            }   
        } else if(FD_ISSET(i, &write_fds)){
            if(i != socket){
                // send data when notified
                sendData(i);
            }
        }    
    }   
}

【问题讨论】:

  • 嗯,别再调用你不想调用的代码了。
  • 在覆盖来自master 的相同fd 集之前调用FD_ZERO 是没有意义的。
  • @Barmar 感谢您指出这一点。
  • 您要求select 在可以写入数据时唤醒。您已经可以写入数据,因此它会立即唤醒。然后你实际上并没有写任何数据,只是回到要求它在你可以写数据时唤醒......你仍然可以,所以它立即唤醒,等等。
  • @immibis 谢谢,我现在明白了那部分,但仍然不清楚处理这个问题的最佳方法是什么。你有一个应该如何做的例子吗?

标签: c sockets select


【解决方案1】:

您要求 select 在网络堆栈中有空间发送更多数据时通知您(这就是 write_fds 正在检查的内容)。由于你只写了 117 个字节,空间很大,所以它会立即返回,告诉你要写更多的数据。

由于您没有更多数据要发送,您要做的就是从write_fds 集中删除该文件描述符。如果对 send 的调用返回短写入(无法写入所有数据)或 EWOULDBLOCK(假设您已将其设置为非阻塞模式),您只想将其添加到 write_fds

这带来了您的代码的主要问题——您在没有检查返回值的情况下调用 send/recv,因此您不知道实际发送或接收了多少数据。

【讨论】:

  • 谢谢。我会跟踪send/recv 的返回值以及它是否返回-1EWOULDBLOCK。不过,我不确定我是否理解你说我应该从write_fds 中删除 fd 并在短写或EWOULDBLOCK 中删除的部分。你的意思是if (sent &lt;= 0) FD_SET (i, &amp;master); else FD_CLR (i, &amp;master);
  • @PeteDarrow:在上面的代码中,masterread_fdswrite_fds 的联合——通常您希望分别跟踪和管理它们。所以你想要的是更像If (sent &lt; tried _to_send) FD_SET(i, &amp;master_write); else FD_CLR(i, &amp;master_write);
【解决方案2】:

套接字几乎总是可写的。如果您之前有来自 send() 的 EAGAIN/EWOULDBLOCK,您应该只在套接字上选择可写性,并且在重试并成功写入后您应该停止这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 2021-06-12
    • 2010-10-13
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多