とりあえずマルチなechoサーバ成功っぽい
なんとかそれらしい動きをするようになりました、が…どうもすっきりしないコードになってしまった…
概要はこんなかんじ。
- 接続受付用のchannel(A)と、クライアントと通信している子スレッド群からの報告を受け取るchannel(B)を用意。
- その他、すべての子スレッド分のchannel(C)をリストにて管理。
- ch(A)からfdを受信すると、クライアント用の子スレッドを立ち上げる。当該子スレッド用のch(C)をリストに追加。
- 子スレッドがクライアントからデータを受信するとch(B)にデータを送信。
- ch(B)から受信した親スレッドは全てのch(C)にデータを送信。
- ch(C)から受信した子スレッドはクライアントに対してデータを送信。
コードはこんな感じ。
open Unix open Event type event = Conn of file_descr | Recv of string let rec loop f = f (); loop f let gone f = ignore (Thread.create loop f) let process socket report_ch = let my_ch = new_channel () in (* my_ch -> connection *) gone ( fun () -> let str = sync (receive my_ch) in ignore (write socket str 0 (String.length str)) ); (* connection -> report_ch *) let str = String.create 1024 in gone ( fun () -> let len = read socket str 0 (String.length str) in sync (send report_ch (Recv (String.sub str 0 len))) ); my_ch let get_server_sock () = let sa = ADDR_INET (inet_addr_any, 54321) in let ls = socket PF_INET SOCK_STREAM 0 in setsockopt ls SO_REUSEADDR true; bind ls sa; listen ls 5; ls let get_connect_ch ls = let ch = new_channel () in gone ( fun () -> sync (send ch (let s, _ = accept ls in Conn s)) ); ch let _ = let report_ch = new_channel () in let connect_ch = get_connect_ch (get_server_sock ()) in let ch_list = ref [] in loop ( fun () -> match Event.select [receive connect_ch; receive report_ch] with | Conn s -> let ch = process s report_ch in ch_list := ch :: !ch_list | Recv str -> List.iter ( fun ch -> sync (send ch str) ) !ch_list )
Event.selectに気がついて助かった、という感じです。
まぁ、一昨日よりはEventモジュールが理解できた気がするので、よかったかなぁ、と。