Socket recv() hängen große Nachricht mit MSG_WAITALL

Ich habe eine Anwendung, die liest große Dateien von einem server und hängt sich Häufig auf eine bestimmte Maschine. Er arbeitete erfolgreich unter RHEL5.2 für eine lange Zeit. Wir haben vor kurzem ein Upgrade auf RHEL6.1 und es hängt jetzt regelmäßig.

Habe ich eine test-app, die das problem reproduziert. Es hängt ca 98 mal von 100.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>

int mFD = 0;

void open_socket()
{
  struct addrinfo hints, *res;
  memset(&hints, 0, sizeof(hints));
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_family = AF_INET;

  if (getaddrinfo("localhost", "60000", &hints, &res) != 0)
  {
    fprintf(stderr, "Exit %d\n", __LINE__);
    exit(1);
  }

  mFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);

  if (mFD == -1)
  {
    fprintf(stderr, "Exit %d\n", __LINE__);
    exit(1);
  }

  if (connect(mFD, res->ai_addr, res->ai_addrlen) < 0)
  {
    fprintf(stderr, "Exit %d\n", __LINE__);
    exit(1);
  }

  freeaddrinfo(res);
}

void read_message(int size, void* data)
{
  int bytesLeft = size;
  int numRd = 0;

  while (bytesLeft != 0)
  {
    fprintf(stderr, "reading %d bytes\n", bytesLeft);

    /* Replacing MSG_WAITALL with 0 works fine */
    int num = recv(mFD, data, bytesLeft, MSG_WAITALL);

    if (num == 0)
    {
      break;
    }
    else if (num < 0 && errno != EINTR)
    {
      fprintf(stderr, "Exit %d\n", __LINE__);
      exit(1);
    }
    else if (num > 0)
    {
      numRd += num;
      data += num;
      bytesLeft -= num;
      fprintf(stderr, "read %d bytes - remaining = %d\n", num, bytesLeft);
    }
  }

  fprintf(stderr, "read total of %d bytes\n", numRd);
}

int main(int argc, char **argv)
{
  open_socket();

  uint32_t raw_len = atoi(argv[1]);
  char raw[raw_len];

  read_message(raw_len, raw);

  return 0;
}

Einige Notizen von meinen Tests:

  • Wenn "localhost" - Karten an die loopback-Adresse 127.0.0.1, die app hängt sich der Aufruf von recv() und kehrt NIE zurück.
  • Wenn "localhost" verweist auf die ip Adresse des Rechners, damit das routing der Pakete über die ethernet-Schnittstelle, die app erfolgreich abgeschlossen ist.
  • Wenn ich ein hang, sendet der server eine "TCP Window Full" angezeigt, und der client antwortet mit einem "TCP ZeroWindow" - Meldung (siehe Bild und befestigt tcpdump capture). Von diesem Punkt, es hängt immer mit dem server senden von keep-alives und den client senden ZeroWindow Nachrichten. Der client scheint nie erweitern Sie das Fenster, so dass die übertragung abgeschlossen ist.
  • Während der hängen, wenn ich überprüfen Sie die Ausgabe von "netstat-a", es gibt Daten in den Server senden-Warteschlange, aber die Kunden erhalten die Warteschlange leer ist.
  • Wenn ich entfernen Sie das flag MSG_WAITALL von der recv () - Aufruf, wird die app erfolgreich abgeschlossen ist.
  • Die hängenden Problem entsteht nur über das loopback-interface auf 1 Maschine. Ich vermute, dass dies alles im Zusammenhang mit timing-Abhängigkeiten.
  • Wie ich drop die Größe des 'Datei', die Wahrscheinlichkeit, dass der hang vorkommenden reduziert

Quelle für die test-app finden Sie hier:

Socket-test Quelle

Den tcpdump-Aufzeichnung von der loopback-Schnittstelle finden Sie hier:

tcpdump capture

Ich das Problem reproduzieren, indem Sie die folgenden Befehle ausgeben:

>  gcc socket_test.c -o socket_test
>  perl -e 'for (1..6000000){ print "a" }' | nc -l 60000
>  ./socket_test 6000000

Dieser sieht 6000000 bytes gesendet, um die test-app, die versucht, Lesen Sie die Daten mit einem einzigen Aufruf von recv().

Ich würde gerne Anregungen zu hören, auf was ich vielleicht falsch mache oder weitere Möglichkeiten zum Debuggen das Problem.

InformationsquelleAutor Shane Carr | 2011-12-12

Schreibe einen Kommentar