2017-12-18 19 views
3

Wir haben einen Code zum Senden von Metriken an einen SOCK_DGRAM, wo ein anderer Daemon diese Nachrichten überwacht und aggregiert/proxisiert. die Fassung zu öffnen wie folgt aussieht:Senden an einen Datagramm-Socket, ohne unbegrenzt zu blockieren

sock <- socket 
(ai :: AddressInfo Inet Datagram UDP):_ <- getAddressInfo (Just "127.0.0.1") Nothing aiNumericHost 
connect s (socketAddress ai) { port } 
return sock 

Und im Moment sind wir es schreiben:

send sock payload mempty 

Ich mag den Anruf, um sicherzustellen, oben nicht sehr lange blockiert (oder zumindest nicht unbegrenzt blockieren), aber mein Verständnis von Unix-Sockets ist nicht sehr tief und ich habe Probleme zu verstehen, wie genau send blockiert, Blick auf Interna here und here.

Es gibt eine ähnliche Frage hier, die hilfreich war: When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?

So sind meine Fragen im Einzelnen:

  • Allgemeine Socket Frage: Ich in dieser Implementierung sehe send (nach busy-Warte blockieren wird) bis im Puffer Platz ist. Wie genau bezieht sich dieser Puffer auf den Verbraucher? Bedeutet das, send kann unbegrenzt blockieren, wenn unser listen -ing-Daemon langsam ist oder abstürzt?
  • Wenn ich lieber abbrechen und nie blockieren würde, muss ich meine eigene Gabel von System.Socket.Unsafe machen, oder fehle ich etwas?

Ich bin nur mit Linux hier betroffen.

EDIT: Auch, und was hatte ich wahrscheinlich mit all diesem gestartet ist finde ich, dass, wenn der Kollektor Metriken nicht ausgeführt wird, dass jeder andere meiner send Anrufe über eine „Verbindung abgelehnt“ Ausnahme auslöst. Warum das so ist oder ob es normal ist, ist eine andere Frage, die ich habe.

EDIT2: Hier ist ein vollständiges Beispiel die Verbindung abgewiesen Problem darstellt, wenn jemand repro möchte helfen:

import Data.Functor 
import System.Socket 
import System.Socket.Family.Inet 

repro :: IO() 
repro = do 
    let port = 6565 
    (s :: Socket Inet Datagram UDP) <- socket 
    (ai :: AddressInfo Inet Datagram UDP):_ <- getAddressInfo (Just "127.0.0.1") Nothing aiNumericHost 
    connect s (socketAddress ai) { port } 

    putStrLn "Starting send" 
    void $ send s "FOO" mempty 
    void $ send s "BAR" mempty 
    putStrLn "done" 

Ich verwende socket-0.5.3.0.

EDIT3: dies scheint aufgrund der connect Aufruf, irgendwie. (Prüfung auf sockets latest):

{-# LANGUAGE ScopedTypeVariables, OverloadedStrings, NamedFieldPuns #-} 
import Data.Functor 
import System.Socket 
import System.Socket.Protocol.UDP 
import System.Socket.Type.Datagram 
import System.Socket.Family.Inet 

repro :: IO() 
repro = do 
    (s :: Socket Inet Datagram UDP) <- socket 

    -- Uncommenting raises eConnectionRefused, everytime: 
    -- connect s (SocketAddressInet inetLoopback 6565  :: SocketAddress Inet) 
    putStrLn "Starting send" 
    void $ sendTo s "FOO" mempty (SocketAddressInet inetLoopback 6565  :: SocketAddress Inet) 
    void $ sendTo s "BAR" mempty (SocketAddressInet inetLoopback 6565  :: SocketAddress Inet) 
    putStrLn "done" 

Wie ich es verstehe wir sollten in der Lage seiner connect zu verwenden (zumindest den zugrunde liegenden syscall) der Standard festlegen Adresse. Ich habe noch nicht in die Implementierung von connect in der Bibliothek gegraben.

Ich habe diese geöffnet: https://github.com/lpeterse/haskell-socket/issues/55

+3

STREAM- und DGRAM-Steckdosen funktionieren sehr unterschiedlich. Ein DGRAM-Sockel weiß nicht, ob etwas tatsächlich auf der anderen Seite zu hören ist. Es feuert einfach alles ab, was du ihm in den interstellaren Raum gibst. –

+1

Es sieht so aus, als wäre das 'MSG_DONTWAIT' Flag zu' send' wahrscheinlich relevant. Die Haskell-Schnittstelle leitet die Flags durch und definiert vermutlich die Konstanten irgendwo. Aber mein Verständnis dieser Dinge ist wahrscheinlich flacher als deins. – dfeuer

+0

Ein 'DGRAM'-Socket blockiert einen Sendevorgang nicht. Es legt einfach ein Paket auf das Netzwerk ab, und wenn der Puffer des Empfängers voll ist, wird das Paket verworfen. Es gibt keine Verbindung, also nichts zu blockieren. –

Antwort

2

Dieses Problem ist kein Haskell - es Verhalten auf Linux erwartet wird, wenn ohne Zuhörprozesses zwei UDP-Pakete an einen lokalen Host-Port sendet. Das folgende C-Programm:

#include <stdio.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <netinet/udp.h> 

int main() 
{ 
    int s = socket(AF_INET, SOCK_DGRAM, 0); 
    struct sockaddr_in dstaddr = { AF_INET, htons(6565), {htonl(0x7f000001)} }; 
    if (connect(s, (struct sockaddr*) &dstaddr, sizeof(dstaddr))) 
     perror("connect"); 
    if (send(s, "FOO", 3, 0) == -1) 
     perror("first send"); 
    if (send(s, "BAR", 3, 0) == -1) 
     perror("second send"); 
    return 0; 
} 

wird second send: Connection refused drucken, nichts unter der Annahme, hört auf localhost Port 6565.

Wenn Sie das tun irgendeine der folgenden - (i) senden einem nicht-lokalen Host, (ii) fallen die connect Anruf und ersetzen Sie die send s mit sendto s, oder (iii) senden Pakete an einen Port Wenn ein Prozess auf UDP-Pakete wartet, wird der Fehler nicht angezeigt.

Das Verhalten ist ein wenig komplex und nicht überall gut dokumentiert, obwohl die Manpages für Hinweis darauf.

Sie können die Diskussion in this Stack Overflow question hilfreich finden.

+0

Awesome Dank, würden Sie sagen, das ist, was passiert: der zweite 'send' Aufruf ist früh mit dem asynchronen Fehler vom ersten Aufruf (und so der dritte Aufruf löst keinen Fehler, aber der vierte Anruf, und so auf...)? Ich bin jetzt auf diesen Leitfaden gestoßen, den ich ab jetzt für diese Art von Fragen konsultiere: http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html – jberryman

+0

Ja, Ich glaube, das ist richtig: Der erste Anruf sendet tatsächlich ein UDP-Paket und gibt den Erfolg zurück. Intern erzeugt dies einen ICMP-unerreichbaren Fehler, bei dem der zweite Aufruf * stattdessen * ein UDP-Paket sendet, so dass der dritte Aufruf ein UDP-Paket senden und den Prozess wiederholen kann. Da dies die Loopback-Schnittstelle ist, ist es absolut zuverlässig - Senden, Fehler, Senden, Fehler. Auf einer echten Netzwerkschnittstelle sehen Sie etwas Ähnliches, aber weniger vorhersehbar. –

Verwandte Themen