Archive

Posts Tagged ‘client’

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: , , ,

Echo server in C

November 30, 2009 Leave a comment

In order to create an echo server we must introduce the concept of socket in C.

Sockets can be seen as an endpoint for cummunication, and in C are created by including in our code the following libraries:

<sys/types.h>
<sys/socket.h>

and by using the function int socket(int domain, int type, int protocol)

Let’s explain briefly the three variables used in the previous function:

int domain represents a communication domain, the protocol family to be used for communication (common values are AF_UNIX for local communication, AF_INET for IPv4 Internet protocols and AF_INET6 for IPv6).

int type specifies the communication semantics (commonly we use SOCK_STREAM that provides sequenced, reliable, two-way, connection-based byte streams but can be used other types as SOCK_RAW which provides raw network protocol access).

int protocol indicates a particular protocol for the socket, normally only a single protocol exists for supporting a particular socket type in a family but in some cases there can be more than one and this variable specifies which one should be used.

Function int socket(…) returns a socket descriptor if valid, otherwise it returns -1 and a errno to handle the error. To send or receive data (e.g to/from another process or machine) the socket must be connected to another socket.

Since we want to create a server first, we must introduce a way to setup a port to listen to. We do this by using the function int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen) (the same libraries as before must have included). This function assigns the address specified by variable addr to the socket assigned to socket descriptor sockfd. addrlen specifies the size in bytes of the address structure pointed to by addr.

The struct sockaddr is structure representing an IP socket address, thus a pain (IP, PORT). This structure may change based on protocol you use, but is something like:

struct sockaddr {
	short sin_family;         // e.g. AF_INET for IPv4
	unsigned short sin_port;  // e.g. htons(4320) see htons()
	struct in_addr sin_addr;  // structure containing the IP address
	char sin_zero[8];
};

where:

in_addr {
	unsigned long s_addr;
};

If successful, bind() returns 0; otherwise it returns -1 and sets errno to indicate the error.
After binding, listening operation can be started as: listen(int sockefd, in queue) where socketfd is our socket descriptor and queue is the maximum number of connections pending. If successful, listen() returns 0; otherwise it returns -1 and sets errno to indicate the error.

To accept a connection we use the function int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) which is used with connection based sockets such as streams. Argument sockfd is a socket that has been created with socket(), bound to a local address with bind(), and is listening for connections after a listen(). The argument addr is a pointer to a sockaddr structure filled in with the address of the peer socket, as known to the communications layer. The addrlen argument is a value-result argument: the caller must ini tialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address. If an error occurs, -1 is returned and errno is set to indicate the cause.

For exchanging data we use two functions: ssize_t send(int sockfd, const void *buf, size_t len, int flags) where buf is the message you want to send, flags is usually set to 0 and the function ssize_t recv(int sockfd, void *buf, size_t len, int flags). These functions return -1 in case of an error.

Finally to close a connection we include library and call int close(int sockfd)

Here an example of a server that waits for a connection and greets the user connected. (note that I did not check the return value of the functions):

#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>

#define MAX_CONNECTIONS    (1000)
#define MAX_LINE           (1000)

int main()
{
  int sockfd;
  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  struct sockaddr_in my_addr;
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  my_addr.sin_port = htons(4000);

  bind(sockfd, (struct sockaddr *) &my_addr, sizeof(my_addr));
  listen(sockfd, MAX_CONNECTIONS);

  while(1)
    {
      struct sockaddr_in client_addr;
      int client_len = sizeof(client_addr);

      int client_fd = accept(sockfd, (struct sockaddr *) &client_addr,  &client_len);

      char buf[MAX_LINE];
      strcpy(buf, "Hello user!\n");
      send(client_fd, buf, strlen(buf) + 1, 0);
      close(client_fd);
    }
}

To run the server type in one shell the following:

bellerofonte@pegaso:~$ ./echoserv

and in another:

bellerofonte@pegaso:~$ telnet localhost 4000
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello user!
Connection closed by foreign host.

Note that this server accepts only one connection (I will post new code for a multi-client server soon)!

Categories: C Tags: , ,