Thread principal bloqué après un appel à recv dans un thread distinct

patochdu77 Messages postés 21 Date d'inscription jeudi 12 février 2004 Statut Membre Dernière intervention 21 octobre 2008 - 20 oct. 2008 à 10:16
patochdu77 Messages postés 21 Date d'inscription jeudi 12 février 2004 Statut Membre Dernière intervention 21 octobre 2008 - 21 oct. 2008 à 18:26
Bonsoir !


Je m'en remet à vous après de longues heures de recherche et ceci depuis un peu plus d'une semaine pour résoudre un problème.

Je developpe un jeu vidéo type rpg en ligne (du genre de ceux très
prisés de nos jours) en C++, avec pour API graphique DirectX (natif),
et la librairie Winsocks pour le réseau.


Jusqu'à maintenant j'utilisait des sockets bloquantes ainsi que la
fonction de multiplexage select afin de pouvoir envoyer mes requêtes au
serveur et en recevoir les réponses de façon à ne pas bloquer le rendu
graphique.

Seulement, à moins que je ne me sois fourvoyé, il me semble qu'il faut
appeler celle-ci avec une structure timeval correspondant à un
intervalle de temps non nul afin de ne pas bloquer, soit au minimum à
peu près 10 ms.

Mon soucis avec cette solution c'est que ce temps accumulé à chaque
appel de select, a un impact, même s'il est minime, sur le temps de
boucle de mon programme.

D'où ma première question, devrais-je négliger ce temps ou aborder le problème d'une autre façon ?


Depuis j'ai essayé une autre méthode, effectuer l'envoi et la reception
des données dans un thread distinct. C'est ici que réside mon plus gros
soucis, une fois que l'utilisateur a entré ses identifiants je lance le
thread qui effectue les opérations d'authentification. La fonction
connect est appelée avec succès et ne bloque pas un poil le thread
principal (de rendu), en revanche après l'envoi des identifiants,
j'appelle la fonction recv afin de recevoir la réponse et cet appel
bloque mon thread principal, et donc le rendu pendant un temps court (1
à 2 sec).

Afin de m'assurer que le problème était bien du à l'appel de recv, j'ai
rajouté un Sleep de quelques secondes à mon serveur entre la reception
des identifiants et l'envoi de la réponse, on constate bien que recv
bloque son propre thread (normal) et le thread principal donc pas de
rendu pendant ce temps là =/


J'espère avoir été le plus clair possible, dans le cas contraire ou
s'il manque des indications, dites le moi, en attendant voici le code
associé au problème.

Merci d'avance à tous.


Le client
<!-- BEGIN TEMPLATE: bbcode_code -->

Code :
//La fonction principale du thread
DWORD WINAPI ConnectionThread(LPVOID lParam)
{
t_client *m_client = (t_client*)lParam;
m_client->Connect();
return 0;
}

//La fonction appelée lorsque les identifiants de l'utilisateur ont été entrés lançant le thread
void t_client::StartConnection(string username, string password)
{
m_userinfo = username + ";" + password;
CreateThread(NULL, 0, ConnectionThread, this, 0, &m_thread);
}

//La fonction appelée par le thread
void t_client::Connect()
{
//On récupère l'IP du serveur à partir du nom de domaine
struct hostent *host;
host = gethostbyname((char *)m_hostname);
SOCKADDR_IN sin;
sin.sin_addr = *((struct in_addr *)host->h_addr);
sin.sin_family = AF_INET;
sin.sin_port = htons(3000);
//On créé le socket avec ces infos
m_socket = socket(AF_INET, SOCK_STREAM, 0);

//On procède à la connexion du client
int success = 0;
success = connect(m_socket, (SOCKADDR *)&sin, sizeof(sin));
if (success == - 1)
{
m_connected = false;
}
else
{
m_connected = true;
//J'ai testé en mode non bloquant avec les deux lignes suivantes, et ça fonctionne
//u_long val = 0;
//ioctlsocket(m_socket, FIONBIO, &val);

//Envoi des identifiants au serveur
Send(m_userinfo);

//Reception de la réponse
s_data login;
login = Receive();
if (login.val.front() == "ok")
{
//L'authentification a réussi
m_authentificated = true;
}
else
{
//L'authentification a échoué, on de deconnecte
m_authentificated = false;
m_connected = false;
}
}
}

<!-- END TEMPLATE: bbcode_code -->Le serveur:
<!-- BEGIN TEMPLATE: bbcode_code -->

Code :
void t_server::ProceedConnection()
{
SOCKADDR_IN sin;
int sin_size = sizeof(struct sockaddr_in);

//On accepte le client
int client = accept(m_socket, (SOCKADDR *)&sin, (int *)&sin_size);
if(client != INVALID_SOCKET)
{
//Reception des identifiants de connexionstring user_info "", username "", password = "";
user_info = Receive(client);
int i = 0;
while ((i < user_info.length()) && (user_info[i] != ';'))
{
username = username + user_info[i];
++i;
}
++i;
while (i < user_info.length())
{
password = password + user_info[i];
++i;
}
cout << "\nDemande de connexion de ";
cout << username << endl;
int login = m_database->User_Login(username, password);
if (login)
{
//Le client existe, on effectue un sleep pour mettre en évidence le blocage puis on envoie la confirmation
Sleep(10000);
Send(client, "login;ok");
++m_nbClients;
m_clients[m_nbClients - 1].name = username;
m_clients[m_nbClients - 1].desc = client;
cout << "Connexion acceptee" << endl;
}
else
{
//Les identifiants sont érronés, on envoie la réponse au client
Send(client, "error");
cout << "Connexion refusee, identifiants incorrects" << endl;
}
}
}

2 réponses

f_l_a_s_h_b_a_c_k Messages postés 56 Date d'inscription vendredi 14 avril 2006 Statut Membre Dernière intervention 1 février 2009
21 oct. 2008 à 01:27
ses comme lire un fichier byte par byte ses plus long que de lire un buffer[1024] d un coup

example 1024??

 int rcvtimeo = 5000 ; // 5 sec  receive time
 
    if( setsockopt( m_s , SOL_SOCKET , SO_RCVTIMEO , (const char *)&rcvtimeo , sizeof(rcvtimeo) ) == SOCKET_ERROR)
 
   
   char buf[1024];
       
        while(1)
        {
            //Sleep(1);
            iRet =     recv( m_s , buf , sizeof( buf ) , 0 ) ;

             

            if( iRet == SOCKET_ERROR )
            {
                dwErr = WSAGetLastError() ;
                printf( "Error recv() = %ld \n" , dwErr ) ;
                continue ;
            }
           
if(iRet>=1024) { ///buffer plein
//utiliser le  buffer!
//decoder une structure ou byte par byte
printf("%s",buffer);

iRet=0;
}

je sais pas si tu pas gagner de la vitesse comme ca?

}
0
patochdu77 Messages postés 21 Date d'inscription jeudi 12 février 2004 Statut Membre Dernière intervention 21 octobre 2008
21 oct. 2008 à 18:26
Salut à toi, merci d'avoir pris le temps de me répondre.

Si j'ai bien compris tu pense donc que la taille des données implique que la durée de reception est importante, donc le blocage serait du uniquement au temps de reception ?
Mais il me semble que ce n'est pas le cas car si je ne l'ai pas précisé, le blocage (l'arret du rendu) est observable dès l'appel à recv, alors que le serveur envoie la réponse environ 10sec après (grâce au sleep que j'ai placé pour mettre en évidence le problème). C'est donc le temps d'attente des données avant lecture qui pose problème.

Je rajouterai qu'en remplaçant l'appel à recv par une fonction testant le descripteur de socket à l'aide d'un select tant qu'il n'y a rien à lire, on constate le meme blocage, sur la fonction select.
Peut-être qu'en utilisant une socket non bloquante et en utilisantévénements WSAAsyncSelect ?

Enfin le soucis final est que logiquement une application multithread permet de se passer du select, des sockets non bloquantes et autres... Aurais-je mal compris quelque chose, ou est-ce une erreur de programmation?
0
Rejoignez-nous