2017-12-18 6 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 

을 나는 위의 적어도 (또는 아주 오랫동안 차단하지 않습니다 전화를 확인하려면 무한히 차단하지는 않습니다.)하지만 유닉스 소켓에 대한 이해는 그리 깊이가 없으며 정확히 send 블록을 이해하고, 내부를 보면서 문제가 발생했습니다. herehere.

도움이되었다 여기에 관련된 질문이있다 : When a non-blocking send() only transfers partial data, can we assume it would return EWOULDBLOCK the next call?

그래서 제 질문은 구체적으로 다음과 같습니다

  • 일반 소켓 질문 : send 차단할 것입니다이 구현에서 볼 (바쁜 대기 후) 버퍼에 공간이있을 때까지. 이 버퍼가 소비자와 얼마나 관련이 있습니까? listening 데몬이 느리거나 멈 추면 send이 무기한 차단 될 수 있습니까?
  • 내가 중단하고 결코 차단하지 않으려면 System.Socket.Unsafe의 자체 포크를 만들어야합니까, 아니면 뭔가 빠졌습니까?

저는 여기서 리눅스에만 관심이 있습니다.

편집 : 또한, 어떤 아마 내가이 모든 시작있어 내가 메트릭 콜렉터가 실행되지 않을 때, 내 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" 

: 여기 사람이 생식 도움을 좋아하면 연결 거부 문제를 보여주는 완벽한 예입니다.

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

이 하스켈 문제가되지 않습니다 - 아니 청취 과정을 로컬 호스트 포트에 두 개의 UDP 패킷을 보낼 때 그것은 리눅스에서 예상 된 동작입니다. 다음 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; 
} 

아무것도 없다고 가정 second send: Connection refused 인쇄 할 수는 로컬 호스트 포트 6565에서 수신 대기합니다.

당신이 할 경우, 다음 중 어느 하나에 - (ⅰ) 로컬이 아닌 호스트로 보내, (ⅱ) connect 전화를 드롭 sendto들과 함께 send의 교체, 또는 (III) 포트에 패킷을 전송 UDP 패킷을 수신하는 프로세스로 - 그러면 오류가 발생하지 않습니다.

동작은 약간 복잡하고 udp(7)에 대한 맨 페이지가 있지만 힌트는 있지만 어디에도 잘 설명되어 있지 않습니다.

in this Stack Overflow question 토론에 도움이 될 수 있습니다.

+0

두 번째'send' 호출은 첫 번째 호출의 비동기 오류로 인해 일찍 실패합니다. (세 번째 호출은 오류를 발생시키지 않지만 네 번째 호출은 그렇게됩니다. 에...)? 나는 지금부터이 가이드를 보았고, 앞으로도 이런 종류의 질문을 위해 컨설팅을 할 것이다 : http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html – jberryman

+0

예, 나는 그것이 정확하다고 믿는다 : 첫 번째 호출은 실제로 UDP 패킷을 보내고 성공을 반환한다. 내부적으로 이것은 ICMP 도달 할 수없는 오류를 생성하며 두 번째 호출은 UDP 패킷 전송 대신 *를보고하므로 세 번째 호출은 UDP 패킷을 보내 프로세스를 반복 할 수 있습니다. 이것은 루프백 인터페이스이기 때문에 send, error, send, error와 같이 완전히 신뢰할 수 있습니다. 실제 네트워크 인터페이스에서는 비슷하지만 예측하기 어려운 것을 볼 수 있습니다. –

관련 문제