///////////////////////////////////////////////////////////////////////////// // Rev 11/12/2003 // // // TCP.C // ----- // // // Sylvain MARECHAL - sylvain.marechal1@libertysurf.fr ///////////////////////////////////////////////////////////////////////////// /* ** Encapsulation des sockets bloquantes pour WIN32 et UNIX */ #include "winunix.h" #include "tcpudp.h" #include "tcp.h" /*****************************************************/ /* Gestion des erreurs */ void TcpSetError( int err ) { TcpUdpSetError( err ); } int TcpGetError() { return TcpUdpGetError(); } /*****************************************************/ /*****************************************************/ /* Gestion du time out (-1:bloquant) */ void TcpSetTimeOut( int t ) { TcpUdpSetTimeOut(t); } int TcpGetTimeOut() { return TcpUdpGetTimeOut(); } /*****************************************************/ #ifdef _WIN32 static s_fCreateOverlappedSocket = FALSE; static SOCKET (*pfWSASocket) ( int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags ); void TcpCreateOverlappedSocket( BOOL fCreateOverlappedSocket ) { s_fCreateOverlappedSocket = fCreateOverlappedSocket; if( s_fCreateOverlappedSocket ) { /* Eviter les pb de link qd on veut pas des overlap et qu'on link avec wsock32.lib */ HINSTANCE hWs2 = LoadLibrary( "ws2_32.dll" ); pfWSASocket = ( SOCKET (*) (int,int,int,LPWSAPROTOCOL_INFO,GROUP, DWORD ) )GetProcAddress( hWs2, "WSASocketA" ); } } #endif /*****************************************************/ SOCKET TcpCreate() { #ifdef _WIN32 WSADATA stack_info ; WSAStartup(MAKEWORD(2,0), &stack_info) ; #endif TcpSetError( 0 ); /* 21-01-99 Gestion des sockets Overlapped */ #ifndef _WIN32 return TcpUdpCreate( SOCK_STREAM ); #else if( ! s_fCreateOverlappedSocket ) return TcpUdpCreate( SOCK_STREAM ); else { return pfWSASocket( PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED ); } #endif } /*****************************************************/ struct sockaddr TcpFormatAdress( char * host, u_short port ) { return TcpUdpFormatAdress( host, port ); } /*****************************************************/ int TcpBind( SOCKET hSocket, u_short portLocal ) { TcpSetError( 0 ); return TcpUdpBind( hSocket, portLocal ); } /**************************************************************/ BOOL TcpConnect( SOCKET hSocket, char * host, unsigned short server_port ) { struct sockaddr addr; u_long arg; int err; /* Exemple d'utilisation : : hSocket=TcpCreate(); TcpBind( hSocket, 0 ); if( ! TcpConnect( hSocket, "machine_distante", 12000 ) ) { //printf( "Ca va pas (%d)\n", TcpGetError() ); TcpDestroy( hSocket ); } */ TcpSetError( 0 ); /* Soit on fournit une adresse IP, soit on fournit un nom */ addr = TcpFormatAdress( host, server_port ); /* Mode bloquant */ arg = 0; err = ioctlsocket( hSocket, FIONBIO, &arg ); /* Connexion au host */ if( connect( hSocket, &addr, sizeof(addr) ) == SOCKET_ERROR ) { TcpSetError( ERRNO ); return FALSE; } return TRUE; } /**************************************************************/ int TcpAsynchConnect( SOCKET hSocket, char * host, unsigned short server_port ) { struct sockaddr addr; u_long arg; int err; int nState; TcpSetError( 0 ); /* Mode non bloquant */ arg = 1; err = ioctlsocket( hSocket, FIONBIO, &arg ); if( err == SOCKET_ERROR ) { //printf( "ioctlsocket() : Error n %d\n", ERRNO ); } /* Soit on fournit une adresse IP, soit on fournit un nom */ addr = TcpFormatAdress( host, server_port ); /* Connexion au host */ if( connect( hSocket, &addr, sizeof(addr) ) == SOCKET_ERROR ) { err = ERRNO; switch(err) { case WSANOTINITIALISED : //printf( "connect(): WSANOTINITIALISED\n" ); nState = CONNECT_ERROR; break; case WSAENETDOWN : //printf( "connect(): WSAENETDOWN\n" ); nState = CONNECT_ERROR; break; case WSAEADDRINUSE : //printf( "connect(): WSAEADDRINUSE\n" ); nState = CONNECT_ERROR; break; case WSAEINTR : //printf( "connect(): WSAEINTR\n" ); nState = CONNECT_ERROR; break; case WSAEINPROGRESS : //printf( "connect(): WSAEINPROGRESS\n" ); nState = CONNECT_ERROR; break; case WSAEALREADY : //printf( "connect(): WSAEALREADY\n" ); nState = CONNECTED; break; case WSAEADDRNOTAVAIL : //printf( "connect(): WSAEADDRNOTAVAIL\n" ); nState = CONNECT_ERROR; break; case WSAEAFNOSUPPORT : //printf( "connect(): WSAEAFNOSUPPORT\n" ); nState = CONNECT_ERROR; break; case WSAECONNREFUSED : //printf( "connect(): WSAECONNREFUSED\n" ); nState = CONNECT_ERROR; break; case WSAEFAULT : //printf( "connect(): WSAEFAULT\n" ); nState = CONNECT_ERROR; break; case WSAEINVAL : //printf( "connect(): WSAEINVAL\n" ); nState = CONNECT_ERROR; break; case WSAEISCONN : //printf( "connect(): WSAEISCONN\n" ); nState = CONNECTED; break; case WSAENETUNREACH : //printf( "connect(): WSAENETUNREACH\n" ); nState = CONNECT_ERROR; break; case WSAENOBUFS : //printf( "connect(): WSAENOBUFS\n" ); nState = CONNECT_ERROR; break; case WSAENOTSOCK : //printf( "connect(): WSAENOTSOCK\n" ); nState = CONNECT_ERROR; break; case WSAETIMEDOUT : //printf( "connect(): WSAETIMEDOUT\n" ); nState = CONNECT_ERROR; break; case WSAEWOULDBLOCK : //printf( "connect(): WSAEWOULDBLOCK\n" ); nState = CONNECT_IN_PROGRESS; break; case WSAEACCES : //printf( "connect(): WSAEACCES\n" ); nState = CONNECT_ERROR; break; default: //printf( "connect(): unknown error %d\n", err ); nState = CONNECT_ERROR; break; } } else nState = CONNECTED; /* On repasse en mode bloquant */ arg = 0; ioctlsocket( hSocket, FIONBIO, &arg ); return nState; } /************************************************************/ BOOL TcpListen( SOCKET hSocket ) { /* listen */ TcpSetError( 0 ); if ( listen( hSocket, 100) == SOCKET_ERROR ) { TcpSetError( ERRNO ); return FALSE; } return TRUE; } /************************************************************/ BOOL TcpGotConnection( SOCKET hSocketListenCon, int nTimeOut ) { fd_set mask; struct timeval tv; TcpSetError( 0 ); /* Si -1, on va chercher le timeout defini par TcpGetTimeOut() */ nTimeOut = (nTimeOut == -1) ? TcpGetTimeOut() : nTimeOut; /* Gestion du time out . Si -1, mode bloquant */ tv.tv_sec = nTimeOut / 1000; tv.tv_usec = nTimeOut % 1000; /* Acceptation d'une connexion */ FD_ZERO(&mask) ; FD_SET(hSocketListenCon, &mask); if (select( hSocketListenCon + 1, &mask, NULL, NULL, ( nTimeOut == -1 ? NULL : &tv) ) == SOCKET_ERROR) { TcpSetError( ERRNO ); return FALSE; } if (! FD_ISSET(hSocketListenCon, &mask)) { /* Pas de connexions */ TcpSetError( 0 ); return FALSE; } return TRUE; } /************************************************************/ SOCKET TcpAccept( SOCKET hSocketListenCon, int nTimeOut ) { SOCKET hSocketGetCon; struct sockaddr_in addr; int len; /* Exemple d'utilisation : : hSocket=TcpCreate(); TcpBind( hSocket, port_ecoute ); TcpListen( hSocket ); hNewSocket = TcpAccept( hSocket, -1 ); if( hNewSocket == SOCKET_ERROR && TcpGetError() ) { //printf( "Ca va pas (%d)\n", TcpGetError() ); TcpDestroy( hSocket ); } */ TcpSetError( 0 ); if( ! TcpGotConnection( hSocketListenCon, nTimeOut ) ) { /* Soit pas de connexion (time out expire), soit erreur du select() */ return SOCKET_ERROR; } len = sizeof( addr ); hSocketGetCon = accept(hSocketListenCon, (struct sockaddr *)&addr, &len); if( hSocketGetCon == INVALID_SOCKET ) { TcpSetError( ERRNO ); return SOCKET_ERROR; } return hSocketGetCon; } /*****************************************************/ int TcpSend( SOCKET hSocket, char * szBuffer, int cbBuffer ) { return TcpSendWithFlagOOB( hSocket, szBuffer, cbBuffer, 0 ); } /*****************************************************/ int TcpSendWithFlagOOB( SOCKET hSocket, char * szBuffer, int cbBuffer, int flagOOB ) { char szSendBuffer[TAILLE_BUFFER_SEND_TCP]; char * pszSend; int cbSend; BOOL fMalloc = FALSE; /* Reallocation de szSend si trop petit... On utilise pas des statics a cause du multithread Avec un peu de chance, si ( TAILLE_BUFFER_SEND > cbBuffer + TAILLE_ENTETE_UDP, on malloc jamais */ TcpSetError( 0 ); pszSend = szSendBuffer; if( cbBuffer + TAILLE_ENTETE_TCP > TAILLE_BUFFER_SEND_TCP ) { pszSend = ( char * )malloc( cbBuffer + TAILLE_ENTETE_TCP ); fMalloc = TRUE; } /* Creation entete */ pszSend[0] = (unsigned char)( cbBuffer >> 8 ); /* / 256 <=> poids fort */ pszSend[1] = (unsigned char)( cbBuffer & 0xff) ; /* poids faible */ /* Copie des donnees */ memcpy( pszSend + TAILLE_ENTETE_TCP, szBuffer, cbBuffer ); /* On envoie dans la nature */ if( (cbSend = send( hSocket, pszSend, cbBuffer + TAILLE_ENTETE_TCP, flagOOB )) == SOCKET_ERROR ) { TcpSetError( ERRNO ); if( fMalloc ) free( pszSend ); return SOCKET_ERROR; } if( fMalloc ) free( pszSend ); return cbSend - TAILLE_ENTETE_TCP; } /*****************************************************/ int TcpRecv( SOCKET hSocket, char * szBuffer, int cbMaxBuffer ) { /* reception bloquante par defaut (-1)*/ return TcpRecvWithFlag( hSocket, szBuffer, cbMaxBuffer, -1, 0 ); } /*****************************************************/ int TcpRecvWithFlag( SOCKET hSocket, char * szBuffer, int cbMaxBuffer, int nTimeOut, int flag ) { int cbRecv, cbLenBuf, cbLus; char szEntete[TAILLE_ENTETE_TCP]; /*struct*/ fd_set fd; struct timeval tv; TcpSetError( 0 ); /* Si -1, on va chercher le timeout defini par TcpGetTimeOut() */ nTimeOut = (nTimeOut == -1) ? TcpGetTimeOut() : nTimeOut; /* Gestion du time out . Si -1, mode bloquant */ tv.tv_sec = nTimeOut / 1000; tv.tv_usec = nTimeOut % 1000; FD_ZERO(&fd); FD_SET(hSocket, &fd); if(select( hSocket + 1, ( flag != 0 ? NULL : &fd ), NULL, ( flag != MSG_OOB ? NULL : &fd ), ( nTimeOut == -1 ? NULL : &tv) ) == SOCKET_ERROR ) { TcpSetError( ERRNO ); return SOCKET_ERROR; } /* Rien dans le buffer */ if (! FD_ISSET(hSocket, &fd)) { return 0; } /* Lecture de la longueur */ if( ( cbRecv = recv( hSocket, szEntete, TAILLE_ENTETE_TCP, flag )) == SOCKET_ERROR ) { TcpSetError( ERRNO ); return SOCKET_ERROR; } /* Il faut absolument avoir recu les 2 premiers octets ... */ if( cbRecv == 1 ) { int cbRecv2; if( ( cbRecv2 = recv( hSocket, szEntete + 1, TAILLE_ENTETE_TCP - 1, flag )) == SOCKET_ERROR ) { TcpSetError( ERRNO ); return SOCKET_ERROR; } cbRecv += cbRecv2; } /* Pas bon, pas bon ... */ if( cbRecv != TAILLE_ENTETE_TCP ) { if( ERRNO == WSAECONNRESET ) return 0; TcpSetError( ERREUR_ENTETE ); return SOCKET_ERROR; } /* Portabilite */ cbLenBuf = (unsigned short)( (( unsigned char)szEntete[0] << 8) + ( unsigned char)szEntete[1] ); /* On doit avoir un buffer suffisament grand */ if( cbLenBuf > cbMaxBuffer ) { TcpSetError( ERREUR_TAILLE_MAX ); return SOCKET_ERROR; } /* Lecture */ cbLus = 0; while( cbLenBuf - cbLus ) { if( ( cbRecv = recv( hSocket, szBuffer + cbLus, cbLenBuf - cbLus, flag )) <= 0 ) { TcpSetError( ERRNO ); return SOCKET_ERROR; } cbLus += cbRecv; } return cbLenBuf; } /*****************************************************/ /*Select non bloquant*/ BOOL TcpIsSet( SOCKET hSocket ) { /*struct*/ fd_set fd; struct timeval tv; /* Gestion du time out . Si -1, mode bloquant */ tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fd); FD_SET(hSocket, &fd); if(select( hSocket + 1, &fd, NULL, NULL,&tv ) == SOCKET_ERROR ) { TcpSetError( ERRNO ); return FALSE; } /* Rien dans le buffer */ if (! FD_ISSET(hSocket, &fd)) { return FALSE; } return TRUE; } /*****************************************************/ void TcpDestroy( SOCKET hSocket ) { TcpUdpDestroy( hSocket ); }