ocamlclient-serverircocaml-lwt

Problem with retransmitting stdin to a server in OCaml


I'm trying to write an IRC inspired server along with a client capable of communicating with it as a part of my uni class. I already wrote a server with which i can successfully interact using tools such as telnet. I'm having trouble with writing a client though, specifically with sending messages from stdin to the server. Module that i use for sending/receiving messages looks as follows:

let (>>=) = Lwt.bind

type t = 
  { user_input : Lwt_io.input_channel;
    server_input : Lwt_io.input_channel;
    output : Lwt_io.output_channel }

let make fd =
  { user_input = Lwt_io.stdin;
    server_input = Lwt_io.of_fd ~mode:Lwt_io.input fd;
    output = Lwt_io.of_fd ~mode:Lwt_io.output fd }

let send conn msg =
  Lwt_io.write conn.output msg >>= fun _ ->
    Lwt_io.flush conn.output
      
let receive input_ch =
 Lwt_io.read_line input_ch
 >>= fun line ->
   Lwt.return line

Funny enough, i know that it almost works properly thanks to some classic print_endline debugging techniques (I logged messages sent and received by the server). The problem is, when i type something in my terminal, it doesn't go through to the server until i press ctrl+c. Example session looks as follows:

  1. I launch the server and successfully connect to it using the client that i wrote
  2. The server asks to choose my nickname
  3. In my client, i type a nick and press enter
  4. The server doesn't signal receiving any messages
  5. I press ctrl+c in my client session
  6. The moment the client closes the server shows that it received all characters typed into the client before shutting it down with ctrl+c

Based on that i assume the message doesn't actually get sent through the socket until i press ctrl+c for some unknown reason.

server responses and corresponding client session for some more reference

I was wondering if it can somehow be caused by the way i read lines using Lwt_io.read_line, something about it maybe not recognizing newline characters and how to fix that (So it works as intended, that is sending typed message to the server after pressing Enter). Additionally here's the code responsible for the main loop of the client:

let rec handle_user conn () =
  let open Connection in 
  receive conn.user_input
  >>= fun msg ->
    send conn msg
  >>= handle_user conn

let rec handle_server conn () = 
  let open Connection in 
  receive conn.server_input
  >>= fun msg ->
    Lwt_io.write Lwt_io.stdout msg 
  >>= handle_server conn 

let handle_connection conn () = 
  Lwt.join [
    handle_user conn ();
    handle_server conn ()
  ]

handle_connection conn () is launched from the main function using Lwt_main.run after doing some standard stuff on sockets. I feel like I have scoured half of the Internet trying to come up with a solution and yet I couldn't find anything and it completely blocks my further progress working on the project.

TL:DR Tried: Sending a message to the server after typing it in my client session and pressing Enter What i expected: Response from the server signaling that it in fact received a message along with it's content What actually happened: The message didn't go through until closing the client with ctrl+c after which the server signaled receiving all messages typed so far but glued together, ignoring newline characters


Solution

  • As far as I can see (without any way to reproduce the error), the issue is that Lwt_io.read_line returns the line without the newline characters as documented at https://ocsigen.org/lwt/5.5.0/api/Lwt_io#VALread_line. Thus,

      receive conn.user_input
      >>= send conn
    

    does not send any newlines, and the server has no way to know that the current message ended before the end of the connection.