Recv() winsock2 en plusieurs morceaux

cs_billbaxter Messages postés 11 Date d'inscription mercredi 13 juillet 2005 Statut Membre Dernière intervention 27 juillet 2005 - 13 juil. 2005 à 23:39
cs_billbaxter Messages postés 11 Date d'inscription mercredi 13 juillet 2005 Statut Membre Dernière intervention 27 juillet 2005 - 16 juil. 2005 à 00:55
Bonjour,

J'ai développé une appli client-serveur avec les winsock2 en me référant aux exemples MSDN.
Ceux-ci ne présentent qu'un dialogue effectué en un seul coup sur un petit nombre d'octets (32).
J'y arrive donc sans problème, mais lorsque le send() envoie plus que la taille du buffer spécifié dans recv(), le dernier recv() reste bloqué !

BOOL ReceiveInBox(SOCKET Sock, char *szRBuf, int cRBufLen, int *cBytesReallyRead)
{
int cBytesRead ;
cBytesRead = recv (Sock, szRBuf, cRBufLen, 0) ;
if ((cBytesRead != SOCKET_ERROR) && (cBytesRead != 0))
{ *cBytesReallyRead = cBytesRead ;
return TRUE ;
}
else
{ *cBytesReallyRead = 0 ;
return FALSE ;
}
}

Voici la boucle (simplifiée) de réception en plusieurs morceaux :

lpBufCour = lpBuf ;
bPremLu = FALSE ;
dimlus = 0 ;
for (;;)
{
// Receive data from the server socket.
if (!ReceiveInBox (ServerSock, lpBufCour, 2048, &nblus))
return FALSE ;

dimlus += nblus ;
if ((bPremLu == FALSE) && (dimlus >= OFFSET_QR_DATAS))
{ // il faut aller chercher en tête du premier sous-bloc, la dimension totale attendue
lpRhead = (struct r_inconnu *) lpBuf ;
dimbloc = lpRhead->dimbloc ;
bPremLu = TRUE ;
}
if ((DWORD)dimlus == dimbloc)
break ; // on sort de la boucle "for" <-------------- c'est le cas normal
if ((DWORD)dimlus > dimbloc)
return FALSE ;
lpBufCour += nblus ;
}

J'ai essayé en passant en mode bloquant et non bloquant, ça ne change rien.

BREF, MES DEUX QUESTIONS SONT :
1) Il semblerait que cRBufLen ait une incidence sur le fonctionnement de recv(). Quelqu'un a-t'il des infos sur une taille optimale ou maximale de ce paramètre de recv() ?
2) Comment savoir qu'il n'y a plus rien à recevoir. Faut-il attendre un recv() à 0 ou faut-il savoir (comme je l'ai fait) combien on attend d'octets, auquel cas on zappe le recv() retournant 0.

Merci de votre aide

BillBaxter (nouveau venu ce jour !)

6 réponses

cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
14 juil. 2005 à 03:13
Salut,

1) Plus le buffer est gros plus tu recois d'octets d'un coup.. donc je
dirais quelques Ko. Seulement recv() n'attendra pas que ton
buffer soit plein avant de retourner, il copiera simplement les données
déja recues dans ton buffer.

2) Pour savoir combien d'octet peuvent etre lus (cad combien d'octet
seront retournés instantanément si tu appeles recv()) tu peux utiliser
ioctlsocket avec le flag FIONREAD.
0
cs_billbaxter Messages postés 11 Date d'inscription mercredi 13 juillet 2005 Statut Membre Dernière intervention 27 juillet 2005
14 juil. 2005 à 11:30
Salut,

Merci de ta réponse, mais reprenons... En plus simple et plus compact.


J'ai un serveur qui reçoit une question, et doit renvoyer la réponse, de dimension dwReponse (variable : de 96 à xkO)


CODE SERVEUR : réponse à la question du client


----------------------------


lpBufCour = lpBuf ;


while (dwReponse > 0)


{ cBytesSent = send (CliSock, lpBufCour, dwReponse, 0) ;


if (cBytesSent != SOCKET_ERROR)


{ lpBufCour += cBytesSent;


dwReponse -= cBytesSent;


}


else


{ sprintf (szAux, "send() en échec, erreur WSAGetLastError %u\n", WSAGetLastError()) ;


OutputDebugString szAux) ;


}


}


// Disable sending on CliSock


shutdown (CliSock, SD_SEND);


closesocket (CliSock) ;





CODE CLIENT :


-----------------------


Les sockets sont bloquants (par défaut), il me semble que je "gracefully close" tout comme y faut.


Le client attend donc la réponse sans connaître à priori la dimension de la réponse. Comme les sockets sont bloquants, j'attends que recv() retourne 0 pour savoir que c'est terminé.





lpBufCour = lpBuf ;


bPremLu = FALSE ;


dimlus = 0 ;


do


{


// Receive data from the server socket.


cBytesRead = recv (ServerSock, lpBufCour, 4096, 0) ;


if (cBytesRead > 0) // NB : SOCKET_ERROR retourne -1


{


dimlus += cBytesRead ;


if ((bPremLu == FALSE) && (dimlus >= OFFSET_QR_DATAS))


{ // il faut aller chercher en tête du premier sous-bloc, la dimension totale attendue


lpRhead = (struct r_inconnu *) lpBuf ;


dimbloc = lpRhead->dimbloc ;


bPremLu = TRUE ;


}


lpBufCour += cBytesRead ;


}


else if (cBytesRead == SOCKET_ERROR)


{ char szError[512] ;


sprintf (szError, "pb recv WSAGetLastError () n°%d", WSAGetLastError ()) ;


OutputDebugString (szError) ;


OutputDebugString ("\n") ;


}


// else (cBytesRead == 0)


} while (cBytesRead != 0) ;


// NB : on doit bien avoir dimlus égal à dimbloc


// Disable receiving on ServerSock.


// shutdown (ServerSock, 0x00);


CleanUp ();








Mon problème est que le recv() bloque indéfiniment ou presque (jusqu'à avoir WSAECONNRESET comme erreur) dès que j'ai un nombre "important" d'octets (ok avec 96, bloque avec 3229 par exemple).


Tout allait bien jusqu'alors, mais ce problème est apparu lorsque j'ai augmenté le débit entre le client et le serveur. Ca ressemble fort à un problème de synchro.


De plus ça ne le fait qu'entre deux PC particuliers !!!


Pour revenir à mon message précédent, j'ai essayé avec des dimensions variables dans recv() pour morceler différemment, et j'ai essayé en non bloquant. Même effet.


A propos des sockets non-bloquants, qui peut et doit le mettre en non-bloquant, l'émetteur, le récepteur, les deux ???


Merci de ton aide.
0
cs_billbaxter Messages postés 11 Date d'inscription mercredi 13 juillet 2005 Statut Membre Dernière intervention 27 juillet 2005
14 juil. 2005 à 14:44
Avant d'avoir une réponse éventuelle, je viens de trouver un truc à tester, sur www.sockets.com
Apparemment, il ne faut pas faire de closesocket tant qu'on n'est pas sûr que le send a flushé tout son contenu.
Il faut que je teste (demain) :

struct linger hangon ;
hangon.l_onoff=1 ;
hangon.l_linger=5 ; // timeout en sec.
setsockopt (sock, SOL_SOCKET, SO_LINGER, (char *)&hangon, sizeof (hangon)) ;

Est-ce que ça va régler mon problème ?
0
cs_aardman Messages postés 1905 Date d'inscription mercredi 22 janvier 2003 Statut Membre Dernière intervention 17 septembre 2012 3
16 juil. 2005 à 00:05
Salut,

Tu pourrais mettre un petit (le plus petit possible) bout de code qui
illustre ton probleme ? car j'avoue j'ai pas vraiment compris ou il se
situait.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_billbaxter Messages postés 11 Date d'inscription mercredi 13 juillet 2005 Statut Membre Dernière intervention 27 juillet 2005
16 juil. 2005 à 00:47
En fait je crois que mon code est nickel, ça marche sur des dizaines de configurations chez mes clients (poste serveur S à un endroit S et postes clients C à un endroit C) depuis des années ! Vraiment...
C'est du send receive classique, MSDN pur jus.

Ca fait une semaine que j'essaie plein de trucs (bloquant, non bloquant, shutdown, close, morcellement différent, etc...) et rien n'y fait.

Je viens de m'apercevoir que cette config qui ne marche pas est la seule qui concerne deux machines distantes dotées d'une Freebox dont l'IP fixe ne diverge que par le dernier nombre (DSLAM commun j'imagine). En effet, ces deux PC sont dans le même quartier. Adresse 78.45.72.110 pour le serveur et 78.45.72.110 pour le clients (adresses fictives évidemment)

J'en ai une troisième avec un autre fournisseur aussi dans le même quartier, et MIRACLE cette machine attaque le serveur S sans problème ! L'adresse IP est complètement différente.

En ce qui me concerne j'ai une freebox à mon domicile et j'attaque le serveur S sans problème.

Toujours plus fort, depuis le client C je peux attaquer le serveur d'un autre client situé ailleurs géographiquement.

Bref, plutôt que de mettre en doute le code, je commence donc à me demander si le pb ne vient pas d'un mauvais routage du côté de Free dans ce cas particulier, ce pb étant apparu qu'à partir de l'installation de la Freebox sur le site serveur et sur le site client. Avant, c'était moins rapide mais opérationnel !

La solution serait d'essayer un autre FAI sur le serveur, mais j'aimerais quand même comprendre.

Pour info, la partie du code qui déconne est le recv qui reste bloqué indéfiniment du coté client lorsque le serveur envoie une réponse de quelques kO (3229 octets par exemple)

lpBufCour= lpBuf ;
bPremLu = FALSE ;


dimlus = 0 ;


do


{


// Receive data from the server socket.


cBytesRead = recv (ServerSock, lpBufCour, 4096, 0) ;


if (cBytesRead > 0) // NB : SOCKET_ERROR retourne -1


{


dimlus += cBytesRead ;


if ((bPremLu == FALSE) && (dimlus >= 32))


{ // il faut aller chercher en tête du premier sous-bloc, la dimension totale attendue


lpRhead = (struct r_inconnu *) lpBuf ;


dimbloc = lpRhead->dimbloc ;


bPremLu = TRUE ;


}


lpBufCour += cBytesRead ;


}


else if (cBytesRead == SOCKET_ERROR)


{ char szError[512] ;


sprintf (szError, "pb recv WSAGetLastError () n°%d", WSAGetLastError ()) ;


OutputDebugString (szError) ;


OutputDebugString ("\n") ;


}


// else (cBytesRead == 0)


} while (cBytesRead != 0) ;


// NB : on doit bien avoir dimlus égal à dimbloc


// Disable receiving on ServerSock.


shutdown (ServerSock, 0x00);
0
cs_billbaxter Messages postés 11 Date d'inscription mercredi 13 juillet 2005 Statut Membre Dernière intervention 27 juillet 2005
16 juil. 2005 à 00:55
Erratum :

...
Je viens de m'apercevoir que cette config qui ne marche pas est la seule qui concerne deux machines distantes dotées d'une Freebox dont l'IP fixe ne diverge que par le dernier nombre (DSLAM commun j'imagine). En effet, ces deux PC sont dans le même quartier. Adresse 78.45.72.110 pour le serveur et 78.45.72.67 pour le clients (adresses fictives évidemment)
...

Ceci dit je crois que je vais redéposer ma question sous un autre titre car "recv() winsock2 en plusieurs morceaux", ça veut plus dire grand chose. Ca correspondait à ma première piste, abandonnée.
Je vais quand même continuer à chercher de mon côté.
A suivre...
0
Rejoignez-nous