This is the mail archive of the cygwin@sourceware.cygnus.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: select()


Thanks to all those who responded to my select() problems. I have found my 
solution. Maybe this is what ails some of you with similar problems, 
maybe your problem is something different.

To recap, my receiving process was hanging on select() at the start even 
though there was data. When a key (even shift) was pressed in the shell 
window it would unhang and consume the already waiting data. The other 
problem was that all calls to select() after that returned with a value 
of 1 even when there was no data on the socket.

It turns out that at run-time or maybe at link time, the select() that is 
used is the unix kind that that uses the bit mask to specify what file 
descriptors are being watched. However, the include files I was using use 
some kind of Windows version that uses a structure with an array in it. 

So, when the bitmask version tried to use the structure I was passing it, 
it looked like a bitmask that wanted it to check stdin all the time. This 
explains the bad behavior. The solution is to simple use the right 
include files with the bitmask macros (FD_SET, FD_ZERO, etc.). That way 
what I build and pass really is the bitmask and it works just fine.

These are those simple little platform issues that really bite you good.

I hope this helps someone else avoid the watsed hours this cost me.

Attached are my simple demo programs that do it right now. These may be 
useful to someone looking for a starting point or to see what they do for 
includes that works.

Todd
// ---------------------------------------- begin file SimpleServer.C

// compile command: gcc -o SimpleServer SimpleServer.C -lstdc++
// on Solaris you may also need: -lsocket -lnsl

// using CenterLine C++: 
//   /vol/CenterLine-2.1.1/bin/CC -o SimpleServer SimpleServer.C -lsocket

// test instructions:
//
// 1. compile both this file and the SimpleClient.C
// 2. run SimpleServer first in a bash or DOS window
// 3. run SimpleClient in another bash or DOS window
// 4. they connect and SimpleClient starts sending messages/sleeping
//
// 5. SimpleServer hangs in a select even though data is available
//    until you press any key in its window (even shift or ctrl 
//    or alt will do)...see also notes at the close(0) call below
//
// 6. once "unhung," SimpleServer always gets a return value of
//    one from select() even when there is no data, thus it always
//    proceeds to the read call where it blocks
//
// BOTTOM LINE: I need select() to block because I want to use 
// the timeout version below in order to wait for data but 
// "come up for air" every so many seconds to do some other 
// stuff...if select() always returns I cannot get my waiting 
// period as desired.
//
// SOLUTION: I was using the wrong include files. The ones I 
// used use a structure with an array for choosing what file 
// descriptors to select. BUT, the runtime select() was really 
// the traditionaly bitmask version. Using the "GOOD" includes 
// below instead of the "BAD" ones fixed it.

#include <stdlib.h>
#include <stdio.h>
#include <fstream.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#ifdef WINNT

// ***************** here's where the problem was *************
// BAD includes
//#define __attribute__(x)
//#include <Windows32/Base.h>
//#include <Windows32/Sockets.h>

// GOOD includes
#include <sys/socket.h>
#include <cygwin32/in.h>

// with the "GOOD" includes I get link 
// errors without this
extern "C" {
  unsigned long  htonl(unsigned long  h);
  unsigned short htons(unsigned short h);
}

#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

// constants
#define PORT 2002

// globals for this file
static int  fd = -1;


int non_block_select(void)
{
  struct fd_set fdbits;
  FD_ZERO(&fdbits);
  FD_SET(fd, &fdbits);

  struct timeval interval; interval.tv_sec  = 0; interval.tv_usec = 0;
   
  int fds_ready = select(fd+1, &fdbits, (fd_set*) NULL, (fd_set*) NULL, &interval);
  cerr << "NB-----------------------------------------------------select() returned " << fds_ready << endl;

  return fds_ready;
}


int timeout_select(int seconds)
{
  struct fd_set fdbits;
  FD_ZERO(&fdbits);
  FD_SET(fd, &fdbits);

  struct timeval interval; interval.tv_sec  = seconds; interval.tv_usec = 0;
   
  int fds_ready = select(fd+1, &fdbits, (fd_set*) NULL, (fd_set*) NULL, &interval);
  cerr << "TO-----------------------------------------------------select() returned " << fds_ready << endl;

  return fds_ready;
}

int block_select(void)
{
  struct fd_set fdbits;
  FD_ZERO(&fdbits);
  FD_SET(fd, &fdbits);
   
  int fds_ready = select(fd+1, &fdbits, (fd_set*) NULL, (fd_set*) NULL, (struct timeval *) NULL);
  cerr << "B------------------------------------------------------select() returned " << fds_ready << endl;

  return fds_ready;
}


void init(void)
{

  struct sockaddr_in addr;
  memset((char *) &addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(PORT);
  cerr << "connecting via port " << PORT << endl;

  int sock = socket(AF_INET, SOCK_STREAM, 0);		// create socket
  if (sock<0) { perror("creating socket"); exit(-1); }

  if (bind(sock, (struct sockaddr*) &addr, sizeof(addr))) { perror ("binding socket"); close(sock); exit(-1); }

  if (listen(sock, 1)) { perror ("listening to socket"); close(sock); exit(-1); }

  struct sockaddr remote;
  int remote_len = sizeof(remote);
  if ((fd = accept(sock, &remote, &remote_len)) == -1) { perror("accepting socket"); close(sock); exit(-1); }
  cerr << "server side connected" << endl;
  close(sock);

  cerr << "fd=" << fd << endl;//zzz
  cerr << "fd modes=" << (void*) fcntl(fd, F_GETFL) << endl;//zzz
}


int main(int, char **)
{
  char buff[1000];
  init(); // open the socket
  while(1)
  {
    //cerr << "calling timeout_select(5)" << endl;
    //timeout_select(5); 
    //cout << "back from timeout" << endl;

    cout << "calling block_select()" << endl;
    block_select();

    cerr << "back from \"select()\" and going on to read" << endl;
    int bytesread = read(fd, buff, (size_t) 999);
    if (bytesread == 0) break;
    buff[bytesread] = 0;
    cerr << "read this #" << buff << "#" << endl;
  }
  cerr << "got eof" << endl;
}

// ---------------------------------------- end file SimpleServer.C
// ---------------------------------------- begin file SimpleClient.C

// compile command: gcc -o SimpleClient SimpleClient.C -lstdc++
// on Solaris you may also need: -lsocket -lnsl

// see test instructions in SimpleSerevr.C


#include <stdlib.h>
#include <stdio.h>
#include <fstream.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#ifdef WINNT

// ***************** here's where the problem was *************
// BAD includes
//#define __attribute__(x)
//#include <Windows32/Base.h>
//#include <Windows32/Sockets.h>

// GOOD includes
#include <sys/socket.h>
#include <cygwin32/in.h>

#else
#include <sys/socket.h>
#include <netinet/in.h>
#endif

#include <arpa/inet.h>

// constants
#define PORT 2002

// globals for this file
static int fd;


void init(void)
{
  struct sockaddr_in addr;
  memset((char *) &addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  addr.sin_port = htons(PORT);
  cerr << "connecting via port " << PORT << endl;

  fd = socket(AF_INET, SOCK_STREAM, 0);		// create socket
  if (fd<0) { perror("creating socket"); exit(-1); }

  if (connect(fd, (struct sockaddr*) &addr, sizeof(addr))) { perror ("socketPrep - connecting socket"); exit(-1); }
  cerr << "socketPrep - client side connected" << endl;
}


int main (int argc, char **argv)
{
  char buff[1000];
  buff[0] = 'A'; 
  buff[1] = 0;
  init(); // open the socket
  cerr << "sending first message in 3 seconds" << endl; sleep(3);

  while(1)
  {
    int len = strlen(buff);
    write(fd, buff, len);
    cerr << "sent this #" << buff << "#" << endl;
    buff[len]   = 'x';
    buff[len+1] = 0;
    cerr << "sleeping" << endl;
    sleep(10);
  }
}

// ---------------------------------------- end file SimpleClient.C

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]