Reputation: 39
I'm developing a client-server application in OCaml using the high-level network connection functions available in OCaml Unix library, following the steps available at https://caml.inria.fr/pub/docs/oreilly-book/html/book-ora187.html. These functions are:
val open_connection : sockaddr -> in_channel * out_channel
val shutdown_connection : in_channel -> unit
val establish_server : (in_channel -> out_channel -> unit) -> sockaddr -> unit
I'm able to successfully build the client and verifier but I cannot terminate the server using the exit
OCaml function.
My (minimal) server code is the following:
let handle_service ic oc =
try while true do
...
if ... then raise Finish_interaction
done ;
with
| Finish_interaction -> raise Sys.Break
| _ -> ...
let main_server serv_fun =
if Array.length Sys.argv < 4 then ...
else try
let port = int_of_string Sys.argv.(1) in
...
let my_address = Unix.inet_addr_loopback in
Unix.establish_server serv_fun (Unix.ADDR_INET(my_address, port))
with
| Sys.Break -> exit 0 (* PROGRAM DOES NOT TERMINATE *)
| _ -> ...
let go_server () =
Unix.handle_unix_error main_server handle_service ;;
go_server ()
I can successfully catch the Sys.Break
exception, but the exit 0
code after catching that exception does nothing and the server just keeps running and waiting for another client connection.
OCaml documentation says the following regarding establish_server
:
The function Unix.establish_server never returns normally.
I don't know if this implies that I can never terminate the program without user interaction (via Ctrl + C, for example).
In a nutshell, how can I terminate my server? The client does terminate after shutdown_connection
but the server keeps waiting for incoming connections. BTW, I'm compiling my code using OCamlbuild.
Upvotes: 0
Views: 429
Reputation: 39
I managed to solve my problem, without having the need to send signals between processes.
I tweaked the establish_server
function so that it doesn't create a loop that answers all incoming connections. By removing the loop, it will only answer one incoming connection, and then it simply ends its execution.
Here is the code for the new establish_server
function:
let establish_server server_fun sockaddr =
let domain = Unix.domain_of_sockaddr sockaddr in
let sock = Unix.socket domain Unix.SOCK_STREAM 0
in Unix.bind sock sockaddr ;
Unix.listen sock 3;
(*while true do*)
let (s, caller) = Unix.accept sock
in match Unix.fork() with
0 -> if Unix.fork() <> 0 then exit 0 ;
let inchan = Unix.in_channel_of_descr s
and outchan = Unix.out_channel_of_descr s
in server_fun inchan outchan ;
close_in inchan ;
close_out outchan ;
exit 0
| id -> Unix.close s; ignore(Unix.waitpid [] id)
(*done ;;*)
Removing the two commented lines gives you the original version of it.
Thanks everyone for the answers!
Upvotes: 0
Reputation: 4939
From the documentation of Unix.establish_server:
A new process is created for each connection
I recommend printing the process IDs (Unix.getpid ()
) to make sure the process calling exit
is the one you're expecting (the parent).
Another thing you can check is that the program is not stuck in the execution of an at_exit
callback. For example, the following program enters an infinite loop during the call to exit
:
let () =
at_exit (fun () -> while true do () done);
print_endline "all is well!";
exit 0
(probably not the problem you're having but could be useful to future visitors)
Upvotes: 1