Book Image

WebRTC Cookbook

By : Andrii Sergiienko
Book Image

WebRTC Cookbook

By: Andrii Sergiienko

Overview of this book

Table of Contents (15 chapters)
WebRTC Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Building a signaling server in Erlang


The following recipe shows how to build signaling server using Erlang programming language and WebSockets for transport protocol. We will not introduce Erlang programming in this recipe, so you should have at least basic knowledge of this programming language and its relevant technologies.

Getting ready

To use this solution, you should have Erlang installed on your system to start with. You can download the Erlang distribution relevant to your system from its home page http://www.erlang.org/download.html. The installation process might need specific actions relevant to specific platforms/OSes, so follow the official installation instructions at http://www.erlang.org/doc/installation_guide/INSTALL.html.

Tip

For this example, I've used Erlang 17. You might need to add some minor changes to the code to make it work under future versions of Erlang.

We will also use the Git versioning system to download additional packets and components necessary for our solution, so you should download and install Git distribution relevant to your system. You can download this from http://www.git-scm.com. As a build tool for the project, we will use Rebar; you should also download and install it from https://github.com/basho/rebar.

How to do it…

The following steps will lead you through the process of building a signaling server using Erlang:

  1. Create a new folder for the signaling server application and navigate to it.

  2. Using the Rebar tool, create a basic Erlang application:

    rebar create-app appid=sigserver
    

    This command will create an src folder and relevant application files in it.

  3. Create the rebar.config file, and put the following Rebar configuration in it:

    {erl_opts, [warnings_as_errors]}.
    {deps,
    [
    {'gproc', ".*", {
    git, "git://github.com/esl/gproc.git", {tag, "0.2.16"}
    }},
    {'jsonerl', ".*", {
    git, "git://github.com/fycth/jsonerl.git", "master"
    }},
    {'cowboy', ".*", {
    git,"https://github.com/extend/cowboy.git","0.9.0"
    }}
    ]}.
  4. Open the src/sigserver.app.src file and add the following components to the application's section list: cowlib, cowboy, compiler, and gproc.

  5. Open the src/sigserver_app.erl file and add the following code:

    -module(sigserver_app).
    -behaviour(application).
    -export([start/2, stop/1, start/0]).
    start() ->
        ok = application:start(ranch),
        ok = application:start(crypto),
        ok = application:start(cowlib),
        ok = application:start(cowboy),
        ok = application:start(gproc),
        ok = application:start(compiler),
        ok = application:start(sigserver).
    
    start(_StartType, _StartArgs) ->
        Dispatch = cowboy_router:compile([
                  {'_',[
                     {"/ ", handler_websocket,[]}
                   ]}
        ]),
        {ok, _} = cowboy:start_http(websocket, 100, [{ip, {127,0,0,1}},{port, 30001}], [
                {env, [{dispatch, Dispatch}]},
                {max_keepalive, 50},
                {timeout, 500}]),
        sigserver_sup:start_link().
    
    stop(_State) -> ok.
  6. Create the src/handler_websocket.erl file and put the following code in it:

    -module(handler_websocket).
    -behaviour(cowboy_websocket_handler).
    -export([init/3]).
    -export([websocket_init/3, websocket_handle/3,
             websocket_info/3, websocket_terminate/3]).
    
    -record(state, {
             client = undefined :: undefined | binary(),
             state = undefined :: undefined | connected | running,
             room = undefined :: undefined | integer()
    }).
    
    init(_Any, _Req, _Opt) ->
        {upgrade, protocol, cowboy_websocket}.
    
    websocket_init(_TransportName, Req, _Opt) ->
        {Client, Req1} = cowboy_req:header(<<"x-forwarded-for">>, Req),
        State = #state{client = Client, state = connected},
        {ok, Req1, State, hibernate}.
    
    websocket_handle({text,Data}, Req, State) ->
        StateNew = case (State#state.state) of
                       started ->
                           State#state{state = running};
                       _ ->
                           State
                   end,
        JSON = jsonerl:decode(Data),
        {M,Type} = element(1,JSON),
        case M of
            <<"type">> ->
                case Type of
                    <<"GETROOM">> ->
                        Room = generate_room(),
                        R = iolist_to_binary(jsonerl:encode({{type, <<"GETROOM">>}, {value, Room}})),
                        gproc:reg({p,l, Room}),
                        S = (StateNew#state{room = Room}),
                        {reply, {text, <<R/binary>>}, Req, S, hibernate};
                    <<"ENTERROOM">> ->
                        {<<"value">>,Room} = element(2,JSON),
                        Participants = gproc:lookup_pids({p,l,Room}),
                        case length(Participants) of
                            1 ->
                                gproc:reg({p,l, Room}),
                                S = (StateNew#state{room = Room}),
                                {ok, Req, S, hibernate};
                            _ ->
                                R = iolist_to_binary(jsonerl:encode({{type, <<"WRONGROOM">>}})),
                                {reply, {text, <<R/binary>>}, Req, StateNew, hibernate}
                        end;
                    _ ->
                        reply2peer(Data, StateNew#state.room),
                        {ok, Req, StateNew, hibernate}
                end;
            _ ->
                reply2peer(Data, State#state.room),
                {ok, Req, StateNew, hibernate}
        end;
    
    websocket_handle(_Any, Req, State) -> {ok, Req, State, hibernate}.
    
    websocket_info(_Info, Req, State) -> {reply, {text,_Info}, Req, State, hibernate}.
    
    websocket_terminate(_Reason, _Req, _State) -> ok.
    
    reply2peer(R, Room) ->
        [P ! <<R/binary>> || P <- gproc:lookup_pids({p,l,Room}) -- [self()]].
    
    generate_room() ->
        random:seed(now()),
        random:uniform(999999).
  7. Now we can compile the solution using the Rebar tool:

    rebar get-deps
    rebar compile
    

    If everything was successful, you should not see any errors (warnings are not critical).

  8. After we build our signaling server, we can start it using the following command:

    erl -pa deps/*/ebin ebin -sasl errlog_type error -s sigserver_app
    

    Tip

    Windows-based systems can't use a star symbol in such constructions, so if you're working under Windows, you should use the full path name as shown in the following command:

    erl -pa deps/cowboy/ebin deps/cowlib/ebin deps/gproc/ebin deps/jsonerl/ebin deps/ranch/ebin ebin -sasl errlog_type error -s sigserver_app
    

Now your signaling server should be running, and you need to listen for incoming WebSocket connections on port 30001.

Note that full source codes are supplied with this book.

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

How it works…

In this recipe, we implemented the WebRTC signaling server in Erlang. The application listens on port 30001 for incoming WebSocket connections from the browser clients.

The first peer will be registered by the server in a virtual room and will get the room number. The second peer after that can use the room number in order to connect to the first peer. The signaling server will check whether the virtual room exists and if so, it will route call/answer requests and answers between the peers in order to make them establish a direct peer-to-peer WebRTC connection.

There's more…

Basically, this is a very simple signaling server. It doesn't have any advanced features, and the main goal of it is to help peers establish direct connection between each other. Nevertheless, a signaling server can serve additional tasks. For example, it can serve for web chats, file transfers, service data exchanges, and other features specific for certain situations. There are no certain requirements for a signaling server; you can implement it using your favorite programming language and technology.

See also

  • For tips on implementing a signaling server in Java, refer to the Building a signaling server in Java recipe

  • You can also refer to the Making and answering calls recipe on how to use a signaling server from a browser application using JavaScript