Asynchronous 使用Async在OCaml中编写TCP服务器/客户端
这里是OCaml的初学者,听说它非常擅长构建网络应用程序。我决定用异步库弄脏我的手 我正在尝试实现类似netcat的东西,其中服务器和客户端可以相互发送消息 这是启动服务器的功能:Asynchronous 使用Async在OCaml中编写TCP服务器/客户端,asynchronous,ocaml,Asynchronous,Ocaml,这里是OCaml的初学者,听说它非常擅长构建网络应用程序。我决定用异步库弄脏我的手 我正在尝试实现类似netcat的东西,其中服务器和客户端可以相互发送消息 这是启动服务器的功能: let start_server p = let host_and_port = Tcp.Server.create ~on_handler_error:`Raise (Tcp.Where_to_listen.of_port p) (fun so
let start_server p =
let host_and_port =
Tcp.Server.create
~on_handler_error:`Raise
(Tcp.Where_to_listen.of_port p)
(fun sock reader writer ->
conn_handler sock reader writer)
in
ignore (host_and_port : (Socket.Address.Inet.t, int) Tcp.Server.t Deferred.t);
Deferred.never ()
这是启动客户端的函数:
let start_client a p =
Tcp.with_connection
(Tcp.Where_to_connect.of_host_and_port { host = a; port = p})
(fun sock reader writer ->
conn_handler sock reader writer)
这两个函数都调用函数conn_handler,该函数实现消息发送逻辑
客户端/服务器必须同时能够:
从stdin读取消息并发送。
接收消息并发回已接收的消息。
conn_处理程序的当前实现:
逻辑的顺序有个错误,我没办法弄清楚。有人知道如何同时实现这两种功能吗?如果您启用代码的自动缩进(例如,使用ocp缩进),问题很容易被发现。例如,您的代码
let rec conn_handler s r w =
let stdin = Lazy.force Reader.stdin in
Reader.read_line stdin >>= function
| `Eof -> return ()
| `Ok x ->
Writer.write_line w x;
Reader.read_line r >>= function
| `Eof -> return ()
| `Ok "exit" -> return ()
| `Ok x ->
print_endline x;
Writer.write_line w "Acknowledged";
conn_handler s r w
将缩进为
let rec conn_handler s r w =
let stdin = Lazy.force Reader.stdin in
Reader.read_line stdin >>= function
| `Eof -> return ()
| `Ok x ->
Writer.write_line w x;
(* woops, it actually happens in this branch *)
Reader.read_line r >>= function
| `Eof -> return ()
| `Ok "exit" -> return ()
| `Ok x ->
print_endline x;
Writer.write_line w "Acknowledged";
conn_handler s r w
这个问题是双重的。首先,您尝试使用分号;将两个匹配表达式分开,但自;具有更高的优先级—它仅将第二个匹配绑定到第一个匹配的第二个分支。换句话说,分号不充当匹配终止符,它只是将一个表达式向左排序,将一个表达式向右排序
match x with
| X -> ()
| Y ->
do_something ();
do_something_else ();
match y with
| Z -> ...
如果您确实想一个接一个地进行两个匹配,则需要使用括号或开始/结束来分隔它们,例如
begin match x with
| A -> ..
| ...
| Z -> ..
end;
begin match y with
| A -> ..
| ...
| Z -> ..
end;
因此,您的连接处理程序正在从标准输入读取一行,当它准备就绪时,它会将作业发布到写入器队列,并且只有在读取和写入之后,才会从通道r进行读取
我们不能在这里使用分号来组合两个任务,因为这两个任务都是延迟的,也就是说,它们的类型为unit deferred.t not unit。因此,我们必须使用延迟接口提供的一些组合器。我们可以使用类型为“a t->”b t->“a*”b t的两个任务,但由于我们的两个任务都评估为单位值,因此可能没有理由构建,值。因此,我们可以使用all_unit combinator,它获取任务列表并计算为unit Deferred.t,一旦确定了所有任务,就会确定unit Deferred.t,例如,下面的实现稍微接近您的意图
let rec conn_handler s r w =
let stdin = Lazy.force Reader.stdin in
let task1 = Reader.read_line stdin >>= function
| `Eof -> return ()
| `Ok x ->
Writer.write_line w x;
return () in
let task2 = Reader.read_line r >>= function
| `Eof -> return ()
| `Ok "exit" -> return ()
| `Ok x ->
print_endline x;
Writer.write_line w "Acknowledged";
return () in
Deferred.all_unit [
task1;
task2;
] >>= fun () ->
conn_handler s r w
在每个步骤中,我们从stdin中读取一行,同时从提供的输入中读取一行,并写入确认的消息。然后,它将等待两个任务完成并重复。因此,我们再次创建了一个不幸的同步。因此,我们可能设计的步骤不正确,因为每个任务都应该有自己的循环,而不是一个循环按顺序运行两个任务,例如
let conn_handler s r w =
let stdin = Lazy.force Reader.stdin in
let rec task1 () = Reader.read_line stdin >>= function
| `Eof -> return ()
| `Ok x ->
Writer.write_line w x;
task1 () in
let rec task2 () = Reader.read_line r >>= function
| `Eof -> return ()
| `Ok "exit" -> return ()
| `Ok x ->
print_endline x;
Writer.write_line w "Acknowledged";
task2 () in
Deferred.any_unit [
task1 ();
task2 ();
]
现在,我们独立运行两个任务,并在其中任何一个停止时立即停止
虽然这个示例是演示性的,但使用异步中的管道机制可以更轻松地解决任务本身。像这样的,
let conn_handler s r w =
let input = Reader.lines (Lazy.force Reader.stdin) in
let src = Reader.pipe r
and dst = Writer.pipe w in
Deferred.any_unit [
Pipe.transfer_id input dst;
Pipe.transfer src dst ~f:(function
| "exit" -> Pipe.close_read src; "Goodbye"
| s ->
print_endline ("Received: " ^ s);
"Acknowledged")
]
let conn_handler s r w =
let input = Reader.lines (Lazy.force Reader.stdin) in
let src = Reader.pipe r
and dst = Writer.pipe w in
Deferred.any_unit [
Pipe.transfer_id input dst;
Pipe.transfer src dst ~f:(function
| "exit" -> Pipe.close_read src; "Goodbye"
| s ->
print_endline ("Received: " ^ s);
"Acknowledged")
]