C和Erlang:Erlang Port示例

ska*_*tek 8 c erlang stdin stdout erlang-ports

免责声明:该问题的作者具有Erlang的平均知识和C的基本知识.

我现在正在阅读互操作性教程用户指南.我已经成功编译了这个complex.c例子,它与Erlang Port一起使用没有任何问题.

但是,我想了解实际的C代码是如何工作的.我一般都理解它:在示例中,它从标准输入读取2个字节并检查第一个字节.取决于它调用的第一个字节foobar函数.这是我现在对它的理解的极限.

所以,如果我们同时采取两个erl_comm.c:

/* erl_comm.c */

typedef unsigned char byte;

read_cmd(byte *buf)
{
  int len;

  if (read_exact(buf, 2) != 2)
    return(-1);
  len = (buf[0] << 8) | buf[1];
  return read_exact(buf, len);
}

write_cmd(byte *buf, int len)
{
  byte li;

  li = (len >> 8) & 0xff;
  write_exact(&li, 1);

  li = len & 0xff;
  write_exact(&li, 1);

  return write_exact(buf, len);
}

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);
}

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);
}
Run Code Online (Sandbox Code Playgroud)

并且port.c:

/* port.c */

typedef unsigned char byte;

int main() {
  int fn, arg, res;
  byte buf[100];

  while (read_cmd(buf) > 0) {
    fn = buf[0];
    arg = buf[1];

    if (fn == 1) {
      res = foo(arg);
    } else if (fn == 2) {
      res = bar(arg);
    }

    buf[0] = res;
    write_cmd(buf, 1);
  }
}
Run Code Online (Sandbox Code Playgroud)

每个功能实际上做了什么?li, len, i, wrote, got变量实际上有什么用途?

一些小问题:

  1. 为什么函数没有任何返回类型,甚至voids?
  2. 当Erlang端口向C发送数据时,第一个字节确定要调用的函数.如果该字节保存小数1,则foo()调用,如果该字节保存小数2,则bar()调用.如果不改变,无论如何这个协议可用于调用多达255个不同的C函数,每个函数只有1个参数.是对的吗?
  3. "添加长度指示符将由Erlang端口自动完成,但必须在外部C程序中明确完成".那是什么意思?它在哪行代码上完成?
  4. 从教程:"默认情况下,C程序应从标准输入(文件描述符0)读取并写入标准输出(文件描述符1)." 然后:"注意stdin和stdout用于缓冲输入/输出,不应该用于与Erlang的通信!" 这里有什么收获?
  5. 为什么buf被初始化为[100]

Ina*_*thi 6

这个答案同样被放弃了(我不是Erlang或C程序员,我恰好经历了相同的材料)

你的初始模型有点偏.代码实际工作的方式是从前两个字节读取stdin,假设它表示实际消息的长度,然后从中读取更多的字节stdin.在这种特定情况下,实际消息总是两个字节(一个对应于一个函数的数字和一个传递给它的单个整数参数).

0 - a) read_exact从中读取len字节stdin,首先read_cmd使用read_exact它来确定它应该读取多少字节(前两个字节表示的数字,或者如果可用的字节少于两个,则为无),然后读取那么多字节.write_exact写入len字节stdout,write_cmd用于write_exact输出两个字节长度的头,然后是一个适当长度的消息(希望).

0 - b)我认为len上面已经充分涵盖了.li是用于为写入函数生成该双字节头的变量的名称(我不能逐步引导您完成位移操作,但最终结果是len在发送的前两个字节中表示).i是一个中间变量,其主要目的似乎是确保write并且read不返回错误(如果它们这样做,则错误代码作为read_exact/ 的结果返回write_exact).wrotegot跟踪已写入/读取的字节数,包含的循环在大于之前退出len.

1 -我其实不确定.我正在使用的版本是类型int,但在其他方面相同.我从编程Erlang的第12章开始,而不是你链接的指南.

2 -这是正确的,但端口协议的要点是你可以改变它以发送不同的参数(如果你发送任意参数,那么使用C Node方法而不是端口可能是更好的主意).作为一个例子,我在最近的一个片段中巧妙地修改它,以便它发送一个字符串,因为我只有一个我想在C侧调用的函数,因此无需指定函数.我还要提一下,如果你的系统需要调用超过255个用C语言编写的不同操作,你可能需要重新考虑它的结构(或者只是整个九个并用C语言写出来).

3 -这样做了

read_cmd(byte *buf)
{
  int len;

  if (read_exact(buf, 2) != 2)   // HERE
    return(-1);                  // HERE
  len = (buf[0] << 8) | buf[1];  // HERE
  return read_exact(buf, len);
}
Run Code Online (Sandbox Code Playgroud)

read_cmd功能和

write_cmd(byte *buf, int len)
{
  byte li;

  li = (len >> 8) & 0xff;        // HERE
  write_exact(&li, 1);           // HERE

  li = len & 0xff;               // HERE
  write_exact(&li, 1);           // HERE

  return write_exact(buf, len);
}
Run Code Online (Sandbox Code Playgroud)

write_cmd功能中.我认为解释说明了0 - a); 这是一个标题告诉/发现消息的其余部分将是多长时间(是的,这意味着它只能是一个有限长度,并且该长度必须以两个字节表示).

4 -我不完全确定为什么这会成为一个问题.注意详细说明?

5 - buf是一个字节数组,必须明确限定(出于内存管理的目的,我认为).我100在这里读到" 比我们计划容纳的最大邮件大小更大的数字".选择的实际数字似乎是任意的,似乎任何4或更高的数字都可以,但我可以在这一点上得到纠正.

  • 考虑所涉及的比特.`0,2` - >`[00000000],[00000010]`.`len`是一个整数,所以它实际上是4个字节; `[00000000:00000000:00000000:00000000]`.执行`[00000000] << 8`将左移8位(在这种情况下,它没有区别,因为第一位是0),给你`[00000000:00000000]`.做`([00000000] << 8)| [00000010]`离开[00000000:00000010],这是"短整数2"的位表示.这被分配给'len`,剩下两个字节(因此你可以在技术上将`len`声明为`short integer`,没有任何不良影响). (4认同)