11 Stimmen

recv mit MSG_NONBLOCK und MSG_WAITALL

Ich möchte die recv Syscall mit nicht blockierenden Flags MSG_NONBLOCK. Aber mit diesem Flag kann der Syscall zurückkehren, bevor die gesamte Anforderung erfüllt ist. Also,

  • Kann ich das MSG_WAITALL-Flag hinzufügen? Wird es nicht blockierend sein?
  • oder wie sollte ich blockierende recv in die Schleife mit nicht blockierenden recv umschreiben

5voto

Matt Punkte 805

Zumindest für IPv4-TCP-Empfang unter Linux wird MSG_WAITALL ignoriert, wenn MSG_NONBLOCK angegeben ist (oder der Dateideskriptor auf non-blocking gesetzt ist).

Aus tcp_recvmsg() in net/ipv4/tcp.c im Linux-Kernel:

if (copied >= target && !sk->sk_backlog.tail)
        break;

if (copied) {
        if (sk->sk_err ||
            sk->sk_state == TCP_CLOSE ||
            (sk->sk_shutdown & RCV_SHUTDOWN) ||
            !timeo ||
            signal_pending(current))
                break;

target wird in diesem Cast auf die angeforderte Größe gesetzt, wenn MSG_DONTWAIT angegeben ist, oder auf einen kleineren Wert (mindestens 1), wenn nicht. Die Funktion wird beendet, wenn:

  1. Es wurden genügend Bytes kopiert
  2. Es liegt ein Socket-Fehler vor
  3. Die Steckdose wurde geschlossen oder heruntergefahren
  4. timeo ist 0 (Socket ist auf nicht-blockierend eingestellt)
  5. Es steht ein Signal für den Prozess an

Mir scheint, dass es sich um einen Fehler in Linux handelt, aber so oder so wird es nicht so funktionieren, wie Sie es wünschen. Es sieht so aus, als würde die Lösung von dec-vt100 funktionieren, aber es gibt eine Race Condition, wenn Sie versuchen, von demselben Socket in mehr als einem Prozess oder Thread zu empfangen.
Das heißt, ein weiterer recv()-Aufruf durch einen anderen Thread/Prozess könnte erfolgen, nachdem Ihr Thread einen Peek durchgeführt hat, wodurch Ihr Thread beim zweiten recv() blockiert wird.

3voto

Duck Punkte 25886

EDITAR:

Einfaches recv() gibt zurück, was sich zum Zeitpunkt des Aufrufs im TCP-Puffer befindet, und zwar bis zur angeforderten Anzahl von Bytes. MSG_DONTWAIT vermeidet lediglich das Blockieren, wenn auf dem Socket keine Daten zum Lesen bereitstehen. MSG_WAITALL fordert zum Blockieren auf, bis die gesamte Anzahl der angeforderten Bytes gelesen werden kann. Sie werden also kein "alles oder nichts"-Verhalten erhalten. Bestenfalls sollten Sie EAGAIN erhalten, wenn keine Daten vorhanden sind, und andernfalls blockieren, bis die gesamte Nachricht verfügbar ist.

Vielleicht können Sie etwas aus MSG_PEEK oder ioctl() mit einem FIONREAD (wenn Ihr System es unterstützt) machen, das sich effektiv so verhält, wie Sie es wollen, aber ich weiß nicht, wie Sie Ihr Ziel nur mit den recv()-Flags erreichen können.

3voto

dec-vt100 Punkte 190

Dies ist, was ich für das gleiche Problem getan habe, aber ich hätte gerne eine Bestätigung, dass dies wie erwartet funktioniert...

ssize_t recv_allOrNothing(int socket_id, void *buffer, size_t buffer_len, bool block = false)
{
    if(!block)
    {
        ssize_t bytes_received = recv(socket_id, buffer, buffer_len, MSG_DONTWAIT | MSG_PEEK);

        if (bytes_received == -1)
            return -1;

        if ((size_t)bytes_received != buffer_len)
            return 0;
    }

    return recv(socket_id, buffer, buffer_len, MSG_WAITALL);
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X