我有一个简单的从Erlang到Golang的端口示例,将数据从Erlang传递到Golang并回显响应。
问题是我可以传输的数据量似乎限制为2 ^ 8字节(请参见下文)。我认为问题可能出在Golang端(没有创建足够大的缓冲区),但是用bufio.NewReaderSize替换bufio.NewReader无效。因此,我现在认为问题可能出在Erlang一方。
我该怎么做才能增加缓冲区大小/能够回显大于2 ^ 8字节的消息?
TIA
justin@justin-ThinkPad-X240:~/work/erlang_golang_port$ erl -pa ebin Erlang/OTP 17 [erts-6.4.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false] Eshell V6.4.1 (abort with ^G) 1> port:start("./echo"). <0.35.0> 2> port:ping(65000). 65000 3> port:ping(66000). ** exception error: bad argument in function port:call_port/1 (port.erl, line 20) 4> port:start("./echo"). <0.40.0> 5> port:ping(66000). 65536
go
package main import ( "bufio" "os" ) const Delimiter = '\n' func main() { // reader := bufio:NewReader(os.Stdin) reader := bufio.NewReaderSize(os.Stdin, 1677216) // 2**24; bytes, _ := reader.ReadBytes(Delimiter) os.Stdout.Write(bytes[:len(bytes)-1]) }
Erlang
-module(port). -export([start/1, stop/0, init/1]). -export([ping/1]). -define(DELIMITER, [10]). start(ExtPrg) -> spawn(?MODULE, init, [ExtPrg]). stop() -> myname ! stop. ping(N) -> Msg=[round(65+26*random:uniform()) || _ <- lists:seq(1, N)], call_port(Msg). call_port(Msg) -> myname ! {call, self(), Msg}, receive {myname, Result} -> length(Result) end. init(ExtPrg) -> register(myname, self()), process_flag(trap_exit, true), Port = open_port({spawn, ExtPrg}, []), loop(Port). loop(Port) -> receive {call, Caller, Msg} -> Port ! {self(), {command, Msg++?DELIMITER}}, receive {Port, {data, Data}} -> Caller ! {myname, Data} end, loop(Port); stop -> Port ! {self(), close}, receive {Port, closed} -> exit(normal) end; {'EXIT', Port, _Reason} -> exit(port_terminated) end.
如果start_link改为使用,您将在第一个命令后看到端口崩溃:
start_link
1> port:start('go run port.go'). <0.118.0> 2> port:ping(65000). 65000 ** exception error: port_terminated
如果将Go代码更改为在循环中运行,则可以避免此崩溃:
func main() { for { // reader := bufio:NewReader(os.Stdin) reader := bufio.NewReaderSize(os.Stdin, 1677216) // 2**24; bytes, _ := reader.ReadBytes(Delimiter) os.Stdout.Write(bytes[:len(bytes)-1]) } }
现在我们可以看到另一个有趣的结果:
33> c(port). {ok,port} 40> port:ping(66000). 65536 41> port:ping(66000). 464 42> port:ping(66000). 65536 43> port:ping(66000). 464
现在我们可以看到实际上没有任何数据丢失,它只是缓存在端口中。由于您尚未指定成帧协议(使用{packet, N}或{line,N}您自己负责收集数据。似乎Erlang端口的内部缓冲区大小为64K(尽管我没有找到有关此文件的文档,也无法更改它)。
{packet, N}
{line,N}
如果将接收更改为在返回之前获取所有数据,则每次将占用每个字节:
loop(Port) -> receive {call, Caller, Msg} -> Port ! {self(), {command, Msg++?DELIMITER}}, Caller ! {myname, receive_all(Port, 10)}, loop(Port); stop -> Port ! {self(), close}, receive {Port, closed} -> exit(normal) end; {'EXIT', Port, _Reason} -> exit(port_terminated) end. receive_all(Port, Timeout) -> receive_all(Port, Timeout, []). receive_all(Port, Timeout, Data) -> receive {Port, {data, New}} -> receive_all(Port, Timeout, [New|Data]) after Timeout -> lists:flatten(lists:reverse(Data)) end.
运行此命令,我们得到:
1> c(port). {ok,port} 2> 3> port:start('go run port.go'). <0.311.0> 4> port:ping(66000). 66000 5> port:ping(66000). 66000 6> port:ping(66000). 66000