使用OCaml封送数据的通信客户端服务器
我想用ocaml中的一个服务器做一个客户端js_of_ocaml应用程序,使用下面描述的约束,我想知道下面的方法是否正确,或者是否有更有效的方法。服务器有时可以发送大量数据(>30MB) 为了使客户端和服务器之间的通信更安全、更高效,我在.mli文件中共享了一个类型t,如下所示:使用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
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值序列化为jsonJson.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