我最近一直在努力更好地理解Eliom的通信功能,为了做到这一点,我试图建立一个简单的网页,允许用户相互发送消息。
如果我通过Firefox作为一个用户登录,第二个用户通过Chrome登录,网页工作正常。但是,当我登录两个不同的用户在同一个浏览器,并尝试从一个用户发送消息到另一个(即从一个选项卡到另一个选项卡),发送的任何消息显示在所有选项卡,而不是预期的收件人的选项卡。
我认为,我可能有一些问题与选择的范围为eref或我在哪里设置/获取范围和eref(顶层与服务定义)。
我正试图纠正我的错误,以便两个用户可以登录到同一浏览器的两个不同的选项卡,并相互发送消息,消息只显示在正确的用户选项卡上。
注意:部分代码取自Eliom网站教程:http://ocsigen.org/tuto/4.2/manual/how-to-implement-a-notification-system
我的。eliom文件:
(* Example website login: localhost:8080/?user_num=1 *)
{shared{
open Eliom_lib
open Eliom_content
open Html5
open Html5.F
open Eliom_registration
open Eliom_parameter
}}
module Channel_example_app =
Eliom_registration.App (
struct
let application_name = "channel_example"
end)
let main_service =
Eliom_service.App.service ~path:[] ~get_params:(string "user_num") ()
let new_message_action =
Eliom_service.Http.post_coservice'
~post_params:(string "from_user_id" ** string "to_user_id" ** string "msg") ()
(* Set the scope used by all erefs *)
let eref_scope = Eliom_common.default_process_scope
(* Create a channel eref *)
let channel_ref =
Eliom_reference.Volatile.eref_from_fun
~scope:eref_scope
(fun () ->
let (s, notify) = Lwt_stream.create () in
let c = Eliom_comet.Channel.create s in
(c, notify)
)
(* Reactive string eref *)
let react_string_ref =
Eliom_reference.Volatile.eref_from_fun
~scope:eref_scope
(fun () ->
let (client_string, send_client_string) :
(string React.E.t * (?step:React.step -> string -> unit) ) =
React.E.create ()
in
(client_string, send_client_string)
)
(* Reactive string to display the users session group *)
let react_session_group_ref =
Eliom_reference.Volatile.eref_from_fun
~scope:eref_scope
(fun () ->
let (session_group_string, send_session_group_string) :
(string React.E.t * (?step:React.step -> string -> unit) ) =
React.E.create ()
in
(session_group_string, send_session_group_string)
)
(* Reactive string to display the users session group size *)
let react_session_group_size_ref =
Eliom_reference.Volatile.eref_from_fun
~scope:eref_scope
(fun () ->
let (session_group_size_string, send_session_group_size_string) :
(string React.E.t * (?step:React.step -> string -> unit) ) =
React.E.create ()
in
(session_group_size_string, send_session_group_size_string)
)
(* Send a message from one client to another *)
let notify from_user_id to_user_id s =
(* Get the session group state for the user *)
let state =
Eliom_state.Ext.volatile_data_group_state ~scope:Eliom_common.default_group_scope to_user_id in
(* Iterate on all sessions from the group *)
Eliom_state.Ext.iter_volatile_sub_states ~state
(fun state ->
(* Iterate on all client process states in the session *)
Eliom_state.Ext.iter_volatile_sub_states ~state
(fun state ->
let (_, notify) = Eliom_reference.Volatile.Ext.get state channel_ref in
notify (Some ("Hello from " ^ from_user_id ^ "! You are user " ^ to_user_id ^ "nn" ^ s))
)
)
(* Action for a client to send a message *)
let () =
Eliom_registration.Action.register
~options:`NoReload
~service:new_message_action
(fun () (from_user_id, (to_user_id, msg)) ->
Lwt.return @@ notify from_user_id to_user_id msg
)
(* Post form for one user to send a message to another user *)
let client_message_form =
Eliom_content.Html5.F.post_form ~service:new_message_action ~port:8080
(
fun (from_user_id, (to_user_id, msg)) ->
[p [pcdata "To:"];
string_input ~input_type:`Text ~name:to_user_id ();
p [pcdata "From:"];
string_input ~input_type:`Text ~name:from_user_id ();
p [pcdata "Send a message here:"];
string_input ~input_type:`Text ~name:msg ();
button ~button_type:`Submit [pcdata "Send Message"]
]
)
let () =
Channel_example_app.register
~service:main_service
(fun user_num () ->
(* Set the session group to which the erefs belong *)
Eliom_state.set_volatile_data_session_group
~set_max:1
~scope:Eliom_common.default_session_scope
~secure:true
user_num;
let (channel, _) = Eliom_reference.Volatile.get channel_ref in
let my_client_string, my_send_client_string = Eliom_reference.Volatile.get react_string_ref in
let my_send_client_string' =
server_function Json.t<string> (fun s -> Lwt.return @@ my_send_client_string s)
in
let c_down = Eliom_react.Down.of_react my_client_string in
(* When a message is received on the channel, push it as a reactive event *)
let _ =
{unit{
Lwt.async
(fun () ->
Lwt_stream.iter (fun (s : string) -> ignore @@ %my_send_client_string' s) %channel
)
}}
in
let my_session_group =
match
Eliom_state.get_volatile_data_session_group
~scope:Eliom_common.default_session_scope
~secure:true ()
with
| None -> "No session group"
| Some sg -> sg
in
let my_session_group_size =
match
Eliom_state.get_volatile_data_session_group_size
~scope:Eliom_common.default_session_scope
~secure:true ()
with
| None -> "0"
| Some gs -> string_of_int gs
in
Lwt.return
(Eliom_tools.F.html
~title:"channel_example"
~css:[["css";"channel_example.css"]]
Eliom_content.Html5.F.(body [
h2 [pcdata ("Your are logged in as user " ^ user_num)];
client_message_form ();
p [pcdata "Your message is:"];
C.node {{R.pcdata (React.S.hold "No message yet" %c_down)}};
p [pcdata ("I am a part of the session group named " ^ my_session_group)];
p [pcdata ("My session group size is " ^ my_session_group_size)]
])))
问题来自于使用遍历所有选项卡的notify函数。我使用了Eliom基础应用程序的哈希表/弱哈希表结构,它纠正了所有的通信问题。关键是修改通知函数,如下所示:
let notify ?(notforme = false) ~id ~to_user ~msg =
Lwt.async (fun () ->
I.fold
(fun (userid_o, ((_, _, send_e) as nn)) (beg : unit Lwt.t) ->
if notforme && nn == Eliom_reference.Volatile.get notif_e
then Lwt.return ()
else
lwt () = beg in
let content = if Some to_user = userid_o then Some msg else None in
match content with
| Some content -> send_e (id, content); Lwt.return ()
| None -> Lwt.return ()
)
id (Lwt.return ()))