I explained some TCP/IP theory and at what layer sockets fit in it in a previous tutorial. I intend to write a series of these starting from basic socket client / server to TCP/UDP based concurrent / iterative servers. Depending upon audience interest, I would hope to continue that with things like simply http web server, proxy or tunnel application or even some encryption.
Time to write a basic working listening server using Sockets.
A socket is a communication end point which basically means it is a combination of Port number and IP address since these two things are used to identify who we are talking to.
IP address helps to reach the destination machine where as port number identifies which application we are actually communicating with.
I have kept it limited to reading a string from client and terminate. The intension is to make it well understood by people. Latter we can extend it to iterative or concurrent servers which can service clients for as long as they like and even doing that with a choice of whether to create new threads for each client or new processes.
With almost every important instruction I have added comments explaining what it is doing and why.
To execute this server, you need gcc/++ linux environment or any POSIX compliant system.
Type $ gcc server.c -o server -> enter
$ ./server
To test it with a client one of the options available on Linux is command line nc utility
Type $ nc 127.0.0.1 1234
Now enter any text. It will be printed on the server. The ip address 127.0.0.1 is loop back which means the server is running on the same machine with port number 1234. In practice these should be configurable. But I wanted to keep the program simple for now.
// File name – server.c
// Written and tested on Linux Fedora Core 12 VM
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 50
int main()
{
// Two socket descriptors which are just integer numbers used to access a socket
int sock_descriptor, conn_desc;
// Two socket address structures - One for the server itself and the other for client
struct sockaddr_in serv_addr, client_addr;
// Buffer to store data read from client
char buff[MAX_SIZE];
// Create socket of domain - Internet (IP) address, type - Stream based (TCP) and protocol unspecified
// since it is only useful when underlying stack allows more than one protocol and we are choosing one.
// 0 means choose the default protocol.
sock_descriptor = socket(AF_INET, SOCK_STREAM, 0);
// A valid descriptor is always a positive value
if(sock_descriptor < 0)
printf("Failed creating socket\n");
// Initialize the server address struct to zero
bzero((char *)&serv_addr, sizeof(serv_addr));
// Fill server's address family
serv_addr.sin_family = AF_INET;
// Server should allow connections from any ip address
serv_addr.sin_addr.s_addr = INADDR_ANY;
// 16 bit port number on which server listens
// The function htons (host to network short) ensures that an integer is interpretted
// correctly (whether little endian or big endian) even if client and server have different architectures
serv_addr.sin_port = htons(1234);
// Attach the server socket to a port. This is required only for server since we enforce
// that it does not select a port randomly on it's own, rather it uses the port specified
// in serv_addr struct.
if (bind(sock_descriptor, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
printf("Failed to bind\n");
// Server should start listening - This enables the program to halt on accept call (coming next)
// and wait until a client connects. Also it specifies the size of pending connection requests queue
// i.e. in this case it is 5 which means 5 clients connection requests will be held pending while
// the server is already processing another connection request.
listen(sock_descriptor, 5);
printf("Waiting for connection...\n");
int size = sizeof(client_addr);
// Server blocks on this call until a client tries to establish connection.
// When a connection is established, it returns a 'connected socket descriptor' different
// from the one created earlier.
conn_desc = accept(sock_descriptor, (struct sockaddr *)&client_addr, &size);
if (conn_desc == -1)
printf("Failed accepting connection\n");
else
printf("Connected\n");
// The new descriptor can be simply read from / written up just like a normal file descriptor
if ( read(conn_desc, buff, sizeof(buff)-1) > 0)
printf("Received %s", buff);
else
printf("Failed receiving\n");
// Program should always close all sockets (the connected one as well as the listening one)
// as soon as it is done processing with it
close(conn_desc);
close(sock_descriptor);
return 0;
}
















