From 0e4baf6a4e611326fe10f61ded1eaf5488241dd0 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Thu, 17 Dec 2015 18:25:39 +0100 Subject: [PATCH] Gestion du TIMEOUT lors du blocage (tout fonctionne bien) --- dep/client.c | 4 ++++ dep/client.h | 1 + dep/server.c | 15 +++++++++++---- dep/server.h | 3 +++ dep/utility.c | 46 ++++++++++++++-------------------------------- dep/utility.h | 9 --------- proxy_ftp.c | 26 ++++++++++++++++---------- proxy_ftp.h | 8 +++----- test | Bin 22870 -> 22983 bytes 9 files changed, 52 insertions(+), 60 deletions(-) diff --git a/dep/client.c b/dep/client.c index e736653..09ceb6b 100644 --- a/dep/client.c +++ b/dep/client.c @@ -47,6 +47,7 @@ int CONNECT_CLIENT(char* serverHost, char* serverPort, int* pSocket){ // si erreur if( *pSocket == -1 ) return -1; + /* [4] On établit la connection =======================================================*/ CONNECT = connect( @@ -63,6 +64,9 @@ int CONNECT_CLIENT(char* serverHost, char* serverPort, int* pSocket){ // on a plus besoin des infos de l'adresse freeaddrinfo(addrinfo); + + /* [5] On retourne la SOCKET + =======================================================*/ return *pSocket; } diff --git a/dep/client.h b/dep/client.h index 719c869..bac9c50 100644 --- a/dep/client.h +++ b/dep/client.h @@ -14,6 +14,7 @@ * [2] On récupère les infos * [3] Création de la socket * [4] On établit la connection +* [5] On retourne la SOCKET * */ int CONNECT_CLIENT(char* serverHost, char* serverPort, int* pSocket); diff --git a/dep/server.c b/dep/server.c index 4247e57..b1b8e63 100644 --- a/dep/server.c +++ b/dep/server.c @@ -63,6 +63,7 @@ void DROP_SERVER(const char* serverHost, char** givenPort, int* listenSocket){ // si erreur if( SOCKET == -1 ) return; + /* [4] On publie la SOCKET =======================================================*/ BIND = bind( @@ -357,10 +358,16 @@ void MANAGE_RESPONSE(char* pAnswer, int* USER_SOCKET, int* FTP_SOCKET, int* DUSE // printf("") /* 5. On vide le BUS DE COMMANDE du serveur FTP */ - printf("XX: [%d]\n", tmp[2]); - tmp[2] = sread(FTP_SOCKET, BUFFER); - printf("YY: [%d]\n", tmp[2]); - + /* A. On définit un TIMEOUT (1 sec) */ + setSocketTimeout(FTP_SOCKET, 1); + + /* B. On attends une réponse */ + tmp[2] = sread(FTP_SOCKET, BUFFER); + + /* C. On enlève le TIMEOUT (default) */ + setSocketTimeout(FTP_SOCKET, SOCKET_TIMEOUT); + + /* 6. On transmet le message de fin de la transaction (226) */ strcpy(response, STOP_DAT_MSG); break; diff --git a/dep/server.h b/dep/server.h index aea8467..434d260 100644 --- a/dep/server.h +++ b/dep/server.h @@ -89,6 +89,9 @@ void MANAGE_REQUEST(char* pRequest, int* USER_SOCKET, int* FTP_SOCKET, int* DUSE * 3. On redirige sur la SOCKET utilisateur (BUS DE DONNEES) * 4. On ferme les SOCKETS du BUS DE DONNEES * 5. On vide le BUS DE COMMANDE du serveur FTP +* A. On définit un TIMEOUT (1 sec +* B. On attends une réponse +* C. On enlève le TIMEOUT (default) * 6. On transmet le message de fin de la transaction (226) * * (9) 226- Fin de transfert de données (fermeture SOCKETS du BUS DE DONNEES) diff --git a/dep/utility.c b/dep/utility.c index dbbea23..80c7c75 100644 --- a/dep/utility.c +++ b/dep/utility.c @@ -65,47 +65,18 @@ int indexOf(char* haystack, char needle){ } -void formatBuffer(char* pBuffer){ - if( DEBUGMOD&BUF ) printf( "BUFLEN (bef): %lu\n", strlen(pBuffer) ); - if( DEBUGMOD&BUF ) printf( "BUFFER: (%s)\n", pBuffer ); - - /* [1] On retire les "\n" et "\r" de la fin de la chaine - ============================================================*/ - unsigned long i = strlen(pBuffer) - 1; - - revealString(pBuffer); - while( pBuffer[i] == '\r' || pBuffer[i] == '\n' ){ - printf("(%lu)\n", i); - if( pBuffer[i] != '\r' && pBuffer[i] != '\n' ) - break; - - pBuffer[i] = '\0'; - i = strlen(pBuffer) - 1; - } - - revealString(pBuffer); - /* [2] On ajoute "\r\n" à la fin - ============================================================*/ - strcat(pBuffer, "\r\n\0"); - - if( DEBUGMOD&BUF ) printf( "BUFLEN (aft): %lu\n", strlen(pBuffer) ); -} - int swrite(int* pSocket, char* pBuffer){ if( *pSocket == -1 ) return -1; // si SOCKET fermée, on retourne une erreur if( strlen(pBuffer) == 0 ) return 0; // si on a rien à écrire, on n'écrit rien :p - /* 1. On formatte le Buffer */ - // formatBuffer(pBuffer); - - /* 2. On écrit sur la SOCKET */ + /* 1. On écrit sur la SOCKET */ if( DEBUGMOD&BUF ) printf("SENDLEN_1: %lu\n", strlen(pBuffer)); int nbSent = write(*pSocket, pBuffer, strlen(pBuffer)); if( DEBUGMOD&BUF ) printf("SENDLEN_2: %d\n", nbSent); - /* 3. Si on est déconnecté, on retourne une erreur */ + /* 2. Si on est déconnecté, on retourne une erreur */ if( nbSent <= 0 ){ if( DEBUGMOD&BUF ) printf("NOTHING TO SEND\n"); return -1; // on retourne une erreur @@ -116,7 +87,7 @@ int swrite(int* pSocket, char* pBuffer){ if( DEBUGMOD&RVL ) printf("SEND_2\n"); - /* 4. On retourne le nombre de envoyés */ + /* 3. On retourne le nombre de envoyés */ return nbSent; } @@ -149,6 +120,17 @@ int sread(int* pSocket, char* pBuffer){ } +void setSocketTimeout(int* pSocket, const int pTimeout){ + /* 1. On créé la structure contenant le timeout */ + struct timeval timeout; + timeout.tv_sec = pTimeout; + timeout.tv_usec = 0; + + setsockopt(*pSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(struct timeval)); + + if( DEBUGMOD&SCK ) printf("Set socket(%d) timeout to %lus\n", *pSocket, (long unsigned) timeout.tv_sec ); +} + void xPrint(char* pPattern, char* pBuffer){ char tmpBuffer[maxBuffLen]; diff --git a/dep/utility.h b/dep/utility.h index bae3723..d27ba60 100644 --- a/dep/utility.h +++ b/dep/utility.h @@ -33,15 +33,6 @@ int indexOf(char* haystack, char needle); -/* Formatte un buffer pour être envoyé -* -* @pBuffer Buffer en question -* -*/ -void formatBuffer(char *pBuffer); - - - /* read/write socket */ int swrite(int* pSocket, char* pBuffer); int sread(int* pSocket, char* pBuffer); diff --git a/proxy_ftp.c b/proxy_ftp.c index 8b0008c..bee0801 100644 --- a/proxy_ftp.c +++ b/proxy_ftp.c @@ -97,22 +97,27 @@ int main(int argc, char* argv[]){ * @THREADABLE_SOCKET SOCKET de la connexion client * * @history -* [1] Initialisation des variables -* [2] Envoi de la séquence de bienvenue -* [3] Attente des données reçues et reception (CLIENT_FTP->PROXY) -* [4] Traitement de la requête client -* [5] Redirection de la requête vers le serveur FTP (PROXY->SERVER_FTP) -* [6] Traitement de la reponse du serveur FTP -* [7] Redirection de la réponse vers le client (PROXY->CLIENT_FTP) -* +* [1] Initialisation des variables +* [2] Envoi de la séquence de bienvenue +* [3] Attente des données reçues et reception (CLIENT_FTP->PROXY) +* [4] Traitement de la requête client +* [5] Redirection de la requête vers le serveur FTP (PROXY->SERVER_FTP) +* [6] Traitement de la reponse du serveur FTP +* [7] Redirection de la réponse vers le client (PROXY->CLIENT_FTP) +* [8] On vide les buffers +* [9] Fermeture des connections (SOCKETS) +* [10] Arrêt du THREAD +* 1. On récupère le rang dans les "managers" +* 2. On met à jour "activeManagers" +* 3. On arrête le THREAD * */ void* manageConnection(void* THREADABLE_SOCKET){ - int USER_SOCKET = (intptr_t) THREADABLE_SOCKET; + int USER_SOCKET = (intptr_t) THREADABLE_SOCKET; // BUS DE COMMANDE utilisateur /* [1] Variables ============================================================================*/ - // int USER_SOCKET = -1; // contiendra le BUS DE COMMANDE utilisateur + // int USER_SOCKET = -1; int FTP_SOCKET = -1; // contiendra le BUS DE COMMANDE FTP int DUSER_SOCKET = -1; // contiendra le BUS DE DONNES utilisateur int DFTP_SOCKET = -1; // contiendra le BUS DE DONNEES FTP @@ -183,6 +188,7 @@ void* manageConnection(void* THREADABLE_SOCKET){ if( index != -1 ) activeManagers[index] = 0; + /* 3. On arrête le THREAD */ if( DEBUGMOD&THR ) printf("THREAD[%d] libéré\n", index); pthread_exit(NULL); } \ No newline at end of file diff --git a/proxy_ftp.h b/proxy_ftp.h index 57e5dc6..9d5ab2e 100644 --- a/proxy_ftp.h +++ b/proxy_ftp.h @@ -33,7 +33,7 @@ #define THR 0x40 // FILTRE pour THREADS // possibilité de cumuler les DEBUGMODES -#define DEBUGMOD 0 // REVEALS + HEADER + THREADS +#define DEBUGMOD SCK // REVEALS + HEADER + THREADS /* vars */ @@ -44,12 +44,10 @@ #define maxHostLen 64 #define maxPortLen 6 -// #define FTP_HOST "31.170.164.47" +// #define FTP_HOST "31.170.164.47" // u712664263 #define FTP_HOST "localhost" #define FTP_PORT "21" -// u712664263 - - +#define SOCKET_TIMEOUT 10, 0 // 10 secondes & 0 micro /* MESSAGES */ // 220-\n // 220- +----------------------------+\n diff --git a/test b/test index 020360e6465f43740407916ddf8e7e71c531491e..5d01d9c8abbe332a8886a42e68ee35830f71488a 100755 GIT binary patch delta 7138 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 AHvj+t