2013-01-24 30 views
2

我已经写了接受新客户的数据和来自客户的服务器代码。但问题在于select并没有等到超时,尽管没有来自客户端的数据。我想等待5秒钟,并为可用客户端发送心跳。但它在第一次迭代中等待5秒钟,然后在下一次迭代中快速发送心跳。如何解决这个问题呢。提前致谢。选择在C++中的插座不等待超时值

void * Communicate(void * id) 
{ 
int *iSockID = (int *) id; 
int listener = *iSockID; 

fd_set master; // master file descriptor list 
fd_set read_fds; // temp file descriptor list for select() read 
fd_set write_fds; // temp file descriptor list for select() read 
int fdmax;  // maximum file descriptor number 

int i, j, rv; 

FD_ZERO(&master); // clear the master and temp sets 
FD_ZERO(&read_fds); 
FD_ZERO(&write_fds); 
// add the listener to the master set 
FD_SET(listener, &master); 
printf("Listener is %d \n" , listener); 

// keep track of the biggest file descriptor 
fdmax = listener; // so far, it's this one 
//accept 3 clients 


// main loop 
for(;;) { 
    read_fds = master; // copy it 
    write_fds = master; 
    struct timeval tv; 
    tv.tv_sec = 5; 
    tv.tv_usec = 0; 
    int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv) ; 
    if (iResult == -1) 
    { 
     perror("select"); 
     exit(4); 
    } 

    for(i = 0; i <= fdmax; i++) 
    {   
     //send work for clients 
     SendHeartBeats(write_fds , fdmax , listener , i); 

    } 

    // run through the existing connections looking for data to read 
    // ADD NEW CONNECTIONS READ FROM CONNECTIONS  
    for(i = 0; i <= fdmax; i++) 
    { 


     if (FD_ISSET(i, &read_fds)) 
     { // we got one!! 
      // handle new connections    
      if (i == listener) 
      {          
       AcceptNewClients(master , fdmax , listener); 
      } else 
      { 
       AccepeDataFromClients(i , master); 
      } // END handle data from client 
     } // END got new incoming connection 
    } // END looping through file descriptors 
    sleep(3); 
} // END for(;;) 
return 0; 
} 
+0

我不能没有看到SendHeartBeats'的'的实施提供了更详细的解答。 – jxh

回答

0

您在您使用Linux 2.6.27评论说。由于在Linux中,fd_set是一个包含整数类型数组的结构,使用指定将master复制到read_fds是很好的。

您的select呼叫未被阻止的原因是因为您正在从master初始化read_fdswrite_fds。你闲置的客户端套接字总是可写的,所以你立即醒来。顺便说一下,在你的listener插座上选择写入是毫无意义的。

当您尝试写入但应对EAGAIN/EWOULDBLOCK错误时,您应该只在write_fds中设置客户端套接字。

1

您不能通过等于fd_set变量。你需要使用FD_COPY。如果你不这样做,你只需要将一个句柄复制到已经标记为已完成的实际数据。

+0

+1。 'fdmax = listener;'只是等着失败,尤其是考虑到'AcceptNewClients(master,fdmax,listener)'的尴尬需要;''更新'fdmax'。而且,代码不应该执行'sleep(3)' - 让超时处理该事件,执行超时后超时检查,以避免在您关心时过度频繁地检测心跳。 –

+0

我已经包含sys/time.h文件。但FD_COPY给我错误FD_COPY没有在这个范围内声明。 @Tony我使用了睡眠(),因为超时无效。你告诉我我在做什么错在这里?? –

+0

@Zeus:如果你不重建你的FD设置并提供正确的最大值,超时将不起作用......一旦有任何事情被设置,我会保持沉默。对于FD_COPY - 您是否检查了您的手册页或其他系统文档? stys/time.h在某些系统(例如某些IBM)上绝对是正确的 - 但这些东西可能会有所不同,或者根本不可用。您可以重置/清除它并再次设置标志,或者在某些不需要使用FD_COPY的系统上 - 例如,检查GNU libc如何在http://www.gnu.org/software/libc/manual /html_node/Server-Example.html –

0

您需要更改代码,重新初始化read_fdwrite_fd变量每次select()被调用时,它会修改他们退出后,所以你需要每次都重新设置。正如其他人所说,使用=运算符来复制master变量不是复制fd_set结构的正确方法。

试试这个:

void * Communicate(void * id) 
{ 
    int *iSockID = (int *) id; 
    int listener = *iSockID; 

    fd_set master; // master file descriptor list 
    fd_set read_fds; // temp file descriptor list for select() read 
    fd_set write_fds; // temp file descriptor list for select() read 
    struct timeval tv; 
    int fdmax;  // maximum file descriptor number 
    int i, j, rv; 

    printf("Listener is %d \n", listener); 

    // add the listener to the master set 
    FD_ZERO(&master); 
    FD_SET(listener, &master); 

    // keep track of the biggest file descriptor 
    fdmax = listener; // so far, it's this one 
    //accept 3 clients 

    // main loop 
    clock_t c1 = clock(); 
    while (1) 
    { 
     FD_ZERO(&read_fds); 
     FD_ZERO(&write_fds); 

     #ifdef MSWINDOWS 
     // Windows does not have FD_COPY() 
     for (u_int i = 0; i < master.fd_count; ++i) 
     { 
      FD_SET(master.fd_array[i], &read_fd); 
      FD_SET(master.fd_array[i], &write_fd); 
     } 
     #else 
     FD_COPY(&master, &read_fd); 
     FD_COPY(&master, &write_fd); 
     #endif 

     tv.tv_sec = 1; 
     tv.tv_usec = 0; 

     int iResult = select(fdmax+1, &read_fds, &write_fds, NULL, &tv); 
     if (iResult == -1) 
     { 
      perror("select"); 
      exit(4); 
     } 

     clock_t c2 = clock(); 
     if (((c2-c1)/CLOCKS_PER_SEC) >= 5) 
     { 
      c1 = c2; 
      for(i = 0; i <= fdmax; i++) 
      {   
       if ((i != listener) && FD_ISSET(i, &write_fds)) 
       { 
        //send work for client 
        SendHeartBeat(i); 
       } 
      } 
     } 

     // run through the existing connections looking for data to read 
     // ADD NEW CONNECTIONS READ FROM CONNECTIONS  
     for(i = 0; i <= fdmax; i++) 
     { 
      if (FD_ISSET(i, &read_fds)) 
      { 
       // we got one!! 
       if (i == listener) 
       {          
        AcceptNewClient(master, fdmax, listener); 
       } 
       else 
       { 
        AcceptDataFromClient(i); 
       } 
      } 
     } 
    } 

    return 0; 
} 
+0

非常感谢您的回答。问题是存在编译错误。“FD_COPY未在此范围内声明”。 –

+0

如果套接字API为你的编译器没有'FD_COPY()'中(而非所有平台做),那么你就必须手动循环throuh了'master'的名单,像我展示的Windows。 –