読者です 読者をやめる 読者になる 読者になる

とりあえずマルチなechoサーバ成功っぽい

ocaml

なんとかそれらしい動きをするようになりました、が…どうもすっきりしないコードになってしまった…

概要はこんなかんじ。

  • 接続受付用の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モジュールが理解できた気がするので、よかったかなぁ、と。