Recieve packet from socket in Erlang active mode with ASCII packet size header

George Hope george@REDACTED
Mon Jan 4 14:40:39 CET 2021


I have Erlang SSL TCP socket which has a permanent TCP connection to other party,

we use a protocol similar to ISO8583 protocol four first byte is a packet size which is ASCII encoded.

Based on Erlang inet documentation (https://erlang.org/doc/man/inet.html) It only supports unsigned integer for packet size.

 

``` The header length can be one, two, or four bytes, and containing an unsigned integer in big-endian byte order.```



right now I use gen_server handle_info, as soon as I receive a packet I read first four byte then compare it to Binary size and if the binary size is small I do nothing and put recieved binary to LastBin and wait for rest of packet and If more than one msg is in packet I call read_iso packet several times, short sample if what I do is like this:

```

handle_info({ConnType, _Socket, Bin}, {BSSl, BProtocol, Psize, OSocket, LastBin})

    when ConnType =:= ssl; ConnType =:= tcp ->

    logger:debug("mp_back:Got response from backend~w", [Bin]),

          read_iso_packet(Bin, {BSSl, BProtocol, Psize, OSocket, LastBin})

    end.

read_iso_packet(Bin, {BSSl, BProtocol, Psize, Socket, LastBin})

    when size(<<LastBin/binary, Bin/binary>>) < 5 ->

    {noreply, {BSSl, BProtocol, Psize, Socket, <<LastBin/binary, Bin/binary>>}};

read_iso_packet(Bin, {BSSl, BProtocol, Psize, Socket, LastBin})

    when size(<<LastBin/binary, Bin/binary>>) > 4 ->

    Packe_Size = get_packet_size(binary:part(<<LastBin/binary, Bin/binary>>, 0, 4)),

    logger:debug("mp_back:packet_size==~w", [Packe_Size]),

    logger:debug("mp_back:bin_size==~w", [size(Bin)]),

    read_iso_packet(Packe_Size + 4 - size(<<Bin/binary, LastBin/binary>>),

                    Packe_Size,

                    <<LastBin/binary, Bin/binary>>,

                    {BSSl, BProtocol, Psize, Socket, LastBin}).



read_iso_packet(0, _Packe_Size, Bin, {BSSl, BProtocol, Psize, Socket, _LastBin}) ->

    do_somthing(server_response, CSocket, Bin),

    {noreply, {BSSl, BProtocol, Psize, Socket, <<>>}};

read_iso_packet(SS, Packe_Size, Bin, {BSSl, BProtocol, Psize, Socket, _LastBin})

    when SS < 0 ->

    do_somthing(server_response, CSocket, [binary:part(Bin, 0, Packe_Size + 4)]),        

    read_iso_packet(binary:part(Bin, Packe_Size + 4, byte_size(Bin) - (Packe_Size + 4)),

                    {BSSl, BProtocol, Psize, Socket, <<>>});

read_iso_packet(SS, _Packe_Size, Bin, {BSSl, BProtocol, Psize, Socket, _LastBin})

    when SS > 0 ->

    logger:debug("mp_back: Small data going to read next~w", [Bin]),

    {noreply, {BSSl, BProtocol, Psize, Socket, Bin}}.



get_packet_size(Bin) ->

    {ok, [A], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 0, 1))),

    {ok, [B], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 1, 1))),

    {ok, [C], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 2, 1))),

    {ok, [D], _} = io_lib:fread("~16u", binary_to_list(binary:part(Bin, 3, 1))),

    A * 1000 + B * 100 + C * 10 + D.

```

my questions are:



 1. is there a better way to do this? last time I made a mistake and read some messages more than once(I fixed that bug, but did not test my code in production yet, but it seems OK in tests). when packet size is unsigned integer Erlang can handle this for me but no success with ASCII encoded packet size.

 2. I tried to use {active, once} and  gen_tcp:recv but It did not work properly for me, Is this a safer approach?

 3. Is gen_server:handle_info synchronous?
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20210104/7ed2cd6b/attachment.htm>


More information about the erlang-questions mailing list