Le client/serveur hello

Fichier d'archive compressé contenant l'ensemble des fichiers sources : hello.tar.gz


Version de base

Le serveur : attend une connexion et renvoie un message du genre (26) Hello machine.client (772) au client qui s'est connecté depuis la machine machine.client
Commande : hello1d numport&numport est le numéro de port du service
Code :
(* ================================================================== *)
(* == POD 98-99 --                                                    *)
(* ------------------------------------------------------------------ *)
(* == Client/Serveur via Internet : Hello v.01                        *)
(* La version de base du serveur                                      *)
(* ------------------------------------------------------------------ *)
(*   ocamlc -custom unix.cma hello1d.ml -o hello1d -cclib -lunix      *)
(* ================================================================== *)

open Unix
;;

(* == val get_s_port : string -> int                                  *)
(* Calcule le numero de port du service en fonction de son argument   *)
let get_s_port p_num =
 try int_of_string p_num with
  Failure("int_of_string") -> failwith("incorrect port number "^p_num)
;;

(* == val message : Unix.sockaddr -> string                           *)
(* Compose le message de reponse a la connection d'un client          *)
let message = function
 ADDR_UNIX _ -> "\n Hum, something's strange with this UNIX socket\n"
|ADDR_INET(cl_inet_addr,_)
 -> ("Hello "^((gethostbyaddr cl_inet_addr).h_name)
    ^" ("^(string_of_inet_addr cl_inet_addr)^")\n")
;;

(* == val server : unit -> unit                                       *)
(* Le serveur                                                         *)
let server () =
 (* Si pas d'argument : erreur    *)
 if Array.length Sys.argv < 2 then
   begin
    Printf.eprintf "Usage : hello1d portnumber\n"
   end
 (* sinon *)
 else
  (* Lecture du numero de port du service *)
  let port = get_s_port Sys.argv.(1) in
  (* Creation de la socket de service *)
  let sock = socket PF_INET SOCK_STREAM 0 in
  (* Adresse du serveur *)
  let h_addr = (gethostbyname (gethostname())).h_addr_list.(0) in
  (* Adresse de la socket *)
  let sock_addr = ADDR_INET(h_addr, port) in
   begin
    (* Liaison socket/adresse *)
    bind sock sock_addr;
    (* Mise en attente de connection *)
    listen sock 5;
    (* Boucle de service *)
    while true do
     (* On accepte une connexion *)
     let (cl_sock, cl_sock_addr) = accept sock in
      (* On cree un processus de traitement de la connexion (le fils) *)
      if fork() == 0 then
       begin
        (* Hack Unix :-) *)
        if fork()<>0 then exit 0;
        (* Emission du message de reponse *)
        (let m = message cl_sock_addr in
          write cl_sock m 0 (String.length m));
        (* Fermeture de la socket client *)
        close cl_sock;
        (* Fin du processus de traitement de connexion *)
        exit 0
       end
      else (* Le pere *)
       (* Attente fin de traitement connexion (par le fils) *)
       let _ = wait() in close cl_sock
     done
    end
;;

(* == Programme principal *)
try
 server()
with
 Unix_error(c,f,x)
 -> Printf.eprintf "\n %s in %s %s\n" (error_message c) f x
|Failure x
 -> Printf.eprintf "\n %s \n" x
;;

Le client : demande une connexion et attend une réponse qu'il affiche sur sa sortie standard
Commande : hello1 serveur.domaine numportserveur.domaine est l'adresse IP du serveur et numport le numéro de port du service
Code :

(* ================================================================== *)
(* == POD 98-99 --                                                    *)
(* ------------------------------------------------------------------ *)
(* == Client/Serveur via Internet : Hello v.01                        *)
(* La version de base du client                                       *)
(* ------------------------------------------------------------------ *)
(*   ocamlc -custom unix.cma hello1.ml -o hello1 -cclib -lunix        *)
(* ================================================================== *)

open Unix
;;

(* == val get_in_addr : string -> Unix.inet_addr                      *)
(* Calcule l'adresse IP du serveur en fonction de son argument        *)
let get_in_addr host = 
 try 
  (* on essaie 'machine.domaine' *)
  (gethostbyname host).h_addr_list.(0)
 with 
  Not_found 
  -> try 
      (* sinon : x.y.z.w *)
      inet_addr_of_string host       
     with
      Failure "inet_addr_of_string" -> failwith("unknown host "^host)
;;

(* == val get_s_port : string -> int                                  *)
(* Calcule le numero de port du service en fonction de son argument   *)
let get_s_port port =
 try int_of_string port with
  Failure("int_of_string") -> failwith("incorrect port number "^port)
;;

(* == val client : unit -> unit                                       *)
(* Le client                                                          *)
let client () =
 (* Si pas assez d'arguments : erreur    *)
 if Array.length Sys.argv < 3 then
   begin
    Printf.eprintf "Usage : hello1 hostname portnumber\n"
   end
 (* sinon *)
 else
  (* Adresse du serveur (1ier argument) *)
  let in_addr = get_in_addr Sys.argv.(1) in
  (* Port de connexion (2nd argument) *)
  let s_port = get_s_port Sys.argv.(2) in
  (* Creation de la socket de service *)
  let sock = socket PF_INET SOCK_STREAM 0 in
   begin
    (* Connexion *)
    connect sock (ADDR_INET(in_addr, s_port));
     (* Lecture et affichage de la reponse *)
     let r = input_line (in_channel_of_descr sock) in
      Printf.printf "(%d) %s\n" (String.length r) r;
      flush (out_channel_of_descr stdout)
   end
;;

(* == Programme principal *)
try
 client()
with
 Unix_error(c,f,x)
 -> Printf.eprintf "\n %s in %s %s\n" (error_message c) f x
;;

Un client avec délai d'attente

Le client : demande une connexion et attend une réponse dans un certain délai. Il affiche soit le message reçu soit le message Terminated after timeout 5 sec.
Commande : hello2 serveur.domaine numportserveur.domaine est l'adresse IP du serveur et numport le numéro de port du service
Code :
(* ================================================================== *)
(* == POD 98-99 --                                                    *)
(* ------------------------------------------------------------------ *)
(* == Client/Serveur via Internet : Hello v.02                        *)
(* La version du client avec delai d'attente pour la reponse          *)
(* ------------------------------------------------------------------ *)
(*   ocamlc -custom unix.cma hello2.ml -o hello2 -cclib -lunix        *)
(* ================================================================== *)

open Unix
;;

(* == val get_in_addr : string -> Unix.inet_addr                      *)
(* Calcule l'adresse IP du serveur en fonction de son argument        *)
let get_in_addr host = 
 try 
  (* on essaie 'machine.domaine' *)
  (gethostbyname host).h_addr_list.(0)
 with 
  Not_found 
  -> try 
      (* sinon : x.y.z.w *)
      inet_addr_of_string host       
     with
      Failure "inet_addr_of_string" -> failwith("unknown host "^host)
;;

(* == val get_s_port : string -> int                                  *)
(* Calcule le numero de port du service en fonction de son argument   *)
let get_s_port port =
 try int_of_string port with
  Failure("int_of_string") -> failwith("incorrect port number "^port)
;;

(* == val client : unit -> unit                                       *)
(* Le client                                                          *)
let client () =
 (* Si pas assez d'arguments : erreur    *)
 if Array.length Sys.argv < 3 then
   begin
    Printf.eprintf "Usage : hello2 hostname portnumber\n"
   end
 (* sinon *)
 else
  (* Delai maximal d'attente *)
  let timeout = 5 in
  (* Adresse du serveur (1ier argument) *)
  let in_addr = get_in_addr Sys.argv.(1) in
  (* Port de connexion (2nd argument) *)
  let s_port = get_s_port Sys.argv.(2) in
  (* Creation de la socket de service *)
  let sock = socket PF_INET SOCK_STREAM 0 in
    (* On cree un processus de d'attente de la reponse (le fils) *)
    if fork() = 0 then
     begin
      (* On declenche le compte a rebours *)
      alarm timeout;
      (* Connexion *)
      connect sock (ADDR_INET(in_addr, s_port));
      (* Lecture et affichage de la reponse *)
      let r = input_line (in_channel_of_descr sock) in
        (Printf.printf "(%d) %s\n" (String.length r) r;
         flush (out_channel_of_descr stdout));
      (* Fermeture de la socket *)
      close sock;
      (* Fin du traitement *)
      exit 0
     end
    else (* Le pere *)
     begin
      (* Fermeture de la socket *)
      close sock;
      (* Attente et analyse fin du fils *)
      (match wait() with
        (* Fin normale *)
        (_,WEXITED 0) -> ()
        (* Fin hors delai *)
       |(_,WSIGNALED 14) -> Printf.printf"Terminated after timeout %d sec.\n" timeout
        (* Autres cas *)
       |_ -> Printf.eprintf"Unexpected terminaison\n")
     end
;;

(* == Programme principal *)
try
 client()
with
 Unix_error(c,f,x)
 -> Printf.eprintf "\n %s in %s %s\n" (error_message c) f x
;;

Version avec acquittement

Le serveur : attend une connexion, renvoie un message du genre (26) Hello machine.client (772) au client qui s'est connecté depuis la machine machine.client et attend un acquittement du client
Commande : hello2d numport&numport est le numéro de port du service
Code :
(* ================================================================== *)
(* == POD 98-99 --                                                    *)
(* ------------------------------------------------------------------ *)
(* == Client/Serveur via Internet : Hello v.02                        *)
(* La version du serveur avec attente d'acquitement                   *)
(* ------------------------------------------------------------------ *)
(*   ocamlc -custom unix.cma hello2d.ml -o hello2d -cclib -lunix      *)
(* ================================================================== *)

open Unix
;;

(* == val get_s_port : string -> int                                  *)
(* Calcule le numero de port du service en fonction de son argument   *)
let get_s_port p_num =
 try int_of_string p_num with
  Failure("int_of_string") -> failwith("incorrect port number "^p_num)
;;

(* == val message : Unix.sockaddr -> string                           *)
(* Compose le message de reponse a la connection d'un client          *)
let message = function
 ADDR_UNIX _ -> "\n Hum, something's strange with this UNIX socket\n"
|ADDR_INET(cl_inet_addr,_)
 -> ("Hello "^((gethostbyaddr cl_inet_addr).h_name)
    ^" ("^(string_of_inet_addr cl_inet_addr)^")\n")
;;

(* == val server : unit -> unit                                       *)
(* Le serveur                                                         *)
let server () =
 (* Si pas d'argument : erreur    *)
 if Array.length Sys.argv < 2 then
   begin
    Printf.eprintf "Usage : hello2d portnumber\n"
   end
 (* sinon *)
 else
  (* Lecture du numero de port du service *)
  let port = get_s_port Sys.argv.(1) in
  (* Creation de la socket de service *)
  let sock = socket PF_INET SOCK_STREAM 0 in
  (* Adresse du serveur *)
  let h_addr = (gethostbyname (gethostname())).h_addr_list.(0) in
  (* Adresse de la socket *)
  let sock_addr = ADDR_INET(h_addr, port) in
   begin
    (* Liaison socket/adresse *)
    bind sock sock_addr;
    (* Mise en attente de connection *)
    listen sock 5;
    (* Boucle de service *)
    while true do
     (* On accepte une connexion *)
     let (cl_sock, cl_sock_addr) = accept sock in
      (* On cree un processus de traitement de la connexion (le fils) *)
      if fork() == 0 then
       begin
        (* Hack Unix :-) *)
        if fork()<>0 then exit 0;
        (* Emission du message de reponse *)
        (let m = message cl_sock_addr in
          write cl_sock m 0 (String.length m));
        (* Lecture de l'acquitement *)
        (let r = String.make 2 '\000' in
          read cl_sock r 0 2);
        (* Fermeture de la socket client *)
        close cl_sock;
        (* Fin du processus de traitement de connexion *)
        exit 0
       end
      else (* Le pere *)
       (* Attente fin de traitement connexion (par le fils) *)
       let _ = wait() in close cl_sock
     done
    end
;;

(* == Programme principal *)
try
 server()
with
 Unix_error(c,f,x)
 -> Printf.eprintf "\n %s in %s %s\n" (error_message c) f x
|Failure x
 -> Printf.eprintf "\n %s \n" x
;;

Un client avec délai d'attente et acquittement

Le client : demande une connexion et attend une réponse dans un certain délai. Lorsqu'il reçoit une réponse, il émet un acquittement. Il affiche soit le message reçu soit le message Terminated after timeout 5 sec.
Commande : hello3 serveur.domaine numportserveur.domaine est l'adresse IP du serveur et numport le numéro de port du service
Code :
(* ================================================================== *)
(* == POD 98-99 --                                                    *)
(* ------------------------------------------------------------------ *)
(* == Client/Serveur via Internet : Hello v.03                        *)
(* La version du client avec delai d'attente pour la reponse et       *)
(* acquittement                                                       *)
(* ------------------------------------------------------------------ *)
(*   ocamlc -custom unix.cma hello3.ml -o hello3 -cclib -lunix        *)
(* ================================================================== *)

open Unix
;;

(* == val get_in_addr : string -> Unix.inet_addr                      *)
(* Calcule l'adresse IP du serveur en fonction de son argument        *)
let get_in_addr host = 
 try 
  (* on essaie 'machine.domaine' *)
  (gethostbyname host).h_addr_list.(0)
 with 
  Not_found 
  -> try 
      (* sinon : x.y.z.w *)
      inet_addr_of_string host       
     with
      Failure "inet_addr_of_string" -> failwith("unknown host "^host)
;;

(* == val get_s_port : string -> int                                  *)
(* Calcule le numero de port du service en fonction de son argument   *)
let get_s_port port =
 try int_of_string port with
  Failure("int_of_string") -> failwith("incorrect port number "^port)
;;

(* == val client : unit -> unit                                       *)
(* Le client                                                          *)
let client () =
 (* Si pas assez d'arguments : erreur    *)
 if Array.length Sys.argv < 3 then
   begin
    Printf.eprintf "Usage : hello2 hostname portnumber\n"
   end
 (* sinon *)
 else
  (* Delai maximal d'attente *)
  let timeout = 5 in
  (* Adresse du serveur (1ier argument) *)
  let in_addr = get_in_addr Sys.argv.(1) in
  (* Port de connexion (2nd argument) *)
  let s_port = get_s_port Sys.argv.(2) in
  (* Creation de la socket de service *)
  let sock = socket PF_INET SOCK_STREAM 0 in
    (* Processus de d'attente et traitement de la reponse (le fils) *)
    if fork() = 0 then
     begin
      (* On declenche le compte a rebours *)
      alarm timeout;
      (* Connexion *)
      connect sock (ADDR_INET(in_addr, s_port));
      (* Lecture et affichage de la reponse *)
      let r = input_line (in_channel_of_descr sock) in
        (Printf.printf "(%d) %s\n" (String.length r) r;
         flush (out_channel_of_descr stdout));
      (* Emission de l'acquitement *)
      write sock "OK" 0 2;
      (* Fermeture de la socket *)
      close sock;
      (* Fin du traitement *)
      exit 0
     end
    else (* Le pere *)
     begin
      (* Fermeture de la socket *)
      close sock;
      (* Attente et analyse fin du fils *)
      (match wait() with
        (* Fin normale *)
        (_,WEXITED 0) -> ()
         (* Fin hors delai *)
      |(_,WSIGNALED 14) -> Printf.printf"Terminated after timeout %d sec.\n" timeout
        (* Autres cas *)
       |_ -> Printf.eprintf"Unexpected terminaison\n")
     end
;;

(* == Programme principal *)
try
 client()
with
 Unix_error(c,f,x)
 -> Printf.eprintf "\n %s in %s %s\n" (error_message c) f x
;;

Version avec trace des connexions

Le serveur : attend une connexion, renvoie un message du genre (26) Hello machine.client (772) au client qui s'est connecté depuis la machine machine.client et attend un acquittement du client. Il produit de plus une trace des connexions et des réceptions d'acquittement dans un fichier hello.log
Commande : hello3d numport&numport est le numéro de port du service
Code :
(* ================================================================== *)
(* == POD 98-99 --                                                    *)
(* ------------------------------------------------------------------ *)
(* == Client/Serveur via Internet : Hello v.03                        *)
(* La version du serveur avec attente d'acquitement et ecriture d'une *)
(* trace dans un fichier 'hello.log'                                  *)
(* ------------------------------------------------------------------ *)
(*   ocamlc -custom unix.cma hello3d.ml -o hello3d -cclib -lunix      *)
(* ================================================================== *)

open Unix

(* == val get_s_port : string -> int                                  *)
(* Calcule le numero de port du service en fonction de son argument   *)
let get_s_port p_num =
 try int_of_string p_num with
  Failure("int_of_string") -> failwith("incorrect port number "^p_num)
;;

(* == val get_cl_name : Unix.sockaddr -> string                       *)
(* Extrait le nom du client a partie de sont addresse IP              *)
let get_cl_name = function
 ADDR_UNIX _ -> failwith "INET socket expected"
|ADDR_INET(cl_inet_addr,_) -> (gethostbyaddr cl_inet_addr).h_name
;;

(* == val message : string -> string -> string                        *)
(* Compose le message de reponse a la connection d'un client          *)
let message cl_addr n_con =
 Printf.sprintf "Hello %s (%s)\n" cl_addr n_con
;;

(* == val write_log : string -> unit                                  *)
(* Ecriture d'une ligne dans le fichier trace                         *)
let write_log s =
 let f = open_out_gen [Open_wronly;Open_text;Open_append; Open_creat] (6*64+4*8+4) "hello.log" in
  output_string f s;
  close_out f
;;

(* == val con_line : string -> Unix.tm -> string -> string            *)
(* Construction d'une ligne pour le fichier trace :                   *)
(* Demande de connexion : nom-client date heure numero                *)
let con_line cl_name t_con n_con = 
 Printf.sprintf "%s connection %d/%d/%d %d:%d:%d (%s)\n"
                cl_name t_con.tm_mday t_con.tm_mon t_con.tm_year t_con.tm_hour
                t_con.tm_min t_con.tm_sec n_con
;;
 
(* == val ack_line : string -> Unix.tm -> Unix.tm -> string -> string *)
(* Construction d'une ligne pour le fichier trace :                   *)
(* Acquitement :                                                      *)
(*  nom-client date heure date-demande heure-demande numero-demande   *)
let ack_line cl_name t_ack t_con n_con = 
 Printf.sprintf "%s acknoledgement %d/%d/%d %d:%d:%d [%d/%d/%d %d:%d:%d (%s) request] \n"
                cl_name 
                t_ack.tm_mday t_ack.tm_mon t_ack.tm_year t_ack.tm_hour 
                t_ack.tm_min t_ack.tm_sec
                t_con.tm_mday t_con.tm_mon t_con.tm_year t_con.tm_hour
                t_con.tm_min t_con.tm_sec n_con 
;;

(* == val mk_n_con : float -> string                                  *)
(* Construction d'un numero de demande de connexion (decimales du     *)
(* temps systeme                                                      *)
let mk_n_con t =
 let t = string_of_float t in
 let i = String.index t '.' in
 let l = (String.length t)-i-1 in
   String.sub t (i+1) l
;;
 
(* == val server : unit -> unit                                       *)
(* Le serveur                                                         *)
let server () =
 (* Si pas d'argument : erreur    *)
 if Array.length Sys.argv < 2 then
   begin
    Printf.eprintf "Usage : hello3d portnumber\n"
   end
 (* sinon *)
 else
  (* Lecture du numero de port du service *)
  let port = get_s_port Sys.argv.(1) in
  (* Creation de la socket de service *)
  let sock = socket PF_INET SOCK_STREAM 0 in
  (* Adresse du serveur *)
  let h_addr = (gethostbyname (gethostname())).h_addr_list.(0) in
  let sock_addr = ADDR_INET(h_addr, port) in
  let sock_addr = ADDR_INET(h_addr, port) in
   begin
    (* Liaison socket/adresse *)
    bind sock sock_addr;
    (* Mise en attente de connection *)
    listen sock 5;
    (* Boucle de service *)
    while true do
     (* On accepte une connexion *)
     let (cl_sock, cl_sock_addr) = accept sock in
      (* On cree un processus de traitement de la connexion (le fils) *)
      if fork() == 0 then
       begin
        (* Hack Unix :-) *)
        if fork()<>0 then exit 0;
        (* Nom du client *)
        let cl_name = get_cl_name cl_sock_addr in
        (* Temps (systeme) de connexion *)
        let t_con = Unix.gettimeofday() in 
        (* Numero de connexion *)
        let n_con = mk_n_con t_con in
        (* Date locale de connexion *)
        let t_con = Unix.localtime t_con in 
         (* Emmission de message reponse *)
         (let m = message cl_name n_con in
           write cl_sock m 0 (String.length m));
         (* Ecriture trace connexion *)
         write_log (con_line cl_name t_con n_con);
         (* Lecture de l'acquitement *)
         (let r = String.make 2 '\000' in
           read cl_sock r 0 2);
         (* Ecriture trace acquittement *)
         (let t_ack = Unix.localtime(Unix.gettimeofday()) in 
           write_log (ack_line cl_name t_ack t_con n_con));
         (* Fermeture de la socket client *)
         close cl_sock;
         (* Fin du processus de traitement de connexion *)
         exit 0
       end
      else (* Le pere *)
       (* Attente fin de traitement connexion (par le fils) *)
       let _ = wait() in close cl_sock
     done
    end
;;

(* == Programme principal *)
try
 server()
with
 Unix_error(c,f,x)
 -> Printf.eprintf "\n %s in %s %s\n" (error_message c) f x
|Failure x
 -> Printf.eprintf "\n %s \n" x
;;


Page initiale Maison Page précédente POD