Erlang调用C程序

来源:互联网 发布:第三帝国 知乎 编辑:程序博客网 时间:2024/06/01 11:00

本文是《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;}
0 0
原创粉丝点击