Skip to main content
GameDev.net gamedev.net
🔒 Locked

select( ) correct usage ?

Started by White Scorpion Jun 29, 2004 at 9:18 PM 41 replies 5.4k views
Original Post
White Scorpion
White Scorpion
Hi everyone ! I want to know if someone could explain me how to use the function select( ) ( in winsock ) because I have read a tutorial and I really don't understand how to use it. I have tried this code that I wrote and my ErrorMessage( ) function outputs "Invalid Argument" Here's the code:
 
void Server::MainLoop( ) 
{ 
   for( ;; ) 
   { 
      if( select( NULL, &Read, NULL, NULL, NULL ) == SOCKET_ERROR ) 
      { 
         ErrorMessage( WSAGetLastError( ) ) ; 
         exit( 0 ) ; 
      } 
      cout << "Ok!\n" ; 
   } 
} 
Big thanks to everyone. PS: I have tried to add a timeval in the function too ( as suggested by someone on another forum ) but it didn't work neither.
SiCrane
SiCrane
How are you filling the read fd_set?
White Scorpion
White Scorpion
Quote:
Original post by SiCrane
How are you filling the read fd_set?
I know I'm really newb with Network Programming but... how do you fill it ?
C-Junkie
C-Junkie
Take a look at the man page I linked to and read the two sentences about FD_ZERO and FD_SET ?
hplus0603
hplus0603
Windows has Excellent Man Pages for all the programming APIs, online.
enum Bool { True, False, FileNotFound };
Eric Lengyel
Eric Lengyel
Under Windows, it's better to use the WSAEventSelect() function in conjunction with WSAWaitForMultipleEvents() instead of the UNIX select() function. The only reason select() is supported is to allow UNIX apps to be ported to Windows more easily. With WSAEventSelect(), you listen on standard Windows event handles instead of UNIX file descriptors.

-- Eric Lengyel
Telastyn
Telastyn
Beej's network programming guide is the best I've seen for explaining select. [and generic network coding stuff]



The first parameter of select needs to be the maximum file descriptor in the set plus 1; not NULL.



and Read needs to be a fd_set. [perhaps elsewhere in your code?]



Anon Mike
Anon Mike
Winsock doesn't use the first parameter to select. If you're using Windows and still in the "I'm just trying to get things working at all" stage feel free to ignore it.

When you start caring about portability then go back and figure out the insane stupidity that is nfds.
-Mike
White Scorpion
White Scorpion
Quote:
Original post by Telastyn
Beej's network programming guide is the best I've seen for explaining select. [and generic network coding stuff]



The first parameter of select needs to be the maximum file descriptor in the set plus 1; not NULL.



and Read needs to be a fd_set. [perhaps elsewhere in your code?]
1st: That's the one I'm on.
2nd: MSDN says it's ignored.
3rd: It is.
Telastyn
Telastyn
Let's see then... perhaps it is trying to get the sockets from Read, and hitting something that's not a socket.



FD_ZERO(&Read);

FD_SET(socketfd,&Read);

[where socketfd is a socket you're checking on] should be before the select.



Ah, a quick check of msdn says that its version of select will return invalid if all socket sets are null, so you probably didn't do FD_ZERO [to initialize and allocate the Read set]



I posted a more complete example [though I use the first param, as I learned networking with BSD sockets] in another thread here in the networking forum.
White Scorpion
White Scorpion
Thank you. Now I have another error ( when I add your code ), it outputs: "Socket operation on nonsocket."
White Scorpion
White Scorpion
///////////////////////////////////////////////////////////// FILE :		SERVER . CPP							 //// AUTHOR :		T I L E X								 //// CREATED ON : MONDAY, JUNE 28TH 2004					 /////////////////////////////////////////////////////////////#include "Server.h"using std::cout ;using std::cerr ;using std::endl ;// static variablesSOCKET Server::s ;sockaddr_in Server::sin ;fd_set Server::Read ;timeval Server::Delay ;void Server::Init( ){	WSADATA wsaData ;	if( WSAStartup( MAKEWORD( 2,2 ), &wsaData ) != 0 )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}	ZeroMemory( &Delay, sizeof( timeval ) ) ;	FD_ZERO( &Read ) ;	FD_SET( s, &Read ) ;}void Server::CreateSocket( ){	s = socket( AF_INET, SOCK_STREAM, 0 ) ;	if( s == SOCKET_ERROR )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}	if( bind( s, ( struct sockaddr* ) &sin, sizeof( sockaddr ) ) == -1 )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}}void Server::DefineSockAddr( ){	ZeroMemory( &sin, sizeof( sockaddr ) ) ;	sin.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" ) ;	sin.sin_family = AF_INET ;	sin.sin_port = htons( DEFAULT_SERVER_PORT ) ;}	void Server::Cleanup( ){	closesocket( s ) ;	WSACleanup( ) ;}void Server::MainLoop( ){	for( ;; )	{		if( select( NULL, &Read, NULL, NULL, &Delay ) == SOCKET_ERROR )		{			ErrorMessage( WSAGetLastError( ) ) ;			exit( 0 ) ;		}		cout << "Ok!\n" ;	}}void Server::ErrorMessage( int error ){	switch( error )	{	case WSAEINTR: cerr << "\nInterrupted function call" << endl ; break ;	case WSAEACCES: cerr << "\nPermission denied" << endl ; break ;	case WSAEFAULT: cerr << "\nBad address" << endl ; break ;	case WSAEINVAL: cerr << "\nInvalid argument" << endl ; break ;	case WSAEMFILE: cerr << "\nToo many open files" << endl ; break ;	case WSAEWOULDBLOCK: cerr << "\nResource temporary unavailable" << endl ; break ;	case WSAEINPROGRESS: cerr << "\nOperation now in progress" << endl ; break ;	case WSAEALREADY: cerr << "\nOperation already in progress" << endl ; break ;	case WSAENOTSOCK: cerr << "\nSocket operation on nonsocket" << endl ; break ;	case WSAEDESTADDRREQ: cerr << "\nDestination address required" << endl ; break ;	case WSAEMSGSIZE: cerr << "\nMessage too long" << endl ; break ;	case WSAEPROTOTYPE: cerr << "\nProtocol wrong type for socket" << endl ; break ;	case WSAENOPROTOOPT: cerr << "\nBad protocol option" << endl ; break ;	case WSAEPROTONOSUPPORT: cerr << "\nProtocol not supported" << endl ; break ;	case WSAESOCKTNOSUPPORT: cerr << "\nSocket type not supported" << endl ; break ;	case WSAEOPNOTSUPP: cerr << "\nOperation not supported" << endl ; break ;	case WSAEPFNOSUPPORT: cerr << "\nProtocol family not supported" << endl ; break ;	case WSAEAFNOSUPPORT: cerr << "\nAddress family not supported" << endl ; break ;	case WSAEADDRINUSE: cerr << "Address already used" << endl ; break ;	case WSAEADDRNOTAVAIL: cerr << "\nCannot assign requested address" << endl ; break ;	case WSAENETDOWN: cerr << "\nNetwork is down" << endl ; break ;	case WSAENETUNREACH: cerr << "\nNetwork is unreachable" << endl ; break ;	case WSAENETRESET: cerr << "\nNetwork dropped connection on reset" << endl ; break ;	case WSAECONNABORTED: cerr << "\nSoftware caused connection abort" << endl ; break ;	case WSAECONNRESET: cerr << "\nConnection reset by peer" << endl ; break ;	case WSAENOBUFS: cerr << "\nNo buffer space available" << endl ; break ;	case WSAEISCONN: cerr << "\nSocket is already connected" << endl ; break ;	case WSAENOTCONN: cerr << "\nSocket not connected" << endl ; break ;	case WSAESHUTDOWN: cerr << "\nCannot send after socket shutdown" << endl ; break ;	case WSAETIMEDOUT: cerr << "\nConnection timed out" << endl ; break ;	case WSAECONNREFUSED: cerr << "\nConnection refused" << endl ; break ;	case WSAEHOSTDOWN: cerr << "\nHost is down" << endl ; break ;	case WSAEHOSTUNREACH: cerr << "\nNo route to host" << endl ; break ;	case WSAEPROCLIM: cerr << "\nToo many processes" << endl ; break ;	case WSASYSNOTREADY: cerr << "\nNetwork subsystem is unavailable" << endl ; break ;	case WSAVERNOTSUPPORTED: cerr << "\nWinsock.dll version out of range" << endl ; break ;	case WSANOTINITIALISED: cerr << "\nSuccessful WSAStartup not yet performed" << endl ; break ;	case WSAEDISCON: cerr << "\nGraceful shutdown in progress" << endl ; break ;	case WSATYPE_NOT_FOUND: cerr << "\nClass type not found" << endl ; break ;	case WSAHOST_NOT_FOUND: cerr << "\nHost not found" << endl ; break ;	case WSATRY_AGAIN: cerr << "\nNonauthoritative host not found" << endl ; break ;	case WSANO_RECOVERY: cerr << "\nThis is a nonrecoverable error" << endl ; break ;	case WSANO_DATA: cerr << "\nValid name, no data record of requested type" << endl ; break ;	default : cerr << "\nUnknown error" << endl ; break ;	}}


///////////////////////////////////////////////////////////// FILE :		SERVER . H								 //// AUTHOR :		T I L E X								 //// CREATED ON : MONDAY, JUNE 28TH 2004					 /////////////////////////////////////////////////////////////#include <winsock2.h>#include <cstdio>#include <iostream>#pragma comment ( lib, "wsock32.lib" )#ifndef __SERVER_H__#define __SERVER_H__#define DEFAULT_SERVER_PORT 3490class Server{public:	static void Init( ) ;	static void CreateSocket( ) ;	static void DefineSockAddr( ) ;	static void MainLoop( ) ;	static void ErrorMessage( int ) ;	static void Cleanup( ) ;private:	// Server	static SOCKET s ;	static sockaddr_in sin ;	// Connected computers	static fd_set Read ;	static timeval Delay ;} ;#endif


Ok I know this code damn sucks but I'm really a newb with OOP and Network Programming.
C-Junkie
C-Junkie
You need to create the socket before putting it into the fd_set
Telastyn
Telastyn
Okay, FD_SET(s,&Read) should probably be done after CreateSocket. Essentially it's an extra step once you've setup the socket by connecting or by setting it to listen.



Right now it looks like you want to set it to listen. After the bind() call, you should probably put a listen call [msdn, "winsock listen()"]. This will tell the bound socket to listen for new connections. I might be wrong, but FD_SET might require the socket to be connected or listening before it will work.



So... lets see if I can collect my thoughts...



Add listen after bind in CreateSocket.

Move FD_SET(s,&Read); from Init, to CreateSocket, after the listen call just added.

And select() modifies the fd_set, so you'll need to change the main loop to:



fd_set buf;{    for(;;)    {        buf=Read;        if (select(NULL, &buf, NULL, NULL, &Delay) == SOCKET_ERROR){        // ...    //...//...}




Since if you don't copy the fd_set each time, it can get overwritten, and suddenly you're not checking on sockets anymore...



To try and explain better... say you have the sockets a,b, and c inside Read. When select is called it checks to see if a,b,and c [the sockets in the fd_set sent to select] have data in their buffers. If not, it keeps checking until the Delay timeout. Say a and c have data... Select modifies the set so that it only contains a and c [but not b]. If you call select again, it won't even check for b, since b is no longer in Read.



And after that there's still a little bit to do to accept connections and read data.



White Scorpion
White Scorpion
Ok, I have done it and it's working now ( thanks to you guys ). But it never shows the message when someone connects lol. Maybe I didn't understand the purpose of select... it checks if there is an incoming connection and then checks its state. But what if you want only 2 or 3 connections maximum ? Well here's the code and I want to know why when I connect to the server with my client program, I don't receive the message on the server ?

CLIENT PROGRAM:
void Client::Init( ){	WSADATA wsaData ;	if( WSAStartup( MAKEWORD( 2,2 ), &wsaData ) != 0 )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}}void Client::DefineSockAddr( unsigned int port, const char* addr ){	if( IS_PORT_VALID( port ) && addr )	{		sin.sin_addr.S_un.S_addr = inet_addr( addr ) ;		sin.sin_family = AF_INET ;		sin.sin_port = htons( DEFAULT_SERVER_PORT ) ;	}	else	{		cerr << "\nPort number or IP address is incorrect." << endl ;		exit( 0 ) ;	}}void Client::CreateSocket( ){	s = socket( AF_INET, SOCK_STREAM, 0 ) ;	if( s == SOCKET_ERROR )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}}void Client::ConnectToServer( ){	if( connect( s, ( struct sockaddr* ) &sin, sizeof( sockaddr ) ) == -1 )	{		ErrorMessage( WSAGetLastError( ) ) ;		Cleanup( ) ;		exit( 0 ) ;	}}void Client::Cleanup( ){	closesocket( s ) ;	WSACleanup( ) ;}


SERVER PROGRAM:
void Server::Init( ){	WSADATA wsaData ;	if( WSAStartup( MAKEWORD( 2,2 ), &wsaData ) != 0 )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}	ZeroMemory( &Delay, sizeof( timeval ) ) ;}void Server::DefineSockAddr( ){	ZeroMemory( &sin, sizeof( sockaddr ) ) ;	sin.sin_addr.S_un.S_addr = inet_addr( "127.0.0.1" ) ;	sin.sin_family = AF_INET ;	sin.sin_port = htons( DEFAULT_SERVER_PORT ) ;}	void Server::CreateSocket( ){	if( ( s = socket( AF_INET, SOCK_STREAM, 0 ) ) == SOCKET_ERROR )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}	if( bind( s, ( struct sockaddr* ) &sin, sizeof( sockaddr ) ) == -1 )	{		ErrorMessage( WSAGetLastError( ) ) ;		exit( 0 ) ;	}	if( listen( s, 20 ) == -1 )	{		ErrorMessage( WSAGetLastError( ) ) ;		Cleanup( ) ;		exit( 0 ) ;	}	FD_ZERO( &Master ) ;	FD_SET( s, &Master ) ;}void Server::MainLoop( ){	fd_set Buffer ;	for( ;; )	{		Buffer = Master ;		if( select( NULL, &Buffer, NULL, NULL, &Delay ) == SOCKET_ERROR )		{			ErrorMessage( WSAGetLastError( ) ) ;			Cleanup( ) ;			exit( 0 ) ;		}		if( Buffer.fd_count > 1 )		{			cout << "ca marche!" ;			exit( 0 ) ;		}	}}void Server::Cleanup( ){	closesocket( s ) ;	WSACleanup( ) ;}

Also I'd want to know how can I send a message to a client that just connected, not every client, only the new one. Like: "Hello %name of the computer% ! Welcome to my server." or anything like that. Thanks for everyone's patience, I know this post starts to be really long but I really want to learn.
Telastyn
Telastyn
you need to check if the listening socket is one of the ones select says is ready, then accept() it.



from my game server code:



// code to setup the listening socket and the fd_sets// code to malloc buf, and other stuff// actual game setup code//snipwhile(1){select(maxfd,&selected,NULL,NULL,NULL);for (testfd=0;testfd<maxfd;testfd++){    if (FD_ISSET(testfd,&selected)){        if (testfd==listenfd){            if (confd=accept(listenfd,(struct sockaddr *)&insock,&sinsize))){                // snip                // Add confd [a new socket] to master fd_set                // Add player to playerlist                // Send new player message to everyone            }        }else{            // if testfd==0 then keyboard input in *nix machines            // else            rtn=recv(testfd,buf,buflen,0);            if (rtn<1){                // then a player dropped, handle it.            }else{                // buf now has data, do stuff with it.            }        }    }}}// snip




so listenfd is my listening socket (s for you). Select for me will wait forever, until it gets a new connection, or until clients give orders. Then I cycle through my file descriptors [naively actually, as some of testfd will never work] and check if each is set. If the listening descriptor is set, I need to accept the connection. If you only want 2-3 people connecting, just keep count, then either don't accept their connection, or accept and then close() [saying the server is full or something]. If the testfd isn't a listening socket, then it's an already connected/accepted socket, so you need to recieve data there.



To send a message to the client,right after the accept call, call send(confd,"message",strlen("message"),0);



And the message never shows, because you never had 2 set sockets. [it should be >0 or >=1...]
White Scorpion
White Scorpion
maxfd is an integer that represents the maximum number of connections right ?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.