2016-09-24 5 views
3

私はLwt_unixモジュールを、何も読まない限りソケットでデータを読み取る単純なクライアントに使ってみたかったのです。その後、結果はOCaml:Lwtと非ブロッキングソケット

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

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) 

を私は用語にしてこの例を試す場合は、次のいくつかはLwtが非ブロッキングソケットを作成しますが、私のコードで、それはまだブロックされていることを教えてくれました

./lwt_socket_client 
_read 
8 
_read 

どのブロック私はCはCtrl +を打つまで。

私は両方試してみました:

Lwt_unix.set_blocking sock false; 

Lwt_unix.set_blocking sock true; 

と、このラインのないコースの

が、それはまだブロックしています。私は間違って何をしていますか?より多くの情報については

、私の前の質問の1:OS X上で 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が述べたように、(プロセスの観点から)ノンブロッキングを行う「慣用的なLwt」方法は、単にLwt_unix.readと同時に追加のLwtスレッドを実行するだけです。問題の特定のコードに関する


は、基礎となる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. *) 

EDIT:また、@ThomasLeonardで述べたように、ignore_resultの一部の使用をあなたのコードではおそらくe >>= fun() -> e'になるはずです。これにより、e'を実行する前に、Lwtはeが完了するのを待たせます。特に、Lwt_unix.connectでこれを行う必要があります。

1

、私が取得:

> ./lwt_socket_client 
_read 
8 
_read 
0 
totoche 

あなたが求めているもののようですどの。しかし、カーネルがどのように作業をスケジュールするかによって、この動作が有用であるかどうかはわかりません。あなたは何をしようとしているのですか?たとえば、入力を待つ間に何か他のものに乗って、ちょうど2番目のLtスレッドを(ブロッキング)読み込みと並行して実行してください。

+0

投稿したコードをコピーしましたか? – cedlemo

+0

理由:私はmpdクライアントで接続しようとしています_接続が開始され、クライアントによって終了されました。クライアントが接続し、ステータスメッセージをmpdから読み込みました。クライアントが接続を閉じるまで、コマンドを送信し、結果を取得します。私の問題は読書中です。私は読書をやめてmpdメッセージを返します。 **ソケットに残っているデータは残っていません。(これは私がこのコードでテストする可能性があります)、または**私はmpdのprotocoleに依存しています**すべてのmpdサーバーメッセージは "\ n"まだテストされていません)。 – cedlemo

+0

"利用可能なデータがありません"というメッセージは役に立ちません。カーネルはすぐに次のビットを与える準備ができていません。 'read'はできるだけ早くすぐに戻りますので、多くの完全なメッセージを処理するだけです'read'が返ってくるたびに、あなたのバッファからできるだけ早く。非ブロッキングの0の読み込みは、テスト中に完全なメッセージが動作する可能性があることを意味しますが、最終的には失敗します。 –

関連する問題