我想在OCaml中使用服务器进行客户端js_of_ocaml应用程序,下面描述了约束,我想知道下面的方法是否正确,或者是否有更高效的方法。服务器有时可以发送大量数据(> 30MB)。
为了使客户端和服务器之间的通信更安全,更高效,我在.mli文件中共享类型t,如下所示:
type client_to_server =
| Say_Hello
| Do_something_with of int
type server_to_client =
| Ack
| Print of string * int
然后,这种类型被编组成一个字符串并在网络上发送。我知道在客户端,缺少某些类型(Int64.t)。
此外,在客户端发送的XMLHTTPRequest中,我们希望从服务器接收多个编组对象,有时以流模式接收(即:在{{1}期间处理收到的编组对象(如果可能)请求的状态,而不仅仅是loading
状态)。
这些约束迫使我们使用XMLHTTPRequest的字段done
和内容类型responseText
。
此外,当我们从application/octet-stream
返回响应时,会进行编码转换,因为JavaScript的字符串是UTF-16。但是编组的对象是二进制数据,我们执行必要的操作以便检索我们的二进制数据(通过使用responseText
覆盖charset并在x-user-defined
字符串的每个字符上应用掩码。)< / p>
服务器(OCaml中的HTTP服务器)正在做这样简单的事情:
responseText
但是,在客户端,let process_request req =
let res = process_response req in
let s = Marshal.to_string res [] in
send s
的js_of_ocaml的实际JavaScript原语需要一个MlString。但是在流媒体模式下,我们不想在MlString中转换javascript的字符串(可以在完整的字符串上转换),我们更喜欢进行大小验证和解组(以及掩码的应用)对于编码问题)仅对读取的字节数。因此,我在javascript中编写了自己的marshal原语。
处理请求和响应的客户端代码是:
caml_marshal_data_size
基元是:
external marshal_total_size : Js.js_string Js.t -> int -> int = "my_marshal_total_size"
external marshal_from_string : Js.js_string Js.t -> int -> 'a = "my_marshal_from_string"
let apply (f:server_to_client -> unit) (str:Js.js_string Js.t) (ofs:int) : int =
let len = str##length in
let rec aux pos =
let tsize =
try Some (pos + My_primitives.marshal_total_size str pos)
with Failure _ -> None
in
match tsize with
| Some tsize when tsize <= len ->
let data = My_primitives.marshal_from_string str pos in
f data;
aux tsize
| _ -> pos
in
aux ofs
let reqcallback f req ofs =
match req##readyState, req##status with
| XmlHttpRequest.DONE, 200 ->
ofs := apply f req##responseText !ofs
| XmlHttpRequest.LOADING, 200 ->
ignore (apply f req##responseText !ofs)
| _, 200 -> ()
| _, i -> process_error i
let send (f:server_to_client -> unit) (order:client_to_server) =
let order = Marshal.to_string order [] in
let msg = Js.string (my_encode order) in (* Do some stuff *)
let req = XmlHttpRequest.create () in
req##_open(Js.string "POST", Js.string "/kernel", Js._true);
req##setRequestHeader(Js.string "Content-Type",
Js.string "application/octet-stream");
req##onreadystatechange <- Js.wrap_callback (reqcallback f req (ref 0));
req##overrideMimeType(Js.string "application/octet-stream; charset=x-user-defined");
req##send(Js.some msg)
这是将大型OCaml值从服务器传输到客户端的最有效方式,还是节省时间和空间的替代方案?
答案 0 :(得分:3)
您是否尝试使用EventSource https://developer.mozilla.org/en-US/docs/Web/API/EventSource
您可以流式传输json数据而不是封送数据。
Json.unsafe_input
应该比unmarshal更快。
class type eventSource =
object
method onmessage :
(eventSource Js.t, event Js.t -> unit) Js.meth_callback
Js.writeonly_prop
end
and event =
object
method data : Js.js_string Js.t Js.readonly_prop
method event : Js.js_string Js.t Js.readonly_prop
end
let eventSource : (Js.js_string Js.t -> eventSource Js.t) Js.constr =
Js.Unsafe.global##_EventSource
let send (f:server_to_client -> unit) (order:client_to_server) url_of_order =
let url = url_of_order order in
let es = jsnew eventSource(Js.string url) in
es##onmessage <- Js.wrap_callback (fun e ->
let d = Json.unsafe_input (e##data) in
f d);
()
在服务器端,您需要依赖deriving_json http://ocsigen.org/js_of_ocaml/2.3/api/Deriving_Json来序列化您的数据
type server_to_client =
| Ack
| Print of string * int
deriving (Json)
let process_request req =
let res = process_response req in
let data = Json_server_to_client.to_string res in
send data
note1:Deriving_json
使用js_of_ocaml中值的内部表示将ocaml值序列化为json。 Json.unsafe_input
是Deriving_json
的快速反序列化程序,它依赖于浏览器本机JSON支持。
note2:Deriving_json
和Json.unsafe_input
负责ocaml字符串编码