Using streams for socket server <--> client communication

Hey everyone!
I am currently trying to build a TCP socket server, which has a conversation with a client via the unrepl protocol. So far I got the initial handshake working, so the server is saying “hello” on each client connect.

After the hello message, I wait for the client to pass me an ocaml program as a stream of characters… after the termination character (i think with netcat ^D is using \x00 to terminate?) the server should parse the data and eval the program.

So far so good, I got it to work with Lwt_io.readlines in_chan on the first call… now I want to make this recursive. After evaling, the server should again wait for a stream of data until the next termination character.

So far I got this this code (doesn’t work as expected yet):

let rec loop in_chan out_chan =
  try
    (* Wait until EOF and read all lines separated with \n etc. *)
    let in_stream = Lwt_io.read_lines in_chan in
    let%lwt program = Lwt_stream.fold (fun line acc -> acc ^ (Format.sprintf "\n%s" line)) in_stream "" in

    let%lwt result = Eval.eval_lwt program in

    let%lwt () = match result with
      | Error msg -> send_exn out_chan msg
      | _ ->
          Lwt.join [
              send_eval out_chan Unrepl.NoPayload;
              send_prompt out_chan;
              print_result result;
            ]
    in
      loop in_chan out_chan
  with _ ->
    Lwt.return_unit

This code will first do the right thing on the first try (evaling the passed program), but after that it will loop indefinitely, getting a progam = "".

I guess this has something to do with the mechanics of Lwt_io.readlines… does anyone know how to design this in a blocking manner?

Thanks :slight_smile:

1 Like

@ryyppy, since you are using the same in_chan recursively, the second time you call Lwt_io.read_lines on it, it is already at EOF. So, nothing is read. As a result, the Lwt_stream.fold call just returns the initial "" that you are passing into it. Instead of using read_lines, you should write a custom function that reads the channel until \x00. You may need some kind of buffer to do this reasonably.

Separately, you should use try%lwt instead of try when you have let%lwt inside the try-block.

1 Like