Forum Programmation.c Appel systeme send ()

Posté par  .
Étiquettes : aucune
0
16
fév.
2006
bonjour a tous,
je realise des appels systemes send () en C, en tres grande quantite (petit programme test de debit reseau). je suis sur 2 PC en Mandriva 2006
hors, je tombe sur le probleme suivant:
par moment, l'appel send () me retourne une valeur inferieure a la valeur du len (3e parametre du send ()).
si je comprends bien le man, avec les options par defaut (appel bloquant), soit le send me renvoie -1, soit il bloque jusqu'a ce que toutes les donnees soient passees au systeme ?
aurais-je rate qq chose ?
si qq peut eclairer ma lanterne...
merci
don
  • # Documentation incorrecte

    Posté par  . Évalué à 1.

    Dans la norme POSIX "IEEE Std 1003.1, 2004 Edition" [1], il est dit pour send():
    RETURN VALUE

    Upon successful completion, send() shall return the number of bytes sent. Otherwise, -1 shall be returned and errno set to indicate the error.

    Ton implémentation de send() est donc peut-être correcte, mais sûrement pas la doc (conforme ou pas, de toute façon il y a un écart entre le code et la doc, donc un rapport de bug s'impose).

    [1] http://www.opengroup.org/bookstore/catalog/t041.htm
    • [^] # Re: Documentation incorrecte

      Posté par  . Évalué à 1.

      oui, mais tout de suite apres, dans la doc, on a:


      If the message is too long to pass atomically through the underlying protocol, the message is not transmitted, -1 is returned, and errno is set to [EMSGSIZE].


      et c'est la que je ne comprends pas. dans mon appel a send(), d'apres cetter phrase, soit toute passe d'un coup, soit rien ne passe et j'ai une erreur qui me remonte...

      hors, moi, seule une partie du buffer est envoyee, et je n'ai pas d'erreur de remontee (appel = succes)

      il me semble alors que dans le cas d'un appel bloquant, la portion de la doc que tu cites est en contradiction avec la phrase juste apres dans la doc que je cites. parcontre, ce serait ok dans le cas d'un appel non bloquant...

      qq a-t-il une explication sur le sujet ?
      • [^] # Re: Documentation incorrecte

        Posté par  . Évalué à 1.

        Pour information, le processus à l'autre bout de la socket, il reçoit quoi quand tu rencontres ce comportement ?

        De plus, est-ce que la longueur du buffer correspond bien à l'argument length ? et ca tient bien dans un size_t ?
        • [^] # Re: Documentation incorrecte

          Posté par  . Évalué à 1.

          le process a l'autre bout de la socket recoit bien ce que le send lui envoie. et une trace sniffer le confirme. la longueur du buffer correspond bien a l'argument length (meme plus grand en fin de boucle --> voir code). et 8192 doit tenir dans size_t ! voila le code... je l'ai un peu elague car il y a des portions optionnelles que je n'utilises pas dans le test dont je parle en debut de ce thread...
          void client_task (int len)
          {
                  int sockfd;
                  struct sockaddr_in srv;
                  struct sockaddr_in where;
                  int where_len;
                  char *buf;
                  int i;
                  uint64_t total_len = 0;
          
                  if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
                          perror ("client_task(): socket()");
                          exit (errno);
                  }
          
                
                  memset (&srv, 0, sizeof (srv));
                  srv.sin_family = AF_INET;
                  srv.sin_addr.s_addr = server_addr.s_addr;
                  srv.sin_port = htons (mtcp_port);
          
                  if (connect (sockfd, (struct sockaddr *) &srv, sizeof (srv)) < 0) {
                          perror ("client_task(): connect()");
                          close (sockfd);
                          exit (errno);
                  }
          
                  where_len = sizeof (where);
                  if (getsockname (sockfd,
                                  (struct sockaddr *) &where,
                                  (socklen_t *) &where_len) < 0) {
                          perror ("client_task(): getsockname()");
                          exit (errno);
                  }
                  printf ("[%4d] Local %s port %d connected to remote %s port %d\n",
                          sockfd,
                          inet_ntoa (where.sin_addr),
                          ntohs (where.sin_port),
                          inet_ntoa (srv.sin_addr),
                          ntohs (srv.sin_port));
          
                  buf = (char *) malloc (buf_len);
                  memset (buf, 0, buf_len);
          
                  for (i = 0; i < len; i+= buf_len) {
                          int sz;
                          sz = MIN (buf_len, (len - i));
          
                          if (send (sockfd, buf, sz, 0) != sz) {
                                  perror ("client_task(): send()");
                                  close (sockfd);
                                  free (buf);
                                  exit (errno);
                          } else {
                                  total_len += sz;
                          }
                  }
          [...]
          
          
          • [^] # Re: Documentation incorrecte

            Posté par  . Évalué à 1.

            int where_len;
            (...)
            where_len = sizeof (where);
            if (getsockname (sockfd,
            (struct sockaddr *) &where,
            (socklen_t *) &where_len) < 0) {
            Erreur: tu déclares where_len comme int, mais tu y mets un size_t, que tu utilises ensuite comme un socklen_t (qui fait au moins 32 bits suivant POSIX)...
            Je n'ai pas regardé en détail la norme, mais la relation entre int et size_t ne semble pas définie. J'ai juste trouvé dans 7.17p4 une recommandation que size_t ne soit pas plus grand que long.

            buf = (char *) malloc (buf_len);

            En C, contrairement au C++, on ne DOIT pas caster le retour de malloc(). 1) c'est inutile et 2) ça peut cacher l'absence de l'include correspondant. Et si on utilise une fonction sans avoir son prototype, on se rapproche du comportement indéfini (je ne me souviens plus du statut exact, mais je soupçonne l'UB).
            Je suppose que, dans ton "vrai" code non simplifié, tu vérifies le retour de malloc().

            for (i = 0; i < len; i+= buf_len) {
            int sz;
            sz = MIN (buf_len, (len - i));

            if (send (sockfd, buf, sz, 0) != sz) {
            Pourquoi n'as-tu pas déclaré sz de type size_t ? Idem pour i et len; je ne vois pas pourquoi tu utilises des int alors que tu as besoin de size_t, vu que malloc, sizeof, send et plein d'autres travaillent avec ce type. En utiliser un autre mène tôt ou tard à avoir des valeurs hors domaine, et à des bugs difficilement identifiables.
            Ca a peut-être l'air de chipotage, mais puisque C et POSIX se décarcassent à définir des prototypes précis pour leurs fonctions, autant les respecter.

            Je ne sais pas si ça résoudra ton problème précis, mais au moins ça t'en évitera d'autres.
  • # Man pages

    Posté par  . Évalué à 1.

    Salut;

    Extrait de la man-page (Linux) :


    When the message does not fit into the send buffer of the socket, send normally blocks, unless the socket has been
    placed in non-blocking I/O mode.


    A priori, rien ne dit que le message entier est supposé être délivré même si sa longeur est inférieure à la limite imposée localement. J'ai rencontré ce cas à plusieurs reprise et j'ai interprété la valeur de retour (si est elle strictement supérieure à 0) comme étant l'offset de départ pour le prochain send :


    The calls return the number of characters sent, or -1 if an error occurred.


    Cela afin d'envoyer le message complètement. Je crois qu'il s'agit d'une approche assez classique (?).

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.