【问题标题】:FD_ISSET() after select() not detecting received dataselect() 后的 FD_ISSET() 未检测到接收到的数据
【发布时间】:2014-05-15 16:26:46
【问题描述】:

我有一个程序,我设计为在一堆不同的节点上运行,以根据它们从主节点上运行的进程获得的指令在它们之间传输文件。每个进程都充当发送者和接收者。

我的策略是这样的:

//Setup a listener socket on port 63000(say) to listen to commands from the master    
//socket. This listener will only listen for commands and read these commands

/* 1) Setup a listener socket on port 63001(say) to listen to connection requests from 
   (NUMNODES-1) other processes on the rest of the nodes
   2) Accept each of the connections from the rest of the (NUMNODES-1) nodes and fill  
   these conneced descriptors into an array of integers  */

for(i=1;i<NUMNODES;i++){
   connfd=accept(...,&node_addr,...);
   index=findNodeIndex(node_addr); //To find the node number, corresp to this address
   connections[index]=connfd; 
}

/* Skipping all details aside, assuming i have a process running on node number 4 (out  
   of a total of 10 nodes, say) connections[4] will be -1, and connections[1..3, 5..10] 
   will hold connection descriptors to each of the other nodes, ready to read any file 
   that they decide to transfer */

fd_set read_set;
for(i=1;i<=NUMNODES;i++){
    if(i==thisNodeNum())   //For nodenum 4, the connections[4] will be -1
        continue;          //So we don't want to put it into the read fd_set

     FD_SET(connections[i],&read_set);
}

fd_set temp_rset;

//A select() call ready to listen to any data that comes in from any of the nodes
for(;;){
    temp_rset=read_set;
    select(maxfdp1,&temp_rset,NULL,NULL,NULL);

    //Listening for commands from master goes here
    if(FD_ISSET(commandListener,&temp_rset){
        ... listen to command and send file to a particular node...
        ... File will be sent to port 63001 on that node...
        ... commandLIstener listens for commands on port 63000(just a reminder)...
    }

    //Listening for data that has come in from the other nodes
    for(i=1;i<=NUMNODES;i++){
        if(FD_ISSET(connections[i],&temp_rset){
            ... write file to the local hard disk, byte by byte...
        }
    }//End of connections[1..NUMNODES] testing for data to read

}//End of infinite for loop

我的问题是我的主人在端口号 63001 上向它喜欢的任何节点发送命令,并且命令被接收并采取行动。文件逐字节发送到适当的节点(例如,主命令节点 5 将文件发送到节点 9.. 节点 5 上的进程将利用连接 [9] 将文件发送到节点 9 上的进程。 .. 节点 9 上的进程将接收连接 [5] 上的数据...至少这是我想要发生的)

文件被发送到正确节点上的适当端口(节点 9 @ 端口 63001), 但是节点 9 上的 FD_ISSET(connections[i], &temp_rset) 条件永远不会检测到任何发送的数据。我使用 tsharktcpdump 进行了检查,数据确实被发送到节点 9,但 select() 调用从不接收任何内容。

我做错了什么?

【问题讨论】:

  • 您必须在循环中每次调用 select() 之前重新设置 fd_set - 请参阅 stackoverflow.com/questions/4563577/…
  • @ChrisStratton:这就是 OP 代码的作用。 (见:temp_rset=read_set;
  • 对不起,我错过了。也许弄清楚 select() 是否正在返回,如果是这样,why 它会在没有提供超时的情况下返回。返回值是多少?它是否表明应该检查errno?
  • for(i=1;i&lt;=NUMNODES;i++){ C 中的索引从零开始 -->> for(i=0;i&lt; NUMNODES;i++){

标签: c linux sockets


【解决方案1】:

您的接受循环接受来自NUMNODES-1 其他节点的连接。因此,如果您有NUMNODES 节点都运行此代码,这意味着它们都必须对每个其他节点执行connect。这意味着您最终将在每对节点之间建立两个连接,一个从每个方向启动。

现在,当您为 select 循环设置 read_set 时,看起来您只查看您接受的连接,而不是您调用 connect 的连接。如果您还在已接受的连接(而不是已连接的连接)上发送数据,则另一端的进程不会注意到它,因为它正在等待已接受的连接而不是其连接的连接。

【讨论】:

    【解决方案2】:

    您的代码应如下所示:

    fd_set read_set; // set of file descriptors (in this case sockets)
    int result;
    
    for(;;)
    {
        FD_ZERO(&read_set); // you need to clear the set first!
        FD_SET(commandListener, &read_set); // add command socket to the set 
        for(i=1; i<=NUMNODES; i++) // add node sockets to the set
        {
            if(i==thisNodeNum()) continue;
            FD_SET(connections[i], &read_set);
        }
    
        // check status of all sockets from the set
        result = select(maxfdp1, &read_set, NULL, NULL, NULL); // instead of tracking maxfdp1 you can use FD_SETSIZE (maximum allowed size of set)
    
        if(result == -1) // error
        {
            perror("select() error");
        }
        else if(result > 0) // there is new data
        {
            if(FD_ISSET(commandListener, &read_set) // there is new command data
            {
                // (...) handling your tasks here
            }
    
            for(i=1; i<=NUMNODES; i++) 
            {
                if(FD_ISSET(connections[i], &read_set) // there is new data on i-th node 
                {
                    // (...) handling your tasks here
                }
            }
        }
    
    }//End of infinite for loop
    

    注意事项:

    1. 您忘记在代码中清除和重建文件描述符(套接字)集(select 修改它 - 请注意稍后使用 FD_ISSET 检查它)。

    2. 您可以将所有套接字放在一组中,并使用 select 一次检查它们,然后确定哪些有新数据(如果有)。

    3. 我不确定您如何索引表“连接”,因为代码中的循环是 1..NUMNODES 而不是自然的 C/C++ 表索引 0..NUMNODES-1。

    【讨论】:

    • 您不必每次都重建整个fd_set。只需创建 2 套,并将memcpy 主一套传入传递给select 的一套。当然,像FD_SETFD_CLR 这样的所有操作都将在主节点上执行。
    • fd_set 上使用memcpy() 并非在所有平台上都是安全的。一些平台有一个FD_COPY() 宏来安全地复制fd_set
    • a FD_SET 保证是一个可修改的左值。您可以只分配:workingset = readset; 这里不需要 memcpy()。
    猜你喜欢
    • 1970-01-01
    • 2015-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 2015-03-22
    • 2020-10-14
    相关资源
    最近更新 更多