Archive

Posts Tagged ‘socket’

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!

Categories: English, Erlang Tags: , ,

A client for our echo server in C

December 1, 2009 Leave a comment

Last time I posted something about sockets, ending up with a small server that accepted one client and greeted it.

Anyhow I left two things to do: fist the client was simulated by a telnet connection, and second the server wasn’t devised for a multiclient approach.

For the client side, I can briefly say that all the concepts I have introduced in last post are valid: to create a client we create a socket descriptor, connect to it and then send and receive data over it (we don’t use listen and accept here), then we close the socket.

Thus a valid example is:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>

#define BUF_SIZE 	(1000)

int main(int narg, char *varg[])
{
    int socketfd;
    struct sockaddr_in server_addr;
    int port = atoi(varg[2]); //convert a string to an integer
    int recv_len, sent_len;
    char buf[BUF_SIZE];

    printf("Connecting to %s:%d\n", varg[1], port);

    // create the socket if possible
    if ((socketfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        perror("unable to create socket");
        return -1;
    }

    // configure the server address for the socket
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(varg[1]);

    // connect to the server via the socket created above
    if (connect(socketfd,
                   (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
        perror("unable to connect");
        return -1;
    }

    // receive data from server
    recv_len = recv(socketfd, buf, BUF_SIZE, 0);
    if (recv_len == 0) {
        printf("server disconnected!\n");
        close(socketfd);
        return 0;
    }
    if (recv_len < 0) {
        perror("error receiving data from server");
        return -1;
    }

    printf("received from server %d bytes:%s", recv_len, buf);

    close(socketfd);
    return 0;
}

To execute the following code try to run the run in one shell the code for the server I have posted last time and in another the following:

bellorofonte@pegaso:~$ ./client 127.0.0.1 4000
Connecting to 127.0.0.1:4000
received from server 14 bytes:Hello World!

You may have noticed that in the above case we specified the IP address of the server in the notation xxx.xxx.xxx.xxx, you may want to use the function struct hostent *gethostbyname(const char *name) by including the library <netdb.h>
You can type something like:

struct hostent *host = gethostbyname("localhost");

where hostent is the following structure:

struct hostent {
    char    *h_name;        // official name of host
    char    **h_aliases;    // list of aliases
    int     h_addrtype;     // host address type
    int     h_length;       // lenght of address
    char    **h_addr_list;  // list of addresses
}

For the multiclient server stay tuned!

Categories: C, English Tags: , , ,
Follow

Get every new post delivered to your Inbox.

Join 26 other followers