Coder Social home page Coder Social logo

enet's Introduction

enet

A complete re-implementation of the ENet protocol in Erlang/OTP.

API

The module enet presents the API of enet.

Data Types

port_number() = 0..65535

peer_count() = 1..255

channel_count() = 1..255

bytes_per_second() = non_neg_integer()

channels() = #{ non_neg_integer() := pid() }

Functions

start_host/3

start_host(Port, ConnectFun, Options) -> {ok, port_number()} | {error, term()}

    Port = port_number()
    ConnectFun = mfa() | fun((PeerInfo) -> ConnectFunResult)
    PeerInfo = map()
    ConnectFunResult = {ok, pid()} | {error, term()}
    Options = [Option]
    Option =
      {peer_limit, peer_count()} |
      {channel_limit, channel_count()} |
      {incoming_bandwidth, bytes_per_second()} |
      {outgoing_bandwidth, bytes_per_second()}

Start a new host. If Port set to 0, the port will be dynamically assigned by the underlying operating system. The assigned port is returned.

The ConnectFun function or MFA tuple will be called when a new peer has started and a connection to a remote peer has been established. This function is expected to spawn a new process and return a pid to which all messages from the new peer will be sent.

stop_host/1

stop_host(Port) -> ok

    Port = port_number()

Stop a host listening on Port.

connect_peer/4

connect_peer(HostPort, IP, RemotePort, ChannelCount) -> {ok, Peer} | {error, atom()}

    HostPort = port_number()
    IP = string()
    RemotePort = port_number()
    ChannelCount = channel_count()
    Peer = pid()

Start a new peer on the host listening on HostPort connecting to a remote host on address IP:RemotePort. The peer process will call ConnectFun (given to start_host/3) when the handshake has been completed successfully.

disconnect_peer/1

disconnect_peer(Peer) -> ok

    Peer = pid()

Disconnect Peer.

disconnect_peer_now/1

disconnect_peer_now(Peer) -> ok

    Peer = pid()

Disconnect Peer immediately without waiting for an ACK from the remote peer.

send_unsequenced/2

send_unsequenced(Channel, Data) -> ok

    Channel = pid()
    Data = iodata()

Send unsequenced data to the remote peer over Channel.

send_unreliable/2

send_unreliable(Channel, Data) -> ok

    Channel = pid()
    Data = iodata()

Send unreliable data to the remote peer over Channel.

send_reliable/2

send_reliable(Channel, Data) -> ok

    Channel = pid()
    Data = iodata()

Send reliable data to the remote peer over Channel.

broadcast_unsequenced/3

broadcast_unsequenced(HostPort, ChannelID, Data) -> ok

    HostPort = port_number()
    ChannelID = integer()
    Data = iodata()

Broadcast unsequenced data to all peers connected to HostPort on ChannelID.

broadcast_unreliable/3

broadcast_unreliable(HostPort, ChannelID, Data) -> ok

    HostPort = port_number()
    ChannelID = integer()
    Data = iodata()

Broadcast unreliable data to all peers connected to HostPort on ChannelID.

broadcast_reliable/3

broadcast_reliable(HostPort, ChannelID, Data) -> ok

    HostPort = port_number()
    ChannelID = integer()
    Data = iodata()

Broadcast reliable data to all peers connected to HostPort on ChannelID.

Examples

Creating an ENet server

ListeningPort = 1234,
ConnectFun = fun(PeerInfo) ->
                     server_supervisor:start_worker(PeerInfo)
             end,
Options = [{peer_limit, 8}, {channel_limit, 3}],
{ok, Host} = enet:start_host(ListeningPort, ConnectFun, Options),
...
...
...
enet:stop_host(Host).

Creating an ENet client and connecting to a server

ListeningPort = 0, %% Port will be dynamically assigned
ConnectFun = fun(PeerInfo) ->
                     client_supervisor:start_worker(PeerInfo)
             end,
Options = [{peer_limit, 1}, {channel_limit, 3}],
{ok, Host} = enet:start_host(ListeningPort, ConnectFun, Options),
...
...
RemoteHost = "...."
Port = 1234,
ChannelCount = 3
{ok, Peer} = enet:connect_peer(Host, RemoteHost, Port, ChannelCount),
...
...
enet:stop_host(Host).

Sending packets to an ENet peer

worker_loop(PeerInfo = #{channels := Channels}, State) ->
    ...
    ...
    {ok, Channel0} = maps:find(0, Channels),
    Data = <<"packet">>,
    enet:send_unsequenced(Channel0, Data),
    enet:send_unreliable(Channel0, Data),
    enet:send_reliable(Channel0, Data),
    ...
    ...
    worker_loop(PeerInfo, State).

Receiving packets from an ENet peer

worker_loop(PeerInfo, State) ->
    ...
    ...
    receive
        {enet, ChannelID, #unsequenced{ data = Packet }} ->
            %% Handle the Packet
            ok;
        {enet, ChannelID, #unreliable{ data = Packet }} ->
            %% Handle the Packet
            ok;
        {enet, ChannelID, #reliable{ data = Packet }} ->
            %% Handle the Packet
            ok
    end,
    ...
    ...
    worker_loop(PeerInfo, State).

enet's People

Contributors

ablu avatar flambard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

enet's Issues

Only one client accepted

it looks like only the first coming client is served. The second client is only able to successfully connect after the other connection was terminated.

Compatibility with C based (original) enet

Hi,

I wonder, is this library compatible / tested against the C based enet implementation?

To me it looks like the Compressed and SentTimeIncluded bits are swapped compared to the official enet. But even after changing that I run into issues with the command parsing...

Regards,
Ablu

out-of-order reliable packets

Hi! I have a question/issue about what happens when out-of-order reliable packets come in.

I see here https://github.com/flambard/enet/blob/master/src/enet_channel.erl#L147-L150
That you have the following:

        {recv_reliable, {
           #command_header{ reliable_sequence_number = N },
           C = #reliable{}
          }} when N =:= S#state.incoming_reliable_sequence_number ->

It seems to me that this is only matching a packet if the packet's sequence number matches the expected incoming_reliable_sequence_number in your state record.

What happens if the packet comes out of order? I have the impression from the ENet docs[1] that the server is responsible for buffering packets until the wanted next packet comes in. E.g, if I send packets [1-10] from the client, and the next packet the server receives is packet 12, then it should buffer packet 12 and wait to deliver it until after packet 11 arrives from the client.

The reason I'm asking is because I think I've seen some crashes related to this, but I'm not 100% sure. I would be happy to help take a stab at fixing it, though, if you agree that it is a problem.

[1] http://enet.bespin.org/Features.html
Relevant lines:

For reliable packets, if a higher sequence number packet arrives, but the preceding packets in the sequence have not yet arrived, ENet will stall delivery of the higher sequence number packets until its predecessors have arrived.

How to run the tests?

After an attempt to fix #6 I tried to run the tests after a change, but all the tests fail:

~/mana/enet $ rebar3 eunit
===> Verifying dependencies...
===> Fetching proper ({pkg,<<"proper">>,<<"1.2.0">>})
===> Version cached at /home/ablu/.cache/rebar3/hex/default/packages/proper-1.2.0.tar is up to date, reusing it
===> Fetching gproc ({pkg,<<"gproc">>,<<"0.8.0">>})
===> Version cached at /home/ablu/.cache/rebar3/hex/default/packages/gproc-0.8.0.tar is up to date, reusing it
===> Linking _build/default/lib/gproc to _build/test/lib/gproc
===> Compiling proper
make: 'include/compile_flags.hrl' is up to date.
/home/ablu/mana/enet/_build/test/lib/proper/src/proper_typeserver.erl:553: Warning: erlang:get_stacktrace/0 used following a 'try' expression may stop working in a future release. (Use it inside 'try'.)
/home/ablu/mana/enet/_build/test/lib/proper/src/proper_typeserver.erl:556: Warning: erlang:get_stacktrace/0 used following a 'try' expression may stop working in a future release. (Use it inside 'try'.)
_build/test/lib/proper/src/proper_typeserver.erl:553: Warning: erlang:get_stacktrace/0 used following a 'try' expression may stop working in a future release. (Use it inside 'try'.)
_build/test/lib/proper/src/proper_typeserver.erl:556: Warning: erlang:get_stacktrace/0 used following a 'try' expression may stop working in a future release. (Use it inside 'try'.)

===> Compiling gproc
_build/default/lib/gproc/src/gproc_dist.erl:25: Warning: behaviour gen_leader undefined

===> Compiling enet
_build/test/lib/enet/test/enet_model.erl:340: Warning: function busy_host_port/1 is unused
_build/test/lib/enet/test/enet_model.erl:355: Warning: function peer_pid/1 is unused
_build/test/lib/enet/test/enet_model.erl:360: Warning: function connect_id/1 is unused
_build/test/lib/enet/test/enet_model.erl:379: Warning: function local_ip/0 is unused

===> Performing EUnit tests...
FFFFFFFFFFFFFF
Failures:

  1) enet_tests:local_zero_peer_limit_test/0
     Failure/Error: {exit,
                        {noproc,
                            {gen_server,call,
                                [enet_sup,
                                 {start_child,
                                     #{id => 5001,
                                       modules => [enet_host_sup],
                                       restart => temporary,
                                       shutdown => infinity,
                                       start => {enet_host_sup,start_link,[]},
                                       type => supervisor}},
                                 infinity]}},
                        [{gen_server,call,3,
                             [{file,"gen_server.erl"},{line,214}]},
                         {enet,start_host,2,
                             [{file,
                                  "/home/ablu/mana/enet/_build/test/lib/enet/src/enet.erl"},
                              {line,22}]},
                         {enet_tests,local_zero_peer_limit_test,0,
                             [{file,
                                  "/home/ablu/mana/enet/_build/test/lib/enet/src/enet_tests.erl"},
                              {line,8}]}]}
     Output:

The other errors seem to be the same / similar... But this is unrelated to my changes (it happens on clean master)

Bug with start_host

gen_udp opens two times, the first time here:

{ok, Socket} = gen_udp:open(Port, enet_host:socket_options()),

and a second time here:
case gen_udp:open(AssignedPort, socket_options()) of

as result I have two ports:
image
and match error in handle_info, because Socket in State and Socket in the message disagrees:

enet/src/enet_host.erl

Lines 205 to 209 in 5a493ff

#state{
socket = Socket,
decompress_fun = Decompress,
connect_fun = ConnectFun
} = S,

I think it's because of this option reuseaddr:

[binary, {active, false}, {reuseaddr, true}, {broadcast, true}].

docs

Could you make some docs?

Or maybe expand test so it will set up some listener and send packet to it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.