2017-12-18 34 views
3

SOCK_DGRAMにメトリックを送信するコードがあります。このコードでは、別のデーモンがこれらのメッセージをリッスンして集約/プロキシします。ソケットを開くと、次のようになります。無期限にブロックしないでデータグラムソケットに送信

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

そして、現時点では、我々はそれに書き込むように:

send sock payload mempty 

私は上記の呼び出しが非常に長い(または非常に少なくともためにブロックされないようにします無期限にブロックされません)。しかし、私のUNIXソケットの理解はそれほど深くはなく、正確に理解するのが難しいです。sendブロック、内部を見てhereおよびhere

役立ちましたここに関連する質問があります:When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?

は、だから私の質問は、具体的には、次のとおりです。

  • 一般的なソケットの質問:私はこの実装sendで見るには、ビジー待機の後(ブロックしようとしています)バッファに空きがあるまで。このバッファは消費者にどのくらい正確に関係していますか? listen-ingデーモンが遅くなったり停止したりすると、sendが無期限にブロックされる可能性がありますか?
  • 私はむしろ中断し、決してブロックしない場合は、System.Socket.Unsafeの自分のフォークを作る必要がありますか、何か不足していますか?

私はここではLinuxに関心があります。

EDIT:また、どのようなおそらく私はこのすべてを始めましたが、私はメトリックコレクタが実行されていないとき、私のsend通話の他のすべてが上記スローする例外を「接続が拒否した」ことを見つけることです。それがなぜ、それとも、それが正常かどうかは、私が持っている別の質問です。

EDIT2

:私は socket-0.5.3.0を使用してい

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" 

:ここでは誰もがREPROを手助けしたい場合は、接続を拒否した問題を説明するための完全な例です。

EDIT3:これはなんとなくconnectの呼び出しによるものと思われます。 (最新socketsでのテスト):私はそれを理解したよう

{-# 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" 

我々は、デフォルトの送信アドレスを設定するには(少なくとも、基盤となるシステムコール)connectを使用することができるはずです。私はまだ図書館にconnectの実装を掘り下げていません。

私はこれをオープンしました:https://github.com/lpeterse/haskell-socket/issues/55

+3

STREAMソケットとDGRAMソケットの動作は大きく異なります。 DGRAMソケットは、実際に相手側で何かが聞こえているかどうかを知らず、気にもなりません。それは星空の空間にあなたが渡したものだけを発射します。 –

+1

'send'への' MSG_DONTWAIT'フラグが関連性が高いようです。 Haskellインターフェースはフラグを通し、おそらくどこかで定数を定義します。しかし、これらのことを私が理解することは、おそらくあなたのものよりも浅いでしょう。 – dfeuer

+0

'DGRAM'ソケットは送信をブロックしません。ネットワーク上にパケットをダンプするだけです。受信側のバッファがいっぱいになると、パケットは破棄されます。接続がないので、ブロックすることはありません。 –

答えて

2

これはHaskellの問題ではありません - ノーリスニングプロセスでlocalhostのポートへの2つのUDPパケットを送信するときに、それは、Linux上で動作予想されます。次のCプログラム:

#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; 
} 

は何もlocalhostのポート6565でリッスンしていないと仮定すると、second send: Connection refusedを印刷します。

あなたが行う場合は、次のいずれかの1 - (ⅰ)非ローカルホストに送信、(ⅱ)connectコールをドロップし、sendto秒でsend Sを交換、または(ⅲ)ポートにパケットを送信UDPパケットをリッスンするプロセスでは、エラーは発生しません。

動作は少し複雑で、マニュアルはudp(7)のヒントですが、どこにも記載されていません。

ディスカッションin this Stack Overflow questionが役に立ちます。

+0

恐ろしいことに、これは起こっていると言いますか?2番目の 'send'コールは、最初のコールからの非同期エラーで早期に失敗しています(3番目のコールではエラーは発生しませんが、に...)?私は今このガイドを見に行きました。これからは、これらの質問について相談していきます。http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html – jberryman

+0

はい、私は正しいと信じています:最初の呼び出しは実際にUDPパケットを送信し、成功を返します。内部的には、これによりICMP到達不能エラーが生成されます。このエラーは、2番目の呼び出しがUDPパケットを送信する代わりに*を報告するため、3番目の呼び出しはUDPパケットを送信して処理を繰り返すことができます。これはループバックインターフェイスなので、send、error、send、errorのすべてが信頼できます。実際のネットワークインターフェースでは、似たようなものですがあまり予測できないものがあります。 –

関連する問題