go - Erlang/Golang 端口示例中的缓冲区大小

标签 go erlang

我有一个粗略的 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

开始

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])
}

二语

-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,您会看到端口在第一个命令后崩溃:

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

现在我们可以看到实际上没有数据丢失,它只是缓存在端口中。由于您没有指定成帧协议(protocol)(使用 {packet, N}{line, N} 您自己负责收集数据。似乎内部Erlang 端口的缓冲区大小为 64K(尽管我没有找到这方面的文档,也无法更改它)。

如果您将接收更改为在返回之前获取所有数据,则每次都会获取每个字节:

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

关于go - Erlang/Golang 端口示例中的缓冲区大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32200352/

相关文章:

go - 如何从 multipart.File 获取 md5

erlang - 如果使用相同的种子,为什么 Erlang 会生成相同的随机数序列?

coding-style - Erlang 风格 - 案例与函数模式匹配

go - 如何做一个 For-Else 循环(其他语言的 While-Else)?

去劫持客户端连接

go - 单 channel 和 select 语句死锁

linux - 如何重启erlang节点?

postgresql - DefaultServeMux 中的 SQL 给出错误

Erlang:用于流程的简单pubsub-我的方法还好吗?

javascript - 将 CouchDB javascript View 转换为 erlang