LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 01-15-2008, 05:58 PM   #1
bryanhdl
LQ Newbie
 
Registered: Jan 2008
Posts: 5

Rep: Reputation: 0
FD_ISSET does not detect new file descriptor.


Hi,

I'm new in Linux and having problem with FD_ISSET to detect second file descriptor for udp socket. There are 2 clients connecting to my udp socket server, I'm setting FD_SET (fdA), FD_SET (fdB) for each client. But the FD_ISSET is only detect the first fdA for the client B (expected FD_ISSET(fdB) is TRUE)

Do you know how to detect activity for fdB?

Thank you,

Bryanhdl
 
Old 01-15-2008, 11:49 PM   #2
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
I cannot fully comment on what is causing the issue you are having, however the code below should work; of course, you will need to add the "extras" around it to meet your needs.

If you are planning to support more than two clients, I would recommend that you consider going with a multi-threaded application, where one thread listens for connections, and another thread handles client requests.

PHP Code:
fd_set rfds;
fd_set rfds_save;
int fdMax 0;

FD_ZERO(&rfds_save);
FD_SET(fdA, &rfds_save);
fdMax std::max(fdMaxfdA);
FD_SET(fdB, &rfds_save);
fdMax std::max(fdMaxfdB);
...
while ( 
someCondition )
{
  
rfds rfds_save;

  if (
select(fdMax 1, &rfds00, &timeout) > 0)
  {
    if (
FD_ISSET(fdA, &rfds))
      
// activity detected for client A

    
else if (FD_ISSET(fdB, &rfds))
      
// activity detected for client B

    
else
      
// disregard
  
}

P.S. If a client disconnects, you will need to update 'rfds_save'.
 
Old 01-16-2008, 11:58 AM   #3
bryanhdl
LQ Newbie
 
Registered: Jan 2008
Posts: 5

Original Poster
Rep: Reputation: 0
Fd_isset

Thank you for your suggestion. I did the same thing like your code for first connection with client A. But when A disconnects, I did not update the rfds_save, so the second connection with client B, I still receive activity on fdA.
To update the rfds_save, do I have to update fdMax?
FD_CLR(fdA, &rfds_save);

Thanks again,
Bryan
 
Old 01-16-2008, 01:57 PM   #4
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Quote:
Originally Posted by bryanhdl View Post
To update the rfds_save, do I have to update fdMax?
You're welcome. I don't know for sure if it is absolutely required to update fdMax, however I always did.

Long ago (4+ years ago) I wrote a multi-threaded application in C++ which enabled the server to handle many client connections. I had one thread whose sole purpose was to accept connections, and maintain this list of connected clients, and to make this information (e.g. fd_set, maxFD, and client objects) available.

Another thread, which performed the select(), and subsequent read() operations for a particular client object, was responsible for managing received messages, and notifying the first thread when a client disconnected.

Anyhow, let me know if you have further questions.
 
Old 01-16-2008, 02:30 PM   #5
bryanhdl
LQ Newbie
 
Registered: Jan 2008
Posts: 5

Original Poster
Rep: Reputation: 0
Fd_isset

Hi,

On my server-like program, there is one thread for select(), FD_ISSET() and recv() in a loop of two steam sockets, the other thread handles conection call control.

The rfds_save is never changed which always has fdA and fdB.
My problem is when there are two connections, FD_ISSET() in receive thread detects only fdA and does not detect fdB.

Do you know how to assign fdA or fdB to accept different connections?

Thank you.
 
Old 01-16-2008, 03:34 PM   #6
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
I'm sorry, I don't quite understand the question.

A server has an open port, which is managed by a socket descriptor. It uses select() to determine if there is activity on that socket (i.e. if a client is connecting). If there is activity on the socket, then the server accepts the client connection. Here's the code for the "listener" thread:
PHP Code:
    //  Listener thread loop
    //
    
while (!stop_requested())
    {
      
fd_set         listen_set;
      
struct timeval timeout = {1,0};

      
FD_ZERO(&listen_set);
      
FD_SET(m_listen_socket, &listen_set);


      
//  Determine if anyone is attempting to connect to the listen socket.
      //  If not, then continue; else accept the connection.
      //
      
if (select(m_listen_socket+1, &listen_setNULLNULL, &timeout) == 0)
        continue;

      if (!(
FD_ISSET(m_listen_socket, &listen_set)))
        continue;


      
//   Accept connection (loop back on error)
      //
      
int sock_len sizeof(sock);
      
int fd       = -1;

      if ((
fd accept(m_listen_socket, (struct sockaddr *) &sock, &sock_len)) < 0)
      {
        if (
errno != EWOULDBLOCK)
        {
          const 
int err errno;
          
dbg[*this] << "Accept Failed (" << err << ")." << el;
        }
        continue;
      }

      
dbg[*this] << "Connection Accepted, client fd = " << fd << el;

      
accept_connection(fd);
    } 
The accept_connection() method creates a new client object with the unique file-descriptor 'fd' for that client. The next client that connects would have a different file-descriptor.

The receiver portion of the server, which is another thread, uses select() that monitors each of the file-descriptors assigned to connected clients. When select() reports activity, the returned fd_set is passed to the object serving as the listener-thread; this thread then reports which client sent data. Only then does the receiver thread get a handle to the client object. Here's how I did that:

PHP Code:
  //  Receiver thread loop
  
while (!stop_requested())
  {
    
//   Setup file descriptor list for select call
    //
    
struct timeval timeout = {10};  // one second
    
fd_set         read_set;
    const 
uint     nfds m_debug_listen.get_client_set(read_set);


    
//   Check if there is any data traffic to be read on any of the
    //   file descriptors.
    //
    
if (select(nfds+1, &read_setNULLNULL, &timeout) == 0)
    {
      
dbg[*this] << "select timed out" << el;
      continue;
    }


    
dbg[*this] << "Detected Activity" << el;


    
//   Activity detected... find out if its from one of the clients
    //   we are monitoring.  Record any clients that send us bad data or
    //   that disconnect.
    //
    
TDD_debug_client *client m_debug_listen.get_debug_client(read_set);

    if (
client == NULL)
    {
      
dbg[*this] << "Client object could not be found" << el;
      continue;
    }

    
// Now that the client handle is available, attempt to read data
    // from it.  Remove the client if it has disconnected.
    // ...
  

I'm certain that there are other ways to model the server application, so please don't feel your design is deficient if it differs from mine. I was very new (and still am) to socket programming when I wrote this code long ago.

Last edited by dwhitney67; 01-16-2008 at 03:38 PM. Reason: Grammar and spelling corrections.
 
Old 01-16-2008, 05:25 PM   #7
bryanhdl
LQ Newbie
 
Registered: Jan 2008
Posts: 5

Original Poster
Rep: Reputation: 0
Fd_isset

Thank you for the code. Do you call FD_SET(fd, &listen_set) in accept_connection(fd)?
 
Old 01-16-2008, 06:08 PM   #8
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
No I do not. However, that is not to say you can't.

My accept_connection() method merely creates a new client object, using the socket descriptor for that client.

I implemented a separate method in my 'listener' thread to perform the FD_SET (looping through my list of clients). When done, this method returns both the fd_set value and the max-FD (or 'nfds') to the caller (i.e. the thread waiting for activity from clients). These values are then used within the select().

Here's that code again from the receiver thread:
PHP Code:
fd_set read_set;
const 
uint nfds m_debug_listen.get_client_set(read_set);

//   Check if there is any data traffic to be read on any of the
//   file descriptors.
//
if (select(nfds+1, &read_setNULLNULL, &timeout) == 0)
... 
I suppose you could do the FD_SET in your "accept connection" method, but eventually you will still require to know the highest-numbered socket descriptor for use in the select() call.
 
Old 01-18-2008, 01:02 AM   #9
bryanhdl
LQ Newbie
 
Registered: Jan 2008
Posts: 5

Original Poster
Rep: Reputation: 0
UDP Socket

Is the listener thread applied to udp sockets?

Thanks.
 
Old 01-18-2008, 02:33 AM   #10
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
In the code I wrote, I was using TCP.
 
Old 01-18-2008, 02:45 AM   #11
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,533

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Btw, your little exercise piqued my interest in revamping my skills with socket programming. Below are the listings of my Listener and Receiver threads, followed by the Client Manager -- all written in C++ and relying on the Boost libraries. This was hastily thrown together yesterday, so it may not be the best design. In fact, it probably would be nice to have a separate Receiver thread for each client, rather than one to service them all.

Listener.cpp:
PHP Code:
#include "Listener.h"
#include "Client.h"
#include "ClientMgr.h"

#include <netinet/in.h>
#include <cstring>        // for memset()
#include <cerrno>         // for errno
#include <iostream>       // for cerr, endl


// Constructor
// (I) port       - port number to listen for client connections.
// (I) maxClients - max number of clients that may connect.
// (I) clientMgr  - object for managing list of connected clients
//
Listener::Listener( const int port, const int maxClientsClientMgr &clientMgr )
  : 
m_portport ),
    
m_maxClientsmaxClients ),
    
m_clientMgrclientMgr )
{
  
// do nothing here
}


// Destructor
//
Listener::~Listener()
{
  
// do nothing here
}


// Thread main loop
//
void
Listener
::operator()( void )
{
  
struct sockaddr_in sock;

  
initSocketsock );

  while ( 
true )
  {
    
fd_set listenSet;
    
struct timeval timeout = { 1};

    
FD_ZERO( &listenSet );
    
FD_SETm_listenSocket, &listenSet );

    if ( 
selectm_listenSocket 1, &listenSet00, &timeout ) == )
      continue;

    if ( !
FD_ISSETm_listenSocket, &listenSet ) )
      continue;

    
socklen_t sockLen sizeof sock;
    
int clientSD = -1;

    if ( (
clientSD acceptm_listenSocket, (struct sockaddr *) &sock, &sockLen )) < )
    {
      if ( 
errno != EWOULDBLOCK )
      {
        
std::cerr << "Listener: Accept Failed (" << errno << ")." << std::endl;
      }
      continue;
    }

    
std::cout << "Listener: Adding client with sd = " << clientSD << std::endl;
    
m_clientMgr.acceptConnectionclientSD );
  }
}


// Initialize socket.
// (I) sock - Socket Address Interface
//
void
Listener
::initSocketstruct sockaddr_in &sock )
{
  
memset( &sock0sizeof sock );

  
sock.sin_family      AF_INET;
  
sock.sin_addr.s_addr htonlINADDR_ANY );
  
sock.sin_port        htonsm_port );

  
m_listenSocket socketsock.sin_familySOCK_STREAM);

  
bindm_listenSocket, (struct sockaddr *) &socksizeof sock );

  
listenm_listenSocketm_maxClients );


Receiver.cpp:
PHP Code:
#include "Receiver.h"
#include "Client.h"
#include "ClientMgr.h"

#include <netinet/in.h>
#include <sys/time.h>
#include <iostream>


// Constructor
// (I) clientMgr  - object for managing list of connected clients
//
Receiver::ReceiverClientMgr &clientMgr )
  : 
m_clientMgrclientMgr )
{
}


// Destructor
//
Receiver::~Receiver()
{
}


// Thread main loop
//
void
Receiver
::operator()( void )
{
  while ( 
true )
  {
    
struct timeval timeout = { 1};
    
fd_set         readSet;
    const 
int      nfds m_clientMgr.getClientSetreadSet );

    if ( 
selectnfds 1, &readSet00, &timeout ) == )
    {
      
// Timeout occurred
      
continue;
    }

    
// Detected activity
    
Client *client m_clientMgr.getClientreadSet );

    if ( 
client == )
    {
      
// could not find client object... error?
      
continue;
    }

    
bool newMsgAvailable true;

    while ( 
newMsgAvailable )
    {
      
char   msg256 ] = { };
      
size_t msgSize client->parseNextMsgmsgsizeof msg );

      switch ( 
msgSize )
      {
        case 
Client::NO_NEW_MSG:
            
newMsgAvailable false;
            break;

        case 
Client::MSG_TOO_LARGE:
        case 
Client::CLIENT_DISCONNECTED:
            
std::cout << "client disconnected" << std::endl;
            
m_clientMgr.removeClientclient );
            
newMsgAvailable false;
            break;

        default:
            
std::cout << "Message Received: " << msg;
            break;
      }
    }
  }


ClientMgr.cpp:
PHP Code:
#include "ClientMgr.h"
#include "Client.h"

#include <algorithm>   // for std::max()


// Constructor
// (I) port       - port number to listen for client connections.
// (I) maxClients - max number of clients that may connect.
//
ClientMgr::ClientMgr()
{
  
// do nothing here
}


// Destructor
//
ClientMgr::~ClientMgr()
{
  
// do nothing here
}


// Returns socket descriptor client set for clients that are connected
// (O) clientSet - socket descriptor set that is returned
//
// Returns the highest numbered socket descriptor of the connected clients.
//
int
ClientMgr
::getClientSetfd_set &clientSet )
{
  
boost::mutex::scoped_lock lockm_mutex );

  
FD_ZERO( &clientSet );

  
int nfds 0;

  for ( 
Iterator it m_clientList.begin(); it != m_clientList.end(); ++it )
  {
    
FD_SETit->first, &clientSet );

    
nfds std::maxnfdsit->first );
  }

  return 
nfds;
}


// Returns handle to client object
// (I) activeClient - socket descriptor set returned by select()
//
Client *
ClientMgr::getClient( const fd_set &activeClient )
{
  
boost::mutex::scoped_lock lockm_mutex );

  
Client *client 0;

  for ( 
Iterator it m_clientList.begin(); it != m_clientList.end(); ++it )
  {
    if ( 
FD_ISSETit->first, &activeClient ) )
    {
      
client it->second;
      break;
    }
  }

  return 
client;
}


// Remove a disconnected client from the list of clients
// (I) client - the client object
//
void
ClientMgr
::removeClientClient *client )
{
  
boost::mutex::scoped_lock lockm_mutex );

  
Iterator it m_clientList.begin();

  for ( ; 
it != m_clientList.end(); ++it )
  {
    if ( 
it->second == client )
    {
      
closeit->first );
      
delete it->second;
      
m_clientList.eraseit );
      
client 0;
      break;
    }
  }
}


// Creates a new client object.
// (I) sd - socket descriptor assigned to connected client.
//
void
ClientMgr
::acceptConnection( const int sd )
{
  
boost::mutex::scoped_lock lockm_mutex );

  if ( 
m_clientList.findsd ) == m_clientList.end() )
  {
    
m_clientListsd ] = new Clientsd );
  }


Last edited by dwhitney67; 01-18-2008 at 03:17 AM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Get the absolute path of file using file descriptor. appas Programming 7 01-19-2012 12:47 PM
File Descriptor 63 kshkid Programming 3 03-02-2006 12:41 PM
Change File Descriptor ilnli Linux - General 1 05-15-2005 06:43 PM
apt-file returns nothing; 'bad file descriptor' overbored Debian 3 10-03-2004 10:13 PM
File descriptor lido Linux - Newbie 5 07-17-2003 12:58 PM


All times are GMT -5. The time now is 12:46 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration