使用OCaml封送数据的通信客户端服务器

使用OCaml封送数据的通信客户端服务器,ocaml,marshalling,js-of-ocaml,Ocaml,Marshalling,Js Of Ocaml,我想用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 stri

我想用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中,我们希望从服务器接收多个封送对象,有时是以流模式(即:在请求的
加载
状态期间处理接收到的封送对象(如果可能),而不仅仅是在
完成
状态期间)

这些约束迫使我们将XMLHTTPRequest的字段
responseText
与内容类型
application/octet-stream
一起使用

此外,当我们从
responseText
返回响应时,会进行编码转换,因为JavaScript的字符串是UTF-16格式的。但是封送对象是二进制数据,为了检索二进制数据,我们做了必要的事情(通过使用
x-user-defined
覆盖字符集,并在
responseText
字符串的每个字符上应用掩码)

服务器(OCaml中的HTTP服务器)正在执行以下简单操作:

let process_request req =
    let res = process_response req in
    let s = Marshal.to_string res [] in
    send s
但是,在客户端,用于
caml\u marshal\u data\u size
的js\u of\u ocaml的实际JavaScript原语需要一个MlString。但在流模式下,我们不想将javascript的字符串转换为MlString(可以在完整字符串上进行iter),我们更喜欢只在读取的字节上进行大小验证和解组(以及针对编码问题应用掩码)。因此,我用javascript编写了自己的封送处理原语

用于处理请求和响应的客户端代码为:

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)
external marshal\u total\u size:Js.Js\u string Js.t->int->int=“my\u marshal\u total\u size”
外部封送来自\u字符串:Js.Js\u字符串Js.t->int->'a=“我的封送来自\u字符串”
让我们应用(f:server\u to\u client->unit)(str:Js.Js\u string Js.t)(ofs:int):int=
设len=str##长度in
让我们记录辅助位置=
让tsize=
尝试一些(pos+My_primitives.marshal_total_size str pos)
有故障->无
在里面
匹配tsize
|一些tsize当tsize
让data=My_primitives.marshal_from_string str pos in
f数据;
辅助tsize
|位置->位置
在里面
辅助ofs
让REQF req ofs回调=
将req##readyState、req##状态与匹配
|XmlHttpRequest.DONE,200->
ofs:=应用f请求##响应文本!ofs
|XmlHttpRequest.LOADING,200->
忽略(应用f请求##响应文本!ofs)
| _, 200 -> ()
|_u,i->处理错误i
让我们发送(f:服务器到客户端->单元)(订单:客户端到服务器)=
let order=Marshal.to_字符串顺序[]in
让msg=Js.string(我的编码顺序)进入(*做一些事情*)
让req=XmlHttpRequest.create()在
请求打开(Js.string“POST”,Js.string//内核,Js.true);
请求##setRequestHeader(Js.string“内容类型”,
Js.string“应用程序/八位字节流”);

req##onreadystatechange是否尝试使用EventSource

您可以流式传输json数据,而不是封送数据。
Json.unsafe_输入
应该比解组更快

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);
 ()
注1:
Deriving_json
使用_ocaml的js_中的值的内部表示形式将ocaml值序列化为json
Json.unsafe_input
是一个快速的反序列化程序,用于
派生依赖于浏览器本机Json支持的_Json

注2:
派生json
json。不安全的输入
注意ocaml字符串编码

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);
 ()
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