OCaml:Lwt和非阻塞套接字

时间:2016-09-24 10:18:46

标签: ocaml lwt

我想为一个简单的客户端尝试Lwt_unix模块,该客户端读取套接字中的数据,直到没有任何内容可读。有些人告诉我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_socket_client
_read
8
_read

在我点击 Ctrl + c 之前的哪个区块。

我曾尝试过:

Lwt_unix.set_blocking sock false;

Lwt_unix.set_blocking sock true;

当然没有这条线,但它仍在阻挡。我做错了什么?

有关更多信息,我之前的一个问题是: OCaml non-blocking client socket

2 个答案:

答案 0 :(得分:3)

从概念上讲,Lwt_unix.read 总是阻止Lwt线程,但永远阻止整个过程 - 除非进程正在等待那个Lwt线程,并且有没有其他Lwt线程可以运行。 Lwt_unix.set_blocking不会影响此行为。它只是更改底层套接字上的设置,因此也就是Lwt内部使用的策略,以避免阻塞进程。

所以,正如@ThomasLeonard所说,"惯用的Lwt"执行非阻塞read的方法(从流程的角度来看)只是与Lwt_unix.read同时运行其他Lwt线程。

关于问题中的特定代码,如果底层套接字是非阻塞的,则基础read系统调用将失败并显示EAGAINEWOULDBLOCK(取决于系统)数据是可用的 - 而不是成功读取零字节,这表明套接字已被关闭。

Unix.read将此转换为异常Unix.Unix_error Unix.EAGAIN(分别为Unix.Unix_error Unix.EWOULDBLOCK)。在这种情况下,Lwt_unix.read重试Unix.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 :(得分:1)

在OS X上,我得到:

FROM custom_elasticsearch_1

RUN plugin install royrusso/elasticsearch-HQ

这似乎是你所要求的。但是,我不确定这种行为是否有用,因为它取决于内核如何安排工作。你想做什么?如果你想要,例如在等待输入时继续使用其他东西,只需与(阻塞)读取并行运行第二个Lwt线程。