簡體   English   中英

使用C中的簡單字節協議讀/寫大整數或浮點數

[英]Read/write big integers or floats with a simple byte protocol in C

免責聲明:該問題的作者具有Erlang的平均知識和C的基本(但不斷增加的)知識。

我正在使用read()函數來讀取我的port.c程序從“ 互操作性教程用戶指南”中提供的Erlang端口示例中獲取的字節(在“Erlang編程”一書的第12章中也有描述)。

但我傾向於認為問題根本不是與Erlang相關的,因為我得到的錯誤值(例如231而不是999)來自C方面。

問題是這個協議不適用於超過255的參數(否則它很好用)。 我想它與byte類型和read_exact()實現有關,但我不知道如何修復它或者可以在其中傳遞float值。

為了理解這段代碼,我已經閱讀了K&R書的一半,但我仍然被卡住了。

這是代碼:

實際C函數:

/* complex.c */

int foo(int x) {
  return x+1;
}

int bar(int y) {
  return y*2;
}

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

緩沖管理:

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

二郎港:

-module(complex1).
-export([start/1, stop/0, init/1]).
-export([foo/1, bar/1]).

start(ExtPrg) ->
    spawn(?MODULE, init, [ExtPrg]).
stop() ->
    complex ! stop.

foo(X) ->
    call_port({foo, X}).
bar(Y) ->
    call_port({bar, Y}).

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

init(ExtPrg) ->
    register(complex, self()),
    process_flag(trap_exit, true),
    Port = open_port({spawn, ExtPrg}, [{packet, 2}]),
    loop(Port).

loop(Port) ->
    receive
    {call, Caller, Msg} ->
        Port ! {self(), {command, encode(Msg)}},
        receive
        {Port, {data, Data}} ->
            Caller ! {complex, decode(Data)}
        end,
        loop(Port);
    stop ->
        Port ! {self(), close},
        receive
        {Port, closed} ->
            exit(normal)
        end;
    {'EXIT', Port, Reason} ->
        exit(port_terminated)
    end.

encode({foo, X}) -> [1, X];
encode({bar, Y}) -> [2, Y].

decode([Int]) -> Int.

我做了一個愚蠢的改變嘗試

typedef unsigned char byte;

typedef int byte;

但它不起作用。

實際上,有兩個問題:

  • 如果我們使用大於255的參數(例如來自Erlang端口的foo(256)調用端口,則執行將在read_cmd()內的read()處終止,其中i = 0;
  • 如果我們用一個小於255的參數調用該端口,但是該函數的結果大於255(比如int foo(int x) { return x+1000; } ,那么程序不會終止,但是我們得到了我們的Erlang端口有一些意想不到的價值;

所以,問題是:為了使協議能夠使用更大的數字甚至浮點數,我該怎么做?

您可能遇到的任何計算機上的字節只能保存-128到127(有符號)或0到255(無符號)的值; 其他任何事情都需要在兩者之間編組數據。 http://www.erlang.org/documentation/doc-5.6/pdf/tutorial.pdf是用於在Erlang和其他語言之間編組數據的官方Erlang教程,並包含C語言中的示例。

read_cmd已經存在將byte編組為較大構造的read_cmd

...
  int len;

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

buf取出前兩個byte ,並將它們視為int (四byte結構)。 根據這個double是8個字節,所以理論上,你應該能夠應用相同的方法。 你想要做的事情(未經測試,幾乎保證不起作用,僅用於說明目的等):

double marshall_eight(byte *buff) {
  int i;
  double tmp, res;
  for(i=0; i<8;i++) {
    tmp = buff[i] << 8*i;
    res += tmp;
  }
  return res;
}

假設從Erlang轉換為字節數組而不是字符數組。

代碼有點寬松,最簡單的答案......

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

本來應該:

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

您的代碼只處理字節和整數,它不能處理浮點數和雙精度數。

您的寫入cmds中還有其他錯誤。

------------編輯-----------------這是你想要在c中實現的一個例子:

 #include <stdio.h>
 #include <math.h>

 /*
  * With certain compilers  __attribute__((transparent_union)) 
  * can be used to ease handling of an unknown type.
  */
 union  u_unionalue
 {
     char                char_union;
     unsigned char       u_char_union;
     short               s_int_union;
     unsigned short      us_int_union;
     int                 int_union;
     unsigned int        u_int_union;
     long                l_int_union;
     unsigned long       ul_int_union;
     long long           ll_int_union;
     unsigned long long  ull_int_union;
     float               float_union;
     double              double_union;
     long double         l_double_union;
 };

 enum e_type
 {
     char_enum    ,
     u_char_enum  ,
     s_int_enum   ,
     us_int_enum  ,
     int_enum     ,
     u_int_enum   ,
     l_int_enum   ,
     ul_int_enum  ,
     ll_int_enum  ,
     ull_int_enum ,
     float_enum   ,
     double_enum  ,
     l_double_enum,
     last_type
 };

 struct s_type
 {
     int  type;
     char *name;
     union u_unionalue value;
     int   size;
     char *stringFormat;

 } as_typeList[]=
 /**
  * This is a quick example of how convoluted type handling can be in C. The 
  * non portable  __attribute__((transparent_union)) can be useful if the 
  * complier supports it. This helps to 
  * reduce the amount of casting, but these are the convoluted tricks that 
  * occur behind the scenes. C++ has to handle this in the compiler as well
  * as a result .. sometimes what you get is not what you expect.
  */
 {
     { char_enum    ,  "char"              ,  {.char_union=(1 << (-1 + sizeof( char ) * 8  ))}, sizeof( char ),"%+d" },
     { u_char_enum  ,  "unsigned char"     ,  {.u_char_union=-1} , sizeof( unsigned char ) ,"%+d" },
     { s_int_enum   ,  "short"             ,  {.s_int_union=((short)1 << (-1 + sizeof( short ) * 8))}  , sizeof( short ),"%+d" },
     { us_int_enum  ,  "unsigned short"    ,  {.us_int_union=-1}, sizeof( unsigned short ),"%+u"  },
     { int_enum     ,  "int"               ,  {.int_union = ((int)1<< (-1 + sizeof( int) * 8  ))}, sizeof( int), "%+i"  },
     { u_int_enum   ,  "unsigned int"      ,  {.u_int_union=-1}, sizeof( unsigned int ), "%+u"  },
     { l_int_enum   ,  "long"              ,  {.l_int_union=((long)1<< (-1 + sizeof( long) * 8  ))}, sizeof( long ), "%+li" },
     { ul_int_enum  ,  "unsigned long"     ,  {.ul_int_union=(long)-1}, sizeof( unsigned long ), "%+lu" },
     { ll_int_enum  ,  "long long"         ,  {.ll_int_union=(long long)-1 }, sizeof( long long ), "%+lli"},
     { ull_int_enum ,  "unsigned long long",  {.ull_int_union=((unsigned long long)1<< (-1 + sizeof( unsigned long long) * 8  ))}, sizeof( unsigned long long  ), "%+llu"},
     { float_enum   ,  "float"             ,  {.float_union=1e+37L}, sizeof( float ), "%+f"  },
     { double_enum  ,  "double"            ,  {.double_union=1e+37L}, sizeof( double ), "%+lf"  },
     { l_double_enum,  "long double"       ,  {.l_double_union=1e+37L}, sizeof( long double), "%+lle"}
 };


 /**
  * This is how your foo and bar functions should be organized this would 
  * allow handling for all your types. but the type is needed.
  */
 void sprintVal(struct s_type *typeVal, char*buf)
 {
     switch(typeVal->type)
     {
     case char_enum     :
         sprintf(buf, typeVal->stringFormat, typeVal->value.char_union);
         break;
     case u_char_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.u_char_union);
         break;
     case s_int_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.s_int_union);
         break;
     case us_int_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.us_int_union);
         break;
     case int_enum      :
         sprintf(buf, typeVal->stringFormat, typeVal->value.int_union);
         break;
     case u_int_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.u_int_union);
         break;
     case l_int_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.l_int_union);
         break;
     case ul_int_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.ul_int_union);
         break;
     case ll_int_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.ll_int_union);
         break;
     case ull_int_enum  :
         sprintf(buf, typeVal->stringFormat, typeVal->value.ull_int_union);
         break;
     case float_enum    :
         sprintf(buf, typeVal->stringFormat, typeVal->value.float_union);
         break;
     case double_enum   :
         sprintf(buf, typeVal->stringFormat, typeVal->value.double_union);
         break;
     case l_double_enum :
         sprintf(buf, typeVal->stringFormat, typeVal->value.l_double_union);
         break;
     }
 }


 void print_types()
 {
     int i=0;
     char buf[100];

     while(i < last_type )
     {
         sprintVal( &as_typeList[i], buf);
         printf( "Type: %-18s value=%-30s size=  %-dBytes \n", as_typeList[i].name, buf, as_typeList[i].size );
         i++;
     };
 }

 int main(int argc, char** argv)
 {

     print_types();
     return(0);
 }

您的問題中最大的問題是您在erlang和c之間發送的消息。 您當前的格式是/ CMD / VALUE /最小,這應該是/ CMD / TYPE / VALUE /,盡管消息頭和校驗和頁腳的常用內容很常見。 應該提升類型,以便可以返回更大的結果值。 所以需要知道你要傳遞的是什么。

哦,如果您將數據作為字符串傳遞,您可以在一定程度上適當地調整您的類型。 如果管道末端之間存在差異,它還可以防止端點問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM