doujiang1001 2015-08-25 09:29
浏览 80
已采纳

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

I have a crude Erlang-to-Golang port example, passing data from Erlang to Golang and echoing the response.

Problem is the amount of data I can transfer seems to be limited to 2^8 bytes (see below). I thought the problem was probably on the Golang side (not creating a big enough buffer) but replacing bufio.NewReader with bufio.NewReaderSize didn't work. So am now thinking the problem is maybe on the Erlang side.

What do I need to do to increase the buffer size / be able to echo a message larger than 2^8 bytes ?

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 = '
'

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.
  • 写回答

2条回答 默认 最新

  • donglin6313 2015-08-25 12:20
    关注

    If you use start_link instead, you'll see that the port crashes after the first command:

    1> port:start('go run port.go').
    <0.118.0>
    2> port:ping(65000).
    65000
    ** exception error: port_terminated
    

    If you change the Go code to run in a loop, this crash can be avoided:

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

    Now we can see another interesting result:

    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
    

    Now we can see that no data is actually lost, it's just buffered in the port. Since you have not specified a framing protocol (using {packet, N} or {line, N} you are responsible yourself for collecting the data. It also seems that the internal buffer size of an Erlang port is 64K (although I found no documentation of this and no way to change it).

    If you change your receive to get all data before returning, you'll every byte each time:

    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.
    

    Running this, we get:

    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
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 matlab数字图像处理频率域滤波
  • ¥15 ELGamal和paillier计算效率谁快?
  • ¥15 file converter 转换格式失败 报错 Error marking filters as finished,如何解决?
  • ¥15 ubuntu系统下挂载磁盘上执行./提示权限不够
  • ¥15 Arcgis相交分析无法绘制一个或多个图形
  • ¥15 关于#r语言#的问题:差异分析前数据准备,报错Error in data[, sampleName1] : subscript out of bounds请问怎么解决呀以下是全部代码:
  • ¥15 seatunnel-web使用SQL组件时候后台报错,无法找到表格
  • ¥15 fpga自动售货机数码管(相关搜索:数字时钟)
  • ¥15 用前端向数据库插入数据,通过debug发现数据能走到后端,但是放行之后就会提示错误
  • ¥30 3天&7天&&15天&销量如何统计同一行