C - Hello World 2001

(Linux GCC; BASH; IPC - TCP/IP Socket)


 

  • Environment:

This following are assumed throughout this guide:

  • Red Hat Linux 7.0
  • GNU Make
  • GNU C Compiler
  • GNU Bourne-Again Shell 2.04

GCC may depend on the following packages:

  • make
  • glibc-devel <-(Kernel-Headers)
  • cpp
  • binutils
  • gcc
Download the Demo sample, extract it and test it.

 

  • Introduction:

The purpose of this sample is to demonstrate how a server process connects and communicates with client processes through a TCP/IP socket.

----------------                  ------------------                     ------------------
|              |  start           |                |    fork             |                |
| Shell Script |==================| Server Process |====================>| Server Process |
|              |  stop     |      |                |                     |                |
----------------           |      ------------------                     ------------------
                           |        /\                                    /\
                           |        || IPC (TCP/IP socket communication)  ||
                           |        \/                                    \/
                           |      ------------------                     ------------------
                           |      |                |                     |                |
                           |=====>| Client Process |                     | Client Process |
                                  |                |                     |                |
                                  ------------------                     ------------------

 

  • Create "./server.h":

#define SERVER_IP_ADDRESS  2130706433
                           // 127.0.0.1 to bits upto 255 each field.

#define SERVER_TCP_PORT       12345
#define SERVER_STRING         "Hello World"
#define SERVER_BUFFER_SIZE    256
#define SERVER_SLEEP_INTERVAL 1

void serve_connection
(
  int socket_value
);

 

  • Create "./server.c":

#include <stdlib.h>     // EXIT_SUCCESS
#include <netinet/in.h> // sockaddr_in, htons, htonl
                        // AF_INET, PF_INET, SOCK_STREAM

#include <sys/types.h>  // pid_t
#include <sys/socket.h> // sockaddr

#include "server.h"     // SERVER_IP_ADDRESS

int main
(
)
{
  struct sockaddr_in server_address;
  int                server_socket;
  struct sockaddr_in client_address;
  int                client_address_size;
  int                current_socket;
  pid_t              process_id;

/*
// Initialise server address structure.
*/
  bzero((char *) &(server_address), sizeof(server_address));
  server_address.sin_family      = AF_INET;
  server_address.sin_port        = htons(SERVER_TCP_PORT);
  server_address.sin_addr.s_addr = htonl(SERVER_IP_ADDRESS);

/*
// Create server socket.
*/
  if ((server_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
  {
    perror("socket");
    exit(EXIT_FAILURE);
  }

/*
// Bind server address with server socket together.
*/
  if (bind
      (
        server_socket,
        (struct sockaddr *) (&(server_address)),
        sizeof(server_address)
      ) < 0)
  {
    perror("bind");
    exit(EXIT_FAILURE);
  }

/*
// Listen for socket connection
*/
  listen(server_socket, 5);

/*
// Loop to serve requests from clients
*/
  for (;;)
  {
/*
// Waits to accept a client request for socket connection.
*/
    client_address_size = sizeof(client_address);
    current_socket =
      accept
      (
        server_socket,
        (struct sockaddr *) &(client_address),
        &(client_address_size)
      );

    if (current_socket < 0)
    {
      perror("accept");
      exit(EXIT_FAILURE);
    }

/*
// Fork process into 2 processes.
// - Parent server process will continue to
//   listen for other client connection requests.
// - Child server process will serve the currently connected client.
*/
    if ((process_id = fork()) < 0)
    {
      perror("fork");
      exit(EXIT_FAILURE);
    }

/*
// Parent server process:
// - Close current connection.
// - Continue listening for more client connections
*/
    if (process_id > 0)
    {
      close(current_socket);
      continue;
    }

/*
// Child server process:
// - Close server socket.
// - Serve current connection.
*/
    close(server_socket);
    serve_connection(current_socket);
  }

  return EXIT_SUCCESS;
}

void serve_connection
(
  int socket_value
)
{
  for (;;)
  {
    write
    (
      socket_value,
      SERVER_STRING,
      (int) strlen(SERVER_STRING)
    );
  }

  return;
}

 

  • Create "./client.c":

#include <stdlib.h>     // EXIT_SUCCESS
#include <stdio.h>      // stdout
#include <netinet/in.h> // sockaddr_in, htons, htonl
                        // AF_INET, PF_INET, SOCK_STREAM

#include "server.h" // SERVER_IP_ADDRESS

int main
(
)
{
  struct sockaddr_in server_address;
  int                server_socket;
  char               buffer[SERVER_BUFFER_SIZE];

/*
// Initialise server address structure.
*/
  bzero((char *) &(server_address), sizeof(server_address));
  server_address.sin_family      = AF_INET;
  server_address.sin_port        = htons(SERVER_TCP_PORT);
  server_address.sin_addr.s_addr = htonl(SERVER_IP_ADDRESS);

/*
// Create socket.
*/
  server_socket = socket(PF_INET, SOCK_STREAM, 0);

/*
// Connect to server socket.
*/
  if (connect
      (
        server_socket,
        (struct sockaddr *) &(server_address),
        sizeof(server_address)
      ) < 0)
  {
    perror("connect");
    exit(EXIT_FAILURE);
  }

  for (;;)
  {
    read(server_socket, buffer, SERVER_BUFFER_SIZE);
    fprintf(stdout, "%s", buffer);
  }

  return EXIT_SUCCESS;
}

 

  • Create "./Makefile":

#
# Usage: /usr/bin/make build
#

CC = /usr/bin/gcc

#
# Server
#
server: server.o
        $(CC) server.o -o server

#
# CLIENT
#
client: client.o
        $(CC) client.o -o client

clean:
        /bin/rm -rf *.o server client

build:
        /usr/bin/make clean
        /usr/bin/make client
        /usr/bin/make server

 

  • Create "./library.sh":

processfind()
{
  if [ ${#} -ne 1 ]
    then
      return 0;
  fi

  /bin/ps -auwx | /usr/bin/awk '{print $11}' | /bin/grep -q ${1};
  GREPFLAG=${?};

  return ${GREPFLAG};
}

 

  • Create "./system.sh":

#
# Usage: ./system.sh start
#        ./system.sh stop
#

#
# Check action by the number of arguments
#
if [ ${#} -ne 1 ]
  then
    /bin/echo "Usage:";
    /bin/echo "${0} start";
    /bin/echo "${0} stop";

    exit ${?};
fi

#
# Get action from the first argument
#
ACTION=${1};

#
# Check for function library
#
if [ ! -f ./library.sh ]
  then
    /bin/echo "${0} cannot find a required file.";

    exit ${?};
fi

#
# Calls the library of functions
#
source ./library.sh

#
# Check if processes already exist
#
RUNNING=0;
if processfind "./server" 
  then
    RUNNING=1;
fi;
if processfind "./client"
  then
    RUNNING=1;
fi;

#
# Follow the action
#
case "${ACTION}" in
#
# START
#
  start)
#
# If it's already running, no need to start.
#
    if [ ${RUNNING} -ne 0 ]
      then
        /bin/echo "The system has already been started."

      exit 1;
    fi;
#
# Start system processes
#
    ./server &
    ./client &

    exit ${?};
    ;;
#
# STOP
#
  stop)
#
# If it's not running, no need to stop.
#
    if [ ${RUNNING} -eq 0 ]
      then
        /bin/echo "The system is not running."

      exit 1;
    fi;
#
# Stop system processes
#
    /usr/bin/killall server;
    /usr/bin/killall client;

    exit ${?};
    ;;
#
# OTHERS
#
  *)
    /bin/echo "Usage: ${0} {start|stop}"

    exit ${?};
esac;

 

  • Test Demo:

# (Ctrl-Alt F1) to switch to text virtual console number 1.
/bin/chmod 700 ./system.sh
./system.sh start

# (Ctrl-Alt F2) to switch to another text virtual console number 2.
./client &

# (Ctrl-Alt F3) to switch to another text virtual console number 3.
/bin/ps -ef      # There should be a few client and server processes running.
/bin/netstat -an # There should be a few TCP/IP socket active.
./system.sh stop

# (Ctrl-Alt F1) to switch back to the original text virtual console number 1.


 

Primac Systems Limited

Copyright 2001

 

1