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.
下面是我的测试过程:
^_^[sunny@localhost ~/erl/io]86$ gcc echo.c -o echo
^_^[sunny@localhost ~/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().
^_^[sunny@localhost ~/erl/io]88$ cat ports.log
start...
buf:[I_love_linux],len:[12]
end...
^_^[sunny@localhost ~/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必须要有可执行的权限。
^_^[sunny@localhost ~/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().
^_^[sunny@localhost ~/erl/io1]17$ cat sun
echo.beam
echo.erl
ls.sh
sun
^_^[sunny@localhost ~/erl/io1]18$ ls
echo.beam echo.erl ls.sh sun
^_^[sunny@localhost ~/erl/io1]19$
到此,我们实现虚拟机启动的技术问题,全部攻克了。
erlang与c之间的连接的更多相关文章
- Socket编程——怎么实现一个服务器多个客户端之间的连接
package coreBookSocket; import java.io.IOException; import java.net.ServerSocket; import java.net. ...
- 浅谈Oracle表之间各种连接
Oracle表之间的连接分为三种: 1.内连接(自然连接) 2.外连接 2.1.左外连接(左边的表不加限制,查询出全部满足条件的结果) 2.2.右外连接(右边的表不加限制,查询出全部满足条件的结果) ...
- 【Xamarin挖墙脚系列:配置Mac之间的连接问题】
原文:[Xamarin挖墙脚系列:配置Mac之间的连接问题] 首先建议把MAC的防火墙关掉,呵呵, 其次,去设置里,允许所有用户远程登录连接MAC
- ALSA声卡驱动中的DAPM详解之五:建立widget之间的连接关系
前面我们主要着重于codec.platform.machine驱动程序中如何使用和建立dapm所需要的widget,route,这些是音频驱动开发人员必须要了解的内容,经过前几章的介绍,我们应该知道如 ...
- Linux下基于Erlang的高并发TCP连接压力实验
1.实验环境: 联想小型机: 操作系统:RedHat Enterprise LinuxServer release6.4(Santiago) 内核版本号:Linux server1 2.6.32-35 ...
- Docker学习笔记 - Docker容器之间的连接
学习目标: 容器之间可以相互连接访问:: --link redis:redisAlias 准备工作 FROM ubuntu:14.04 RUN apt-get install -y ping RUN ...
- Docker使用Link与newwork在容器之间建立连接
一,使用 --link容器互联 docker 默认使允许container 互通的(通过-icc=false 关闭互通)同一个宿主机上的多个docker容器之间如果想进行通信,可以通过使用容器的ip地 ...
- ROS Learning-016 Arduino-For-ROS-001 搭建 Arduino 和 ROS 之间相连接的开发环境
Arduino For ROS-001 - 搭建 ROS 和 Arduino 相连接的开发环境 我的Ubuntu系统:Ubuntu 14.04.10 TLS 32位 Arduino的版本:Arduin ...
- oracle 表之间的连接
排序 - - 合并连接(Sort Merge Join, SMJ): a) 对于非等值连接,这种连接方式的效率是比较高的. b) 如果在关联的列上都有索引,效果更好. c) 对于将2个较大的row s ...
随机推荐
- HTML基础第四讲---图像
转自:https://blog.csdn.net/likaier/article/details/326735 图像,也就是images,在html语法中用img来表示,其基本的语法是: < ...
- python登录验证程序
自己写的一个python登录验证程序: 基础需求: 让用户输入用户名密码 认证成功后显示欢迎信息 输错三次后退出程序 升级需求: 可以支持多个用户登录 (提示,通过列表存多个账户信息) 用户3次认证失 ...
- Docker---(8)Docker启动Redis后访问不了
原文:Docker---(8)Docker启动Redis后访问不了 版权声明:欢迎转载,请标明出处,如有问题,欢迎指正!谢谢!微信:w1186355422 https://blog.csdn.net/ ...
- 00091_字符输入流Reader
1.字符输入流Reader (1)字符输入流Reader我们读取拥有中文的文件时,使用的字节流在读取,那么我们读取到的都是一个一个字节: (2)只要把这些字节去查阅对应的编码表,就能够得到与之对应的字 ...
- Android开发人员应该知道的Kotlin
本文来源于我在InfoQ中文站翻译的文章,原文地址是:http://www.infoq.com/cn/news/2016/01/kotlin-android Android开发人员在语言限制方面面临着 ...
- 逻辑与和逻辑或:&& 、||
逻辑或:首先看左边是真还是假(除了那5个都是真),如果为真,返回左边值,如果为假,返回右边的值 逻辑与:和逻辑或相同,先看左边的值是真是假,如果左边为真返回右边的值,左边为假返回左边的值 在两者同时出 ...
- OC学习篇之---协议的概念和用法
这一篇文章我们在来看一下OC中协议的概念以及用法,协议也是OC中的一个重点,Foundation框架以及我们后面在写代码都会用到. OC中的协议就是相当于Java中的接口(抽象类),只不过OC中的名字 ...
- [AngularFire 2] Joins in Firebase
Lets see how to query Firebase. First thing, when we do query, 'index' will always help, for both SQ ...
- FastSocket学习笔记~RPC的思想,面向对象的灵活
首先非常感谢这位来自新浪的老兄,它开发的这个FastSocket非常不错,先不说性能如何,单说它的使用方式和理念上就很让人赞口,从宏观上看,它更像是一种远程过程的调用RPC,即服务器公开一些命令,供客 ...
- 22、在Ubuntu 14.0上使用韦东山IP2977测试总结(未成功)
1. 去www.kernel.org下载同版本的内核(与Ubuntu 14.0) 解压后把drivers/media/video/uvc目录取出(发现我的3.13版本的在drivers\media\u ...