GithubHelp home page GithubHelp logo

gen_nb_server's Introduction

Description

gen_nb_server is an OTP behavior designed to simplify writing completely non-blocking TCP servers. Gone are the days of having to use a separate process to perform the listen/accept loop. Instead, gen_nb_server uses features built into prim_inet to create a truly non-blocking server. This is the same mechanism networking-intense projects, like RabbitMQ, use to create their TCP servers.

In addition, every gen_nb_server is also a gen_server so you can gen_server:call/cast/info to your heart's content! What's not to like?

How to use gen_nb_server

  1. Drop the gen_nb_server behavior annotation at the top of your source file like so:
-behavior(gen_nb_server).
  1. Implement the required functions. These include the usual suspects from gen_server (see the gen_server manpage for details) and two new functions: sock_opts/0 and new_connection/4.

2a. sock_opts/0 is used by gen_nb_server to retrieve the set of socket options to use when creating the listen socket. These options will also be inherited by the client connection sockets. See the manpages for gen_tcp and inet for more information on socket options.

2b. new_connection/4 is called every time a new connection is accepted. It is called with the newly connected socket and the server's current state.

Here's a complete example which should give you an idea on how to use gen_nb_server:

-module(example).

-export([start_link/0,
         add_listener/3,
         remove_listener/3]).

-export([init/2, handle_call/3, handle_cast/2, handle_info/2]).
-export([terminate/2, sock_opts/0, new_connection/4]).

-behavior(gen_nb_server).

start_link() ->
    gen_nb_server:start_link(?MODULE, []).

add_listener(Pid, IpAddr, Port) ->
    gen_server:call(Pid, {add_listener, IpAddr, Port}).

remove_listener(Pid, IpAddr, Port) ->
    gen_server:call(Pid, {remove_listener, IpAddr, Port}).

init([], State) ->
    {ok, State}.

handle_call({add_listener, IpAddr, Port}, _From, State) ->
    case gen_nb_server:add_listen_socket({IpAddr, Port}, State) of
        {ok, State1} ->
            {reply, ok, State1};
        Error ->
            {reply, Error, State}
    end;
handle_call({remove_listener, IpAddr, Port}, _From, State) ->
    case gen_nb_server:remove_listen_socket({IpAddr, Port}, State) of
        {ok, State1} ->
            {reply, ok, State1};
        Error ->
            {reply, Error, State}
    end;
handle_call(_Msg, _From, State) ->
    {reply, ignored, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({tcp, Sock, Data}, State) ->
    Me = self(),
    P = spawn(fun() -> worker(Me, Sock, Data) end),
    gen_tcp:controlling_process(Sock, P),
    {noreply, State};

handle_info(_Msg, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

sock_opts() ->
    [binary, {active, once}, {packet, 0}].

new_connection(_IpAddr, _Port, Sock, State) ->
    Me = self(),
    P = spawn(fun() -> worker(Me, Sock) end),
    gen_tcp:controlling_process(Sock, P),
    {ok, State}.

worker(Owner, Sock) ->
    gen_tcp:send(Sock, "Hello\n"),
    inet:setopts(Sock, [{active, once}]),
    gen_tcp:controlling_process(Sock, Owner).

worker(Owner, Sock, Data) ->
    gen_tcp:send(Sock, Data),
    inet:setopts(Sock, [{active, once}]),
    gen_tcp:controlling_process(Sock, Owner).

Note: This code is also available in priv/example.

gen_nb_server's People

Contributors

bigkevmcd avatar

Stargazers

Michael Tang avatar Security Trails avatar  avatar AlexanderT avatar Cyuan avatar Dmitrii Podkorytov avatar Gavin M. Roy avatar Neurotec Tecnologia S.A.S avatar  avatar Damien Mathieu avatar dongyuwei avatar Dmitry Averbakh avatar Tung Dao avatar  avatar Angus H. avatar Ilya Sidorov avatar Srijan Choudhary avatar Bharat S avatar Josh Bush avatar Bryan Hunter avatar  avatar David.Gao avatar Dan Tovarnak avatar Richard.L avatar jstoker avatar Yao avatar  avatar Anatoly Chernov avatar  avatar Jade Allen avatar Boris Köster avatar Christian Dahlqvist avatar Dave Cottlehuber avatar Yueyoum avatar Ulf Ejlertsson avatar  avatar  avatar Alex Kaplan avatar  avatar  avatar  avatar Jakub Zawierucha avatar  avatar Asim Ihsan avatar Michael Uvarov avatar Wondong Shin avatar Roman avatar Christoph Grabo avatar  avatar Knut Nesheim avatar Simon Liu avatar Raman Hafiyatulin avatar Toru Tomita avatar Anton Lavrik avatar Igor Khomyakov avatar Vassil "Vasco" Kolarov avatar Mrinal Wadhwa avatar Niclas Axelsson avatar Holger Winkelmann avatar Örjan Angré (Lundberg) avatar  avatar Mateusz Pawlik avatar  avatar Paolo Freuli avatar Aleksandar Radulovic avatar Juan Jose Comellas avatar Özgür Orhan avatar Thijs Terlouw avatar Benjamin Halsted avatar SP avatar  avatar Chris Duesing avatar Hans Roman avatar Manuel Durán Aguete avatar Anthony Molinaro avatar Philip Luppens avatar Zvi avatar Bruce Durling avatar Chris Bernard avatar Mihai Balea avatar Fabian Linzberger avatar Richard Metzler avatar Amit Upadhyay avatar Russell Brown avatar Varnit avatar Christopher Zorn avatar Nicolas R Dufour avatar Hynek Vychodil avatar Joe Williams avatar  avatar Lucian Parvu avatar Davide avatar Gus Becciu avatar Martin Pompéry avatar Marc Worrell avatar Tim Wee avatar Eric Cestari avatar Tim Watson avatar Mikl Kurkov avatar Steve Davis avatar

Watchers

edwardt avatar andywu avatar Wondong Shin avatar  avatar robbinfan avatar Richard.L avatar  avatar

gen_nb_server's Issues

Race condition in example?

I think that there might be a (low probability) race condition in your example in the interaction between handle_info/3 and the worker/3 function. If the commands execute in the order indicated by the numbers below, the wrong process owns the socket after sending the response data.

handle_info({tcp, Sock, Data}, State) ->

  1. Me = self(),
  2. P = spawn(fun() -> worker(Me, Sock, Data) end),
  3. gen_tcp:controlling_process(Sock, P),
  4. {noreply, State};

worker(Owner, Sock, Data) ->
3 . gen_tcp:send(Sock, Data),
4 . inet:setopts(Sock, [{active, once}]),
5. gen_tcp:controlling_process(Sock, Owner).

I simulated the issue by inserting a timer:sleep(1000) between instructions 2 and 6.

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.