實現Golang和Erlang的鏈接(Port)


title: 實現Golang和Erlang的鏈接(Port)html

categories: Golang

在Erlang中,有不少種方式去實現與其餘語言的交互,常見的幾種方式有golang

  1. 使用TCP協議交互
  2. 使用Port
  3. 使用Erl_Interface方式去實現
  4. CNode
  5. NIF

後面幾種難度都是有的,也使用了比較複雜的C/C++,並且比較容易出現問題。TCP的方式是經過網絡協議,我的也不是很喜歡,那就剩下Port方式去鏈接Erlang服務器。shell

Erlang的官方文檔中對Port的介紹數組

在Erlang中使用Port很是簡單,實際上就是經過標準輸入輸出流與外部程序就行交互。服務器

如今我仍是沿用官方文檔中代碼,僅作部分修改,實現Erlang與Golang的交互。網絡

%% complex1.erl

-module(complex1).
-export([start/1, stop/0, init/1]).
-export([foo/1, bar/1]).

start(ExtPrg) ->
    spawn(?MODULE, init, [ExtPrg]).
stop() ->
    complex ! stop.

foo(X) ->
    call_port({foo, X}).
bar(Y) ->
    call_port({bar, Y}).

call_port(Msg) ->
    complex ! {call, self(), Msg},
    receive
    {complex, Result} ->
        Result
    end.

init(ExtPrg) ->
    register(complex, self()),
    process_flag(trap_exit, true),
    %% 注意{packet, 2}表明的是用2個字節表示傳輸的
    %% Port = open_port({spawn, ExtPrg}, [{packet, 2}]),
    %% 在這裏不去處理這個數據頭,不須要這個參數
    Port = open_port({spawn, ExtPrg}, []),
    loop(Port).

loop(Port) ->
    receive
    {call, Caller, Msg} ->
        Port ! {self(), {command, encode(Msg)}},
        receive
        {Port, {data, Data}} ->
            Caller ! {complex, decode(Data)}
        end,
        loop(Port);
    stop ->
        Port ! {self(), close},
        receive
        {Port, closed} ->
            exit(normal)
        end;
    {'EXIT', Port, Reason} ->
        exit(port_terminated)
    end.

%% 進行編碼,將數據轉成2進制
encode({foo, X}) -> <<1:8, X:8>>;
encode({bar, Y}) -> <<2:8, Y:8>>].

%% 這裏有點特別,
decode(Data) -> erlang:list_to_binary(Data).

對於Golang,須要讀取標準輸入流的數據,這裏我用bufio處理下(其實也沒有太多必要,哈哈哈)。由於這裏沒有使用數據流的頭部來記錄數據的長度,因此默認將數據長度設置爲2的byte數組。oop

// port.go

package main

import (
    "bufio"
    "os"
)

func main() {
    reader := bufio.NewReader(os.Stdin)
    writer := bufio.NewWriter(os.Stdout)
    for {
        buff := make([]byte, 2)
        _, err := reader.Read(buff)
        if err != nil {
            panic(err.Error())
        }
        switch int(buff[0]) {
        case 1:
            buff[1] = byte(foo(int(buff[1])))
        case 2:
            buff[1] = byte(bar(int(buff[1])))
        }
        writer.Write(buff[1:2])
        // 這裏須要注意,要用Flush進行處理,不然erlang端收不到信息
        writer.Flush()
    }
}

func foo(num int) int {
    return num + 1
}

func bar(num int) int {
    return 2 * num
}

使用

步驟1 編譯golang

go build port.go

步驟2 運行Erlang虛擬機

unix> erl
Erlang (BEAM) emulator version 4.9.1.2

Eshell V4.9.1.2 (abort with ^G)
1> c(complex1).
{ok,complex1}

步驟3 運行

2> complex1:start("extprg").
<0.34.0>
3> complex1:foo(3).
4
4> complex1:bar(5).
10
5> complex1:stop().
stop
相關文章
相關標籤/搜索