2016-09-24 1 views
3

읽을 것이 없을 때까지 소켓에서 데이터를 읽는 간단한 클라이언트에 대해 Lwt_unix 모듈을 사용하려고했습니다.OCaml : Lwt 및 비 차단 소켓

open Lwt 
open Unix 

(* ocamlfind ocamlc -o lwt_socket_client -package lwt,lwt.unix,unix -linkpkg -g lwt_socket_client.ml *) 
let host = Unix.inet_addr_loopback 
let port = 6600 

let create_socket() = 
    let sock = Lwt_unix.socket PF_INET SOCK_STREAM 0 in 
    Lwt_unix.set_blocking sock false; 
    sock 

let s_read sock maxlen = 
    let str = Bytes.create maxlen in 
    let rec _read sock acc = 
    Lwt.ignore_result(Lwt_io.write_line Lwt_io.stdout "_read"); 
    Lwt_unix.read sock str 0 maxlen >>= fun recvlen -> 
    Lwt.ignore_result(Lwt_io.write_line Lwt_io.stdout (string_of_int recvlen)); 
    if recvlen = 0 then Lwt.return (acc) 
    else _read sock (acc^(String.sub str 0 recvlen)) 
    in _read sock "" 

let socket_read sock = 
    Lwt.ignore_result(Lwt_unix.connect sock @@ ADDR_INET(host, port)); 
    s_read sock 1024 >>= fun answer -> 
    Lwt_io.write_line Lwt_io.stdout answer 

let() = 
    let sock = create_socket() in 
    Lwt_main.run (socket_read sock) 

나는 용어에서와이 예제를 시도하는 경우 :

echo "totoche" | netcat -l 127.0.0.1 -p 6600 

후 결과는 다음과 같습니다 일부는 Lwt 비 블로킹 소켓을 만들 수 있지만, 내 코드와 함께, 여전히 차단하고 있다는 얘기

./lwt_socket_client 
_read 
8 
_read 

어떤 블록 내가 Ctrl 키 + C을 명중 할 때까지.

내가 함께 모두 시도 :

Lwt_unix.set_blocking sock false; 

Lwt_unix.set_blocking sock true; 

이 라인이없는 과정의

,하지만 여전히 차단하고 있습니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까? 내 앞의 질문의 더 많은 정보에 대한

, 하나 OCaml non-blocking client socket

+1

주 :'ignore_result x; ... "는"그것을 기다리지 않고 백그라운드에서 수행 "을 의미합니다. 당신은 다음과 같이하자. x >> = fun() -> ...'(x가 끝날 때까지 기다린다.) –

답변

2

개념적으로, Lwt_unix.read항상 차단 LWT 스레드하지만 결코 블록 전체 프로세스 - 프로세스가 그 LWT 스레드를 기다리고, 실행하는 다른 LWT 스레드가없는 경우를 제외하고. Lwt_unix.set_blocking은이 동작에 영향을주지 않습니다. 기본 소켓의 설정을 변경하기 때문에 프로세스를 막지 않도록 Lwt가 내부적으로 사용하는 전략이 변경됩니다.

@ThomasLeonard에서 언급했듯이 nonblock을 수행하는 "관용적 인 Lwt"방법은 프로세스의 관점에서 Lwt_unix.read과 동시에 추가 Ltt 스레드를 실행하는 것입니다. 문제의 특정 코드에 관한


는 기본 read 시스템 호출은 기본 소켓이 비 블로킹 경우 (시스템에 따라) EAGAIN 또는 EWOULDBLOCK 실패하지만, 데이터를 사용할 수 없습니다 - 대신에 성공보다 소켓이 닫힌 것을 나타내는 0 바이트 읽기.

Unix.read은 이것을 예외 Unix.Unix_error Unix.EAGAIN (각각 Unix.Unix_error Unix.EWOULDBLOCK)로 변환합니다. 이 경우 Lwt_unix.readUnix.read을 재 시도합니다. 따라서 Lwt_unix.read을 사용하는 경우 이런 방식으로 실패하는 비 차단 읽기에 직접 (현재) 응답 할 수 없습니다.당신이 원하는 경우

것은/Lwt_unix로 생성 된 소켓이 수준의 제어가 필요합니다, 당신은이 작업을 수행 할 수 있습니다

Lwt_unix.set_blocking sock false; 

try 
    Unix.read (Lwt_unix.unix_file_descr sock) str 0 maxlen 
with Unix.Unix_error (Unix.EAGAIN | Unix.EWOULDBLOCK) -> 
    (* Handle no data available. *) 

편집 : 또한, @ThomasLeonard에서 언급 한 바와 같이, ignore_result의 일부 사용 대신 코드에서 e >>= fun() -> e'이되어야합니다. 이렇게하면 Lwt가 e이 완료 될 때까지 기다렸다가 e'을 실행합니다. 특히 Lwt_unix.connect에 대해이 작업을 수행해야합니다.

1

OS의 X에, 내가 얻을 : 보인다

> ./lwt_socket_client 
_read 
8 
_read 
0 
totoche 

당신이 요구하는지합니다. 그러나 커널이 작업을 스케줄하는 방법에 따라 다르므로이 동작이 유용하다고 확신하지 못합니다. 너 뭐하려고? 예를 들어 원하는 경우 입력을 기다리는 동안 다른 작업을 수행하고, (차단) 읽기와 병렬로 두 번째 Lt 스레드를 실행하십시오.

+0

내가 게시 한 코드를 복사 했는가? 내가 ArchLinux x86_64에 있었기 때문에 작동하지 않았다. – cedlemo

+0

이유 : mpd 클라이언트에서 작업을 시도했는데, mpd 연결에서 _the 연결이 시작되고 클라이언트에 의해 종료되었습니다. 클라이언트가 연결되어 mpd에서 상태 메시지를 읽습니다. 그런 다음 클라이언트가 connection_을 닫을 때까지 명령을 보내고 결과를 얻는다. 내 문제는 읽는 중이다. 나는 읽기를 멈추고 mpd 메시지를 반환한다. 이것을 위해 ** 두 가지 이벤트 **를 사용할 수있다. ** 소켓에 남은 데이터 없음 ** (이 코드로 테스트 할 가능성이 있음) 또는 ** 모든 mpd 서버 메시지가 "\ n"에 의해 종료되었다고 말하는 mpd protocole에 의존합니다. 아직 테스트되지 않음) – cedlemo

+0

"no data available"은 아무 것도 알려주지 않고 커널이 다음 비트를 제공 할 준비가되지 않았다는 것을 알려줍니다.이 경우에 할 수있는 유일한 일은'read' 다시 더 많은 메시지가 도착했는지 다시 한번 확인하십시오. 'read'는 가능한 한 즉시 항상 리턴 할 것이므로, 완전한 메시지를 처리하십시오 'read'가 돌아올 때마다 당신의 버퍼에서 할 수있는 것처럼 처리합니다. 비 차단 읽기 0을 가정하면 테스트 중에 완전한 메시지가 작동하지만 결국 실패 할 것임을 의미합니다. –