Jump to content




Recent Status Updates

  • Photo
      30 Sep
    rhossis

    laptop hard disk seated beneath motherboard but with no access panel. 7 hours to replace :(

    Show comments (3)
  • Photo
      19 Sep
    Chall

    I love it when you go to write a help thread, then while writing, you reach an enlightenment, and figure it out yourself.

    Show comments (3)
View All Updates

Developed by TechBiz Xccelerator
Photo
- - - - -

A simple TCP server using Linux C API

encryption

  • Please log in to reply
4 replies to this topic

#1 fkl

fkl

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 415 posts

Posted 23 May 2011 - 10:06 AM

Writing a TCP based server using POSIX Linux C API

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;
}

  • 0

#2 Flying Dutchman

Flying Dutchman

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,079 posts
  • Location:::1
  • Programming Language:C++, Python

Posted 23 May 2011 - 02:18 PM

I'd add that AF_INET means using IPv4 and AF_INET6 is IPv6 and SOCK_STREAM means TCP protocol whereas SOCK_DGRAM means UDP.

I have a question though, which probably falls under next tutorial; if one wants to create a server for more than one client and wants to keep it "active", meaning it can process both user requests parallel, should one create a new thread for each client connected or there a better way around this?
  • 0

The roots of education are bitter, but the fruit is sweet.


#3 fkl

fkl

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 415 posts

Posted 23 May 2011 - 02:39 PM

Thanks Flying Dutchman. I missed that info.

About your question, we will see the implementations when we get there. But i can provide some theory right away.

When we say 'process requests in parallel', this might mean multiple things:

1. You might have 2 to as many clients which are in connected state but mostly any one of them is transferring data at a time.
Select or Poll API is better to handle such a situation. They simply listen on a group of descriptors and return the one on which data comes. The server processes which ever descriptor was received with data first and start listening again. If more than one descriptors have data, they are queued and processed by server.

2. There are 2 to as many clients all of which are transferring a lot of data at the same time where processing of every client requires some shared resources.
Use threads: Since you need access to shared resources creating separate processes wouldn't be a good idea.

3. Similar to 2 but isolated tasks and each one requires it's own resources. E.g. each client needs to run a new application.
Use separate processes: You wouldn't have any IPC over head (Inter process communication) plus each process competes for a separate time quantum for scheduling with OS. For threads, they all share the time quantum of the parent process unless it is kernel level thread.

In case of large processing servers which receive hundreds of thousands of connection requests (may be Google), my guess is that they would use a mix of both i.e. say they have 100k clients. They might make 5k (threads OR processes) with using select with 20 clients per thread. 20 might be a number small enough that even if all 20 receive data simultaneously, it completes without visible delay and gives the illusion of parallel processing.
  • 0

#4 fkl

fkl

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 415 posts

Posted 23 May 2011 - 02:46 PM

As a general principle, TCP based servers are written concurrently using threads / processes etc. Whereas UDP servers are iterative since there is no connection so you receive and process data as it comes compared to TCP in which you have to formally terminate connection once you are through and need to establish it again before sending data.
  • 0

#5 fkl

fkl

    CC Devotee

  • Senior Member
  • PipPipPipPipPipPip
  • 415 posts

Posted 14 July 2011 - 10:37 AM

Here is one follow up tutorial

http://forum.codecal...pi-linux-c.html
  • 0
Today is the first day of the rest of my life





Also tagged with one or more of these keywords: encryption

Powered by binpress