title: realize the connection between Golang and Erlang (Port)
categories: Golang
In Erlang, there are many ways to interact with other languages. The common ways are
- Interaction using TCP protocol
- Using Port
- Using ERL? Interface to realize
- CNode
- NIF
The latter several kinds of difficulties are all there, but also use a more complex C/C + +, and more prone to problems. The way of TCP is through network protocol, and I don't like it very much, so I'll leave Port to connect to Erlang server.
The introduction of Port in Erlang's official documents
Using Port in Erlang is very simple, which is actually to interact with external programs through standard input and output streams.
Now I still use the code in the official document and only make some modifications to realize the interaction between Erlang and 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), %% Be careful{packet, 2}Represents the transmission in 2 bytes %% Port = open_port({spawn, ExtPrg}, [{packet, 2}]), %% You don't need to process this header here. You don't need this parameter 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. %% Code, convert data to binary encode({foo, X}) -> <<1:8, X:8>>; encode({bar, Y}) -> <<2:8, Y:8>>]. %% It's a little special here, decode(Data) -> erlang:list_to_binary(Data).
For Golang, you need to read the data of the standard input stream. Here I use bufio to process it (in fact, it's not necessary, hahaha). Because the header of the data stream is not used to record the length of the data, the data length is set to the byte array of 2 by default.
// 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]) // It should be noted here that Flush should be used for processing, otherwise the erlang end will not receive the information writer.Flush() } } func foo(num int) int { return num + 1 } func bar(num int) int { return 2 * num }
Use
Step 1 compile golang
go build port.go
Step 2 run Erlang virtual machine
unix> erl Erlang (BEAM) emulator version 4.9.1.2 Eshell V4.9.1.2 (abort with ^G) 1> c(complex1). {ok,complex1}
Step 3 run
2> complex1:start("extprg"). <0.34.0> 3> complex1:foo(3). 4 4> complex1:bar(5). 10 5> complex1:stop(). stop