erlang与c之间的连接

http://blog.chinaunix.net/uid-22566367-id-382012.html

erlang与c之间的连接
参考资料:网络资料
作者:Sunny
    在Programming Erlang书上的第十二章中实现了elang与c语言之间的连接,本人觉得那个例子还是有点复杂,在此,本人举一个更简单的例子。而且在那本书上的Page 161第7行,有一个错误。
书上写的是:
    Port ! {PidC, {connect, Pid1}}
    把端口连接进程PID从Pid1改为PidC
他把这句话写反了(也许是翻译的问题),应为:把端口连接进程PID从PidC改为Pid1。

下面开始我们的程序。
预备知识:
(一)、一般而言,A语言要和B语言进行连接的话,有两种方式。
1.把B语言的可执行二进制代码拷贝到A程序当中,定义标准的参数调用接口,从而B程序执行的结果返回到A程序当中。
2.启用两个独立的进程,A进程可以和B进程之间通信,以此来实现A和B的连接。
当然,我们使用的Eralng语言是采用第二中方式进行的。这样做主要有这方面的考虑:我们不可能保证在erlang之外的程序正常运行,一旦外部程序崩溃,那么端口连接进程就会收到一个退出信号,相应的,如果端口连接进程消亡,那么外部程序也会崩溃的。除此之外,erlang外部程序是操作系统来运行的,不会导致erlang系统的崩溃。
(二)、要创建一个端口,我们可以用这个命令。
    Port = open_port(PortName, PortSettings).
每个端口必须绑定到一个 Erlang 进程中,一般来讲,创建 Erlang
端口的进程即为端口连接进程。创建号端口后,你就可以向该端口发送数据,或者从端口读取数据。
open_port()使用详解:
@spec open_port(PortName, [Opt]) -> Port
PortName是如下之一:
{spawn,Command}
启动一个扩展程序。Command是扩展程序的名字。Command会在Erlang的工作空间以外工作,除非找到了叫做Command的内联驱动。
{fd,In,Out}
允许Erlang进程存取一个已经打开的文件描述符。文件描述符 “In” 用作stdin,而文件描述符 “Out” 用作stdout。
Opt是如下之一:
{packet,N}
包前面加上N(1,2,4)字节长度的长度包头。
stream
消息不是按照包长度发送的。应用自己知道如何处理这些包。
{line,Max}
以行为单位传递消息。如果一行大于Max字节,则会切割为只有Max字节。
{cd,Dir}
仅用于 {spawn,Command} 选项,扩展程序的初始目录为Dir。
{env,Env}
仅用于 {spawn,Command} 选项。指定扩展程序可用的环境变量为Env。Env是一个列表的 {VarName,Value} 对。两个变量都是字符串。
这并不是完整的 open_port 参数列表。我们可以在参考手册的erlang模块里找到详细的描述。、
这里举一个例子来说明一下这个函数的使用:
eg:     open_port({spawn, "./c_fun2.exe"}, [{packet, 2}]).
解释:spawn 基本上是固定的关键字,第二项是对应的 exe 文件的路径,最后一项[{packet, 2}]是告诉 erlang端口驱动,发送数据时,给数据头部增加一个长度标记,长度用两个字节表示。
(三)、向端口进程发送消息。
    Port ! {PidC, {command, Data}}
(四)、改变端口绑定的进程(就是我在前面提到的书中的那个错误)。
    Port ! {PidC, {connect, Pid1}}
    说明:把端口连接进程的PID从PidC改为Pid1.
(五)、关闭端口。
    Port ! {PidC, close}
注意:在上面这些使用当中注意列表和元组的区别。列表适用于参数个数不变的情况,而元组的参数个数是可以变化的。

预备知识到此结束。不过,还有一个问题,open_port()函数将进程和端口已经绑定了,为什么在向端口发送消息的时候(    Port ! {PidC, {connect, Pid1}}),还使用了进程的PidC?我是这么考虑的,也许是为了方便端口切换绑定的进程(Port ! {PidC, {connect, Pid1}})。
不知道这样对不对?

注:这个测试程序不是本人所写,是来自网络的。
程序开始:
//echo.c
#include <stdio.h>
#include <unistd.h>
typedef unsigned char byte;
typedef char int8;
int8 read_exact(byte* buf, int8 len);
int8 write_exact(byte* buf, int8 len);
int main() {
    FILE * fp;
    fp = fopen("ports.log", "w");
    fprintf(fp, "start...\n");
    fflush(fp);
    byte buf[256]={0};
    while(read_exact(buf, 1)==1)
    {
        int8 len = buf[0];
        if(read_exact(buf, len)<=0) return -1;
        fprintf(fp, "buf:[%s],len:[%d]\n", buf, len);
        fflush(fp);
        if(write_exact(&len, 1)<=0) return -1;
        if(write_exact(buf, len)<=0) return -1;
    }
    fprintf(fp, "end...\n");
    fclose(fp);
    return 0;
}
int8 read_exact(byte* buf, int8 len)
{
    int i, got=0;
    do {
        if ((i=read(0, buf+got, len-got)) <= 0)
            return (i);
        got += i;
    }while (got < len);
    return (len);
}
int8 write_exact(byte* buf, int8 len)
{
    int i, wrote=0;
    do {
        if ((i= write(1, buf+wrote, len-wrote)) <= 0)
            return (i);
        wrote += i;
    }while (wrote < len);
    return (len);
}
%%echo.erl
%% echo.erl
-module(echo).
-export([start/0, stop/0, echo/1]).

start() ->
    spawn(fun() ->
            register(echo, self()),
            process_flag(trap_exit, true),
            Port = open_port({spawn, "./sun.sh"}, [{packet, 1}]),
            loop(Port)
        end).

stop() ->
    echo ! stop.

echo(Msg) ->
    echo ! {call, self(), Msg},    %% Msg必须是一个List
    receive
        Result -> Result
    after 1000 -> io:format("time out~n"), true
    end.

loop(Port) ->
    receive
        {call, Caller, Msg} ->
            Port ! {self(), {command, Msg}},    %% Msg必须是一个List
            receive
                {Port, {data, Data}} ->     %% 返回的Data也是一个List
                    Caller ! Data
            end,
            loop(Port);
        stop ->
            Port ! {self(), close},
            receive
                {Port, closed} -> exit(normal)
            end;
        {‘EXIT‘, Port, Reason} ->
            io:format("port terminated!~n"),
            exit({port_terminated, Reason})
end.
下面是我的测试过程:
^_^[[email protected] ~/erl/io]86$ gcc echo.c -o echo
^_^[[email protected] ~/erl/io]87$ erl
Erlang R13B04 (erts-5.7.5) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]

Eshell V5.7.5  (abort with ^G)
1> c(echo).
{ok,echo}
2> echo:start().
<0.39.0>
3> echo:echo(["I_love_linux"]). 
"I_love_linux"
4> echo:stop().
stop
5> halt().
^_^[[email protected] ~/erl/io]88$ cat ports.log 
start...
buf:[I_love_linux],len:[12]
end...
^_^[[email protected] ~/erl/io]89$

由此表明,Erlang和C语言连接成功。

拓展:Erlang和Shell脚本的交互。
    相信实现了Erlang和C的交互后,Erlang和shell的交互就是“A piece of cake”。

需要两个文件:ls.sh  echo.erl
#ls.sh
#!/bin/bash
ls > sun
exit 0

%%echo.erl
-module(echo).
-export([start/0, stop/0]).

start() ->
    spawn(fun() ->
            register(echo, self()),
            process_flag(trap_exit, true),
            Port = open_port({spawn, "./ls.sh"}, [stream]),
            loop(Port)
        end).
loop(Port) ->
    io:format("Execute successfully!The port is~p~n", [Port]).

stop() ->
    echo ! stop.
注意:ls.sh必须要有可执行的权限。

^_^[[email protected] ~/erl/io1]16$ erl
Erlang R13B04 (erts-5.7.5) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-poll:false]

Eshell V5.7.5  (abort with ^G)
1> c(echo).
{ok,echo}
2> echo:start().
<0.39.0>
Execute successfully!The port is#Port<0.1972>
3> halt().
^_^[[email protected] ~/erl/io1]17$ cat sun
echo.beam
echo.erl
ls.sh
sun
^_^[[email protected] ~/erl/io1]18$ ls
echo.beam  echo.erl  ls.sh  sun
^_^[[email protected] ~/erl/io1]19$

到此,我们实现虚拟机启动的技术问题,全部攻克了。

时间: 2024-10-20 07:06:57

erlang与c之间的连接的相关文章

Docker学习笔记(9-2)Docker容器之间的连接

学习目标: 容器之间的连接 准备工作 FROM ubuntu:14.04 RUN apt-get install -y ping RUN apt-get update RUN apt-get install -y nginx RUN pat-get install -y curl EXPOSE 80 CMD /bin/bash #构建测试镜像 docker build -t lexiaofei/cct . 1.允许所有容器互联 --icc=true 默认 $ docker run -it --n

List集合之间的连接

之前学习数据库的时候总结过数据库中表和表之间的连接<数据库中多表的连接>,现在做的项目需要要调用其他系统WCF服务返回List集合,从自己系统再返回一部分集合,将这两种集合链接起来再将"连接的集合'返回到界面. 通过查阅资料,有两种方法可以实现List集合之间的连接:一种是利用Linq语句,另外一种是利用lamda表达式,现在总结一下: 数据实体: /// <summary> /// 考试实体 /// </summary> public class Exam

创建数据库之间的连接

数据库之间的连接:(db_link) 通过一台数据库查询另一台数据的数据 服务端: 开启监听 开启数据库fsdb 使用测试数据:system.xxx 表 客户端: 创建:tnsnames.ora  #tns 名字p1 登录到本地一台数据库(非fsdb) 创建db_link: create database link fsdb_link connect to system identified by oracle using 'p1'; #create database link db_link的

浅谈Oracle表之间各种连接

Oracle表之间的连接分为三种: 1.内连接(自然连接) 2.外连接 2.1.左外连接(左边的表不加限制,查询出全部满足条件的结果) 2.2.右外连接(右边的表不加限制,查询出全部满足条件的结果) 2.3.全外连接(左右两边表均不加限制) 3.自连接(同一张表内的连接) SQL的标准写法: select table1.column,table2.column from table1 [inner|left|right|full] join table2 on table1.column1 =

oracle表之间的连接之-----&gt;哈希连接(Hash Join)

哈希连接(HASH JOIN)是一种两个表在做表连接时主要依靠哈希运算来得到连接结果集的表连接方法. 对于排序合并连接,如果两个表在施加了目标SQL中指定的谓词条件后得到的结果集很大而且需要排序,则排序合并连接的执行效率一定不高:而对于嵌套循环连接,如果驱动表所对应的驱动结果集的记录数很大,即便在被驱动表的连接列上存在索引,此时使用嵌套循环连接的执行效率也会同样不高.为了解决这个问题,于是ORACLE引进了哈希连接.在ORACLE 10g及其以后的版本中,优化器 (实际上是CBO,因为哈希连接仅

oracle表之间的连接之------&gt;排序合并连接(Merge Sort Join)

排序合并连接 (Sort Merge Join)是一种两个表在做连接时用排序操作(Sort)和合并操作(Merge)来得到连接结果集的连接方法. 对于排序合并连接的优缺点及适用场景如下: a,通常情况下,排序合并连接的执行效率远不如哈希连接,但前者的使用范围更广,因为哈希连接只能用于等值连接条件,而排序合并连接还能用于其他连接条件(如<,<=,>.>=) b,通常情况下,排序合并连接并不适合OLTP类型的系统,其本质原因是对于因为OLTP类型系统而言,排序是非常昂贵的操作,当然,如

Docker使用Link在容器之间建立连接

原文链接:Docker使用Link在容器之间建立连接 在使用Docker的时候我们会常常碰到这么一种应用,就是我需要两个或多个容器,其中某些容器需要使用另外一些容器提供的服务.比如这么一种情况:我们需要一个容器来提供mysql的数据库服务,而另外两个容器作为客户端来连接使用mysql数据库服务.下面我们就来看看Docker是怎样通过Link来实现这种功能的. 1. 这里我们首先创建两个容器image,一个用来模拟mysql数据库,另外一个使用mysql的client来模拟一些使用mysql服务的

Linux下基于Erlang的高并发TCP连接压力实验

[题解整理]二分题 题目类型: 二分查找: 二分答案. 大致解题思路: 查找注意有序和返回值: 浮点数注意精度: 整数注意返回值,建议另外维护一个变量,用于储存可行解. 题目 分类 传送门 WA点 poj 2785 二分查找 题解 lightoj 1088 二分查找 题解 lightoj 1307 二分查找 题解 longlong poj 2456 整数二分答案 题解 poj 3104 整数二分答案 题解 poj 3258 整数二分答案 题解 poj 3273 整数二分答案 题解 lightoj

Socket编程——怎么实现一个服务器多个客户端之间的连接

  1 package coreBookSocket; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 /* 8 * 这个方法的主要目地是为了用多线程的方法实现网络编程,让多个客户端可以同时连接到一个服务器 9 *1:准备工作和单个客户端编程类似,先建立服务器端的套接字,同时让客户端那边调用accept()方法来接受服务器端的信息 10 *2:这里面定一个w