您的位置:首页 > 其它

Erlang调用C程序

2016-05-09 17:34 316 查看
本文是《Erlang程序设计》中的示例,因为C语言基础薄弱,理解这节花了一些时间,示例中的C程序分为三个文件,为了便于调试,我合并成了一个文”c.c”,而Erlang代码则叫”e.erl”,在代码中加入了一些便于理解的注释。

主要用到的Erlang函数为:open_port(PortName,[Opt])

参数其中PortName可以是下列选项中的一个:

{spawn,Command}

启动一个外部程序。Command是这个外部程序的名称。

{fd,In,Out}

允许一个Erlang进程访问Erlang使用的任何当前打开文件描述符。文件描述符In可以用作标准输入,文件描述符Out会在

Erlang工作空间之外运行。

参数Opt可以是下列选项中的一个:

{packet,N}

数据包(packet)前面有N(1/2/4)个字节长度计数(包头)。

stream



e.erl

-module(e).
-export([start/0,stop/0,twice/1,sum/2,call_port/1,loop/1]).

start() ->
register(e,spawn(fun() ->
process_flag(trap_exit,true),
%% 启动一个名为c的外部可执行程序,{packet,2}表示根据包大小自动添加2字节的包头
Port = open_port({spawn,"./c"},[{packet,2}]),
loop(Port)
end)).

stop() ->
?MODULE ! stop.

sum(X,Y) ->
call_port({sum,X,Y}).

twice(X) ->
call_port({twice,X}).

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

loop(Port) ->
receive
{call,Caller,Msg} ->
%% 发送示例 :调用 e:sum(3,4),encode后向端口发送 {self(),{command,[1,3,4]}} 的消息内容,
%% 端口驱动自动给消息内容加上两个字节的长度包头{0,3},表示消息内容有3个字节,然后把 {0,3,1,3,4} 发送给外部C程序
Port ! {self(),{command,encode(Msg)}},
receive
%% 接收示例:返回{0,1,7},端口驱动移除长度包头{0,1},然后向相连进程发送一个{Port,{data,[7]}}的消息
{Port,{data,Data}} ->
Caller ! {?MODULE,decode(Data)}
end,
loop(Port);

stop ->
Port ! {self(),close},
receive
{Port,closed} ->
exit(normal)
end;
{'EXIT',Port,Reason} ->
exit({port_terminated,Reason})
end.

encode({sum,X,Y}) ->
[1,X,Y];

encode({twice,X}) ->
[2,X].

decode([Int]) ->
Int.


c.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef unsigned char byte;

int read_cmd(byte *buff);
int read_exact(byte *buf,int len);
int write_cmd(byte *buff,int len);
int write_exact(byte *buf,int len);
int sum(int x,int y);
int twice(int x);

int main() {
int fn,arg1,arg2,result;
byte buff[100];
while( read_cmd(buff) >0 ) {
fn = buff[0];
if( fn == 1) {
arg1 = buff[1];
arg2 = buff[2];
result = sum(arg1,arg2);
} else if( fn == 2 ) {
arg1 = buff[1];
result = twice(arg1);
} else {
exit(EXIT_FAILURE);
}
buff[0] = result;
// fprintf(stderr,"buff_value: %i,%i,%i \n",buff[0],buff[1],buff[2]);
write_cmd(buff,1);
}
}

/*
read_cmd函数两次调用了read_exact
例如标准输入里面有5个字节 {0,3,1,3,4}
第一次调用 read(0,buf,2) ,读取了头两个字节{0,3}
第二次调用 read(0,buf,3),读取了剩下的3个字节{1,3,4}
*/
int read_cmd(byte *buf) {
int len;
if( read_exact(buf,2) !=2 ) return(-1);
// "<<" 左移 ,二进制的向左移8位,n<<8 十进制表示 n * 2^8 ;"|" 按位或
len = (buf[0] << 8) | buf[1];
return read_exact(buf,len);
}

// 从标准输入读取 len 个字节
int read_exact(byte *buf,int len) {
int i,got=0;
do {
if(( i = read(0,buf+got,len-got)) <=0 ) return(i);
got += i;
} while ( got < len );
return(len);
}

/*
例如:this->sum(3,4),返回结果{7,3,4},调用三次write_exact,分别输出{0,1,7},前两个字节是包头,表示消息体内容长度为1,之后的是消息体内容
*/
int write_cmd(byte *buf,int len) {
byte li;
li = (len >> 8) & 0xff; //  fprintf(stderr,"%d \n",li); out : 0
write_exact(&li,1);
li = len & 0xff; // fprintf(stderr,"%d \n",li); out : 1
write_exact(&li,1);
return write_exact(buf,len);
}

// 输出终端最多 len 个字节
int write_exact(byte *buf,int len) {
int i,wrote = 0;
do {
if( (i=write(1,buf+wrote,len-wrote)) <= 0 ) return (i);
wrote += i;
} while (wrote<len);
return(len);
}

// 求和
int sum(int x,int y) {
return x+y;
}

// 求2倍
int twice(int x) {
return 3*x;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: