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

Jesper Louis Andersen jesper.louis.andersen@REDACTED
Mon Jan 4 15:20:48 CET 2021


On Mon, Jan 4, 2021 at 2:40 PM George Hope <george@REDACTED> wrote:

> 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?
>
>
1. Rough idea:

When you get a new bin in, you can probably just append it directly:

handle_info({ConnType, _Sock, Data}, SoFar) ->
    read_iso_packet(<<SoFar/binary, Data/binary>>, ...).

This then makes the rest of the code simpler since it can just inspect that
singular data block, trying to cut the head out of the binary in order to
decode the ASCII value. Some solutions will return either {more, N, Bin} if
more data is needed (N bytes) or {packet, Packet, Rest} if a packet was
decoded. You then loop again on Rest after having handled the packet
because you might have enough data for the next message already in the
buffer. But this depends a bit on your socket mode being active or passive.
It's often easier to just try decoding from the head. If you can get a full
packet out, process and recur. If you don't have enough data, stuff it into
the buffer and wait for more data to arrive. Unless you are looking at very
high loads this is going to be efficient.

2. {active, once} and {active, N} are very recommended! You call
inet:setopts/2 on the socket once you have received a message:

handle_info({ConnType, Socket, Data}, #state { socket = Socket, buffer =
SoFar} = State) ->
    ok = inet:setopts(Socket, [{active, once}]),
    read_iso_packet(<<SoFar/binary, Data/binary>>, State);
It allows you to do other things in the process while you are waiting on
the network socket for more data, increasing responsiveness of your system.
Furthermore, it provides backpressure on the TCP socket if you end up in a
situation where the sender outpaces your receiver. {active, once} is
probably fast enough for most cases.

3. It's not clear what you mean by synchronous here. Other TCP data might
arrive on the socket while you are executing handle_info/2 but as soon as
you return it'll get called again with the new data. There's only a single
process executing the handle_info/2 call, so you have some very neat
linearity guarantees and you don't have to worry about a lot of problems
that would normally be present in code using critical sections (mutexes,
condition variables, ...).



-- 
J.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20210104/4cc241e6/attachment.htm>


More information about the erlang-questions mailing list