Archive

Posts Tagged ‘sockets’

Serving multiple clients on the same port in Erlang

December 15, 2009 Leave a comment

In the previous post, I wrote down a really basic interface for the gen_server I’m going to use in my card game: there were a couple of basic handle_calls used to manage users’ turns etc etc.

Obviously, if we want to handle connecting users, we must set up a socket on a specific port and listen to what is coming in there.

A really easy way to do this is shown in this post. Let’s start by writing in our gen_server (in the internal functions space) the following function:

conn_manager(Port) ->
    {ok, Listen} = gen_tcp:listen(Port, [binary,
                                                    {keepalive, true},
                                                    {reuseaddr, true},
                                                    {active, once}]),
    spawn(fun() -> parallel_connection(Listen) end).

conn_manager(Port) is a really simple fuction called in the init function; it does the following: create a tcp socket on the Port passed as argument to the function, this socket will have some features, as you can see we specified that the traffic going trough it is of type binary, that we want the server to send keepalive packets to mantain the connection up and running, that the address may be reused and most importan that this socket will be active once.

What does the active once mean? Well, the socket could have been always active, and this means that we would have been listening to the incoming data without any restrictions…well, sometimes this is a good thing, but think about Denial of Service attacks..we definitely may prefer to listen to the incoming traffic only when neededt…

After the creation of the socket we spawn a new process and pass to it the reference to the socket.

Let’s move on:

parallel_connection(Listen) ->
    {ok, Socket} = gen_tcp:accept(Listen),
    case is_in_blacklist(Socket) of
	false ->
	    spawn(fun() -> parallel_connection(Listen) end),
	    conn_loop(Socket);
	true ->
	    gen_tcp:close(Socket)
    end.

parallel_connection(Listen) is the function spawned as a new process by the previous function. In the very beginning it accepts any incoming connection and checks whether it comes from a blacklisted IP (note that is_in_blacklist is function that I did by myself so it is not a erlang standard function, if the clients in in the blacklist the connection is closed, otherwise we do two things: first we spawn a new parallel_connection function so that new clients may be able to connect to the server, and the we call the function conn_loop(Socket) so that we can manage the traffic coming from the already connected user.

conn_loop(Socket) ->
    receive
	{tcp, Socket, Bin} ->
	    inet:setopts(Socket, [{active, once}]),
	    case Bin of
                 ....case matching with incoming binaries (e.g. join match)
	    end,
	    conn_loop(Socket);
	{tcp_closed, Socket} ->
	    closed
    end.

As you can see conn_loop(Socket) is a very basic function…it is a simple receive function that in case of incoming traffic {tcp, Socket, Bin} triggers the socket to receive once more and than with a case construct manages the incoming binaries.

This post was written in a hurry, so if you have questions or comments or anything just comment it or mail me so that I can correct it!

Advertisements
Categories: English, Erlang Tags: , ,

A card game in erlang…continue!

December 9, 2009 2 comments

Here I am with a new post!

A couple of days ago, I had some time to fix my erlang environment in my new Ubuntu Karmic Koala, so I decided to continue with the development of my cards game in erlang.

If you remember from my previous posts, I did a process for handling the table, it was a gen_server providing a basic interface for a set of actions users may want to perform, as “play a card on the table”, “take a single card/a set of cards from table”, “give me 10 cards” and so on…

This process was also responsible for checking whether the action user wants to do is allowed (e.g. is the card user wants to take in the table), so I thought that next step would have been the creation of another gen_server.

In my mind this server should have listened to a specific port waiting for users; since “scopa” is a game played by 4 users, the server should have waited until 4 users joined the game, than it would have started a new table process for these users.

Now I’m going to write some code, and discuss it with you.

Let’s start with some handle_calls for our gen_server:

handle_call({add, Socket}, _From, State) ->
    NewSockets = [Socket | State#state.sockets],
    Reply = ok,
    NewState = #state{sockets=NewSockets},
    {reply, Reply, NewState};

The previous handle_call adds a new socket to the list of sockets stored in the state of the gen_server; I use the sockets to distinguish each player.

handle_call(get_players_number, _From, State) ->
    PlayersNum = length(State#state.sockets),
    Reply = PlayersNum,
    {reply, Reply, State};

The previous handle_call is used to retrieve the number of users who joined already the game.

handle_call(get_players_sockets, _From, State) ->
    PlayersSockets = State#state.sockets,
    Reply = PlayersSockets,
    {reply, Reply, State};

The previous handle_call is used to retrieve the list of sockets in the game (we can use this to understand whether the socket sending commands is a valid user)

handle_call(get_player_turn, _From, State) ->
    [PlayerSocket | _Others] = State#state.sockets,
    Reply = PlayerSocket,
    {reply, Reply, State};

The previous handle_call is used to retrieve wich user/socket is the next to perform an action (so we can manage turns)

handle_call(update_turn, _From, State) ->
    [PlayerSocket | Others] = State#state.sockets,
    NewSockets = Others ++ [PlayerSocket],
    NewState = #state{sockets=NewSockets},
    Reply = ok,
    {reply, Reply, NewState}

The last handle_call is used to update the turn list after a player submit an action.

Let’s say that a player sends a command to our server…before forwarding this command to the table we must check whether the user who sent the command is a player of the match and whether he is the player we expect to play (turn); we may want to use two functions like:

%%--------------------------------------------------------------------
%% Func: is_player(Socket) -> true | false
%% Description: Given a socket checks whether it represents a user in
%%              current match
%%--------------------------------------------------------------------
is_player(Socket) ->
    lists:member(Socket, gen_server:call(?MODULE, get_players_sockets)).

and

%%--------------------------------------------------------------------
%% Func: is_player_turn(Socket) -> true | false
%% Description: Given a socket checks whether the user it represents
%%              is the one expected to play
%%--------------------------------------------------------------------
is_player_turn(Socket) ->
    Socket == gen_server:call(?MODULE, get_player_turn).

Next time new stuff! Stay tuned!

Categories: English, Erlang Tags: , , , ,