Socket bloqué en lecture lors de l’envoi de x86 à x64

J’écris un client / serveur de messagerie minuscule pour un projet de classe. Lorsque j’exécute le client et le serveur sur le même ordinateur, tout fonctionne correctement. Lorsque j’exécute le serveur sur une machine x86 et qu’un client sur une machine x64, je suis bloqué en lecture. Lorsque j’exécute le serveur sur une machine x64 et qu’un client sur une machine x86 est lu, la lecture est terminée, mais lorsque j’imprime la chaîne, elle est vide. Voici la fonction que j’utilise pour lire

/*----------------------------------------------------------------*/ int readn(int sd, char *buf, int n) { printf("readn via utils. %d, %s, %d\n", sd, buf, n); int toberead; char * ptr; toberead = n; ptr = buf; while (toberead > 0) { int byteread; fprintf(stderr, "toberead: %d, byteread: %d\n",toberead, byteread ); byteread = read(sd, ptr, toberead); fprintf(stderr, "toberead: %d, byteread: %d\n",toberead, byteread ); if (byteread <= 0) { fprintf(stderr, "byteread val: %d",byteread); if (byteread == -1) perror("read"); raise (6); return (0); } toberead -= byteread; ptr += byteread; } fprintf(stderr, "Finished readn. %s\n", buf); return (1); } 

Autres fonctions utilitaires qui utilisent ou ont un impact sur readn

 /*----------------------------------------------------------------*/ Packet *recvpkt(int sd) { printf("Recvpkt via utils.\n"); Packet *pkt; /* allocate space for the pkt */ pkt = (Packet *) calloc(1, sizeof(Packet)); if (!pkt) { fprintf(stderr, "error : unable to calloc\n"); return(NULL); } /* read the message type */ if (!readn(sd, (char *) &pkt->type, sizeof(pkt->type))) { free(pkt); return(NULL); } /* read the message length */ if (!readn(sd, (char *) &pkt->lent, sizeof(pkt->lent))) { free(pkt); return(NULL); } pkt->lent = ntohl(pkt->lent); /* allocate space for message text */ if (pkt->lent > 0) { pkt->text = (char *) malloc(pkt->lent); if (!pkt) { fprintf(stderr, "error : unable to malloc\n"); return(NULL); } /* read the message text */ if (!readn(sd, pkt->text, pkt->lent)) { freepkt(pkt); return(NULL); } } fprintf(stderr, "Reading packet complete succesfully.\n"); /* done reading */ return(pkt); } int sendpkt(int sd, char typ, long len, char *buf) { fprintf(stderr, "Send packet via utils.\n"); char tmp[8]; long siz; /* write type and lent */ bcopy(&typ, tmp, sizeof(typ)); siz = htonl(len); bcopy((char *) &siz, tmp+sizeof(typ), sizeof(len)); write(sd, tmp, sizeof(typ) + sizeof(len)); /* write message text */ if (len > 0) write(sd, buf, len); return(1); } void freepkt(Packet *pkt) { fprintf(stderr, "Freeing packet.\n"); free(pkt->text); free(pkt); } 

La sortie de ce qui précède dans le premier cas (x86 à x64) est ci-dessous.

 readn via utils. 3, , 1 toberead: 1, byteread: 0 toberead: 1, byteread: 1 Finished readn. readn via utils. 3, , 8 toberead: 8, byteread: 1 toberead: 8, byteread: 8 Finished readn. readn via utils. 3, , 57 toberead: 57, byteread: 58 toberead: 57, byteread: 53 toberead: 4, byteread: 53 

La sortie de la fonction de x64 à x86 est ci-dessous

 readn via utils. 3, , 1 toberead: 1, byteread: -1079631112 toberead: 1, byteread: 1 Finished readn. readn via utils. 3, , 4 toberead: 4, byteread: 1 toberead: 4, byteread: 4 Finished readn. readn via utils. 3, , 57 toberead: 57, byteread: -1079631112 toberead: 57, byteread: 57 Finished readn. Reading packet complete succesfully. >> 

Les données sont censées être imprimées après >>

Faites-moi savoir si ma question est claire ou si d’autres informations. J’ai passé 2 jours complets à essayer de résoudre ce problème, mais en vain.

Mise à jour : La fonction qui appelle readn est mise à jour.

Sur x64, vous lisez 1 octet puis 8 octets (puis quelques octets supplémentaires).
Sur x86, vous lisez 1 octet puis 4 octets (puis quelques octets supplémentaires).
Ceci est un indice sur l’inadéquation entre les versions x86 et x64 de votre programme.

Peut-être appelez-vous comme ceci:

 long l; l = /* something */; sendn(sd, &l, sizeof l); 

long s a une largeur de 32 bits sous Linux 32 bits et de 64 bits sous Linux 64 bits.

Il y a plusieurs problèmes avec le code:

  • La première printf(""readn via utils ...) suppose que la mémoire pointée par buf a été initialisée, au moins buf[0] aurait dû être mis à '\0' , sinon le poids de cet appel n’est pas défini.

  • La première fonction fprintf(stderr, ... introduit un comportement non défini, car byteread est consulté non initialisé. int byteread = 0; ce problème et rendrait la sortie de journalisation beaucoup plus utile.

  • read() renvoie également ssize_t et prend size_t pas int comme le code. Cette erreur ferait échouer le code pour de gros morceaux de données.

  • L’appel à perror() peut ne pas refléter l’erreur survenue dans l’appel à read() car la valeur de errno peut-être été modifiée par l’appel à fprintf() effectué avant perror() .

  • Si les données reçues ne portent pas le caractère '\0' comme dernier caractère, la ligne fprintf(stderr, "Finished readn. %s\n", buf); serait également introduit un comportement indéfini. Modifiez-le pour imprimer octet par octet dans la mémoire tampon ou testez le 0 final et ajoutez-le “à la main” avant d’imprimer la “chaîne”.

Quelques corrections au code selon mes commentaires ci-dessus:

 int readn(int sd, char * buf, size_t n) { printf("readn via utils. %d, %s, %zu\n", sd, buf, n); size_t toberead = n; char * ptr = buf; while (toberead > 0) { int errno_save = 0; fprintf(stderr, "toberead: %zu\n", toberead); ssize_t byteread = read(sd, ptr, toberead); errno_save = errno; fprintf(stderr, "toberead: %zu, byteread: %zd\n", toberead, byteread); if (byteread <= 0) { fprintf(stderr, "byteread val: %zd\n", byteread); if (byteread == -1) { errno = errno_save; perror("read"); } raise(6); return (0); } toberead -= byteread; ptr += byteread; } if ('\0' != buf[n]) /* This assumes buf is one byte **larger** then n. */ { buf[n] = '\0'; } fprintf(stderr, "Finished readn. %s\n", buf); return (1); } 

Dernier commentaire: cette construction permettant de lire les données d’un socket suppose que l’appelant sait à l’avance combien d’octets seront envoyés, ou que la connexion sera fermée lorsque moins de données auront été envoyées, sinon (c’est-à-dire moins d'octets que n envoyé et la connexion n’est pas fermée) un appel à readn() serait bloqué.

Voir la réponse d ' immibis pour une raison pour laquelle ce dernier pourrait être un problème pour votre configuration (32 bits par rapport à 64 bits).