Click here to Skip to main content
15,881,898 members
Articles / Programming Languages / C++

Understanding IPC from Scratch (Part 1)

Rate me:
Please Sign up or sign in to vote.
4.79/5 (18 votes)
9 Jul 2013CPOL26 min read 39.3K   878   54   4
An article that discusses IPC in detail

Introduction

I was trying to learn about networking programming and while doing so, I went through various articles and other sources. The problem I faced was that either the articles were very specific or they were assuming that the user has some prior knowledge of different things. After struggling a bit, I decided to write this article. This article only assumes that you have prior knowledge of C++. I have written an earlier article on Threads which is used as reference in this article. This article will take you through the learning of IPC in an easy, comprehensive and practical way. This is the first article in the series of IPC. Others will follow soon.

Background

I have earlier written an article on Threads (Understanding Thread’s Synchronization Objects with a Real Life Example). Reading this one is recommended as we will be using Threads quite often.

Using the Code

In the package, we already have Visual Studio projects. You can open them by using vc9. The article will have the explanation of each project.

What this Article Explains

This article explains the IPC techniques. This is the first article in the series of IPC. This article primarily explains Sockets.

IPC (Inter Process Communication)

IPC is a set of programming interfaces that allow a programmer to coordinate activities among different program processes that can run concurrently in an operating system. This allows a program to handle many user requests at the same time. Since even a single user request may result in multiple processes running in the operating system on the user's behalf, the processes need to communicate with each other. The IPC interfaces make this possible. Each IPC method has its own advantages and limitations so it is not unusual for a single program to use all of the IPC methods. We will understand different APIs one by one.

Typically, a client server model is used for IPC (other ways area also there). Let’s try to understand the client server model.

Client Server Model

  • Standard model for developing network applications.
  • Notion of client and server:
    • A server is a process that is offering some services.
    • A client is the process which is requesting some services.
    • Server or client may be running on different machines.
    • Server waits for requests from the client(s).
  • Typical Scenario:
    • The server process starts on some computer system.
      • Initializes itself and then goes to sleep waiting for a client requests.
      • Sleep means it's waiting.
    • A client process starts, either on the same machine or on some other system.
      • Sends a request to the server.
    • When the server process has finished providing its service to the client, the server goes back to sleep, waiting for the next client request to arrive.
    • This process will be repeated until the server is shutdown.  

    • Roles of client and server processes are asymmetric.
    • Two types of servers (in terms of how they process the requests)
      • Iterative Servers
      • Concurrent Servers  

Iterative Servers

These are used when server process knows in advance how long it takes to handle each request and it handles each request itself.

  • Single copy of server runs all the time
  • A client may have to wait if server is busy
  • This means if there are 5 clients who send requests at the same time, the first one which server receives will be handled first and the others will wait. One request will be processed at a time.

Concurrent Servers

Used when amount of work requires handling the request is unknown; the server starts another process to handle each request.

  • A copy of server caters to a client’s request in a dedicated fashion.
  • As many copies of server as there are client requests.
  • All the requests can be processed simultaneously; however we can limit the number of clients connecting at a time by some configuration.

There can be different ways for doing IPC:

  • Pipes and Named Pipes
  • Message Queueing
  • Semaphores
  • Shared Memory
  • Sockets

I will try to explain the sockets in this article.

Sockets

Introduction to Sockets

Sockets are one of the most fundamental technologies of computer programming. Sockets allow applications to communicate using standard mechanisms built into network hardware and operating systems. The sockets were first developed by Berkley university and were called BSI (Berkley Socket Interface). This was written as a wrapper over TCP and IP.

When we use Sockets, these are the underlying technologies which are used at different layers for communication:

  1. At data link layer level, it uses Ethernet.
  2. At network layer level, it uses IP.
  3. At transport layer level, we use TCP/UDP.
  4. At application layer level, we use Sockets.

Communication

In a nutshell, a socket represents a single connection between exactly two pieces of software. More than two pieces of software can communicate in client/server or distributed systems (for example, many Web browsers can simultaneously communicate with a single Web server), but multiple sockets are required to do this. Socket-based software usually runs on two separate computers on the network, but sockets can also be used to communicate locally (interprocess) on a single computer.

Sockets are bidirectional, meaning that either side of the connection is capable of both sending and receiving data. Sometimes the one application that initiates communication is termed the client and the other application the server, but this terminology leads to confusion in non-client/server systems and should generally be avoided.

Interface Types

  1. Socket interfaces can be divided into three categories. Perhaps the most commonly-used type, the stream socket, implements "connection-oriented" semantics. Essentially, a "stream" requires that the two communicating parties first establish a socket connection, after which any data passed through that connection will be guaranteed to arrive in the same order in which it was sent.
  2. Datagram sockets offer "connection-less" semantics. With datagrams, connections are implicit rather than explicit as with streams. Either party simply sends datagrams as needed and waits for the other to respond; messages can be lost in transmission or received out of order, but it is the application's responsibility and not the socket's to deal with these problems. Implementing datagram sockets can give some applications a performance boost and additional flexibility compared to using stream sockets, justifying their use in some situations.
  3. The third type of socket -- the so-called raw socket -- bypasses the library's built-in support for standard protocols like TCP and UDP. Raw sockets are used for custom low-level protocol development.

Addresses and Ports

Today, sockets are typically used in conjunction with the Internet protocols -- Internet Protocol, Transmission Control Protocol, and User Datagram Protocol (UDP). Libraries implementing sockets for Internet Protocol use TCP for streams, UDP for datagrams, and IP itself for raw sockets.

  • TCP is a connection oriented, reliable full-duplex byte stream service. Its underlying IP layer is unreliable and provides connectionless delivery system. So TCP has to do error checking on its own to make the TCP reliable.
  • UDP is a connectionless and unreliable datagram service. UDP don’t do error checking of its own hence making the service unreliable. Its underlying layer is IP only.
  • For understanding TCP/IP, I recommend that you listen to these lectures - they are very nice and elaborate. These lectures are for beginners:

To communicate over the Internet, IP socket libraries use the IP address to identify specific computers. Many parts of the Internet work with naming services, so that the users and socket programmers can work with computers by name (e.g., "thiscomputer.compnetworking.about.com") instead of by address (e.g., 208.185.127.40). Stream and datagram sockets also use IP port numbers to distinguish multiple applications from each other. For example, Web browsers on the Internet know to use port 80 as the default for socket communications with Web servers.

Socket Programming

Traditionally, sockets have been of interest mainly to computer programmers. But as new networking applications emerge, end users are becoming increasingly network-savvy. Many Web surfers, for example, now know that some addresses in the browser look like http://206.35.113.28:8080/ where 8080 is the port number being used by that socket.

The socket APIs are relatively small and simple. Socket is very similar to a file in terms of its usage. Just like after opening a file, we get the file descriptor and by using that, we can read or write to the file. Similarly by using a socket, a socket descriptor is returned and it can be used to read or write to the socket. Many of the functions are similar to those used in file input/output routines such as read(), write(), and close(). The actual function calls to use depend on the programming language and socket library chosen.

Before using the TCP or UDP (in the sockets), the connection has to be established in case of TCP between two hosts. Let us first try to understand what the connection is?

There are 5 components which constitute the connection:

  1. Protocol used
  2. Source IP
  3. Source port number.
  4. Destination IP
  5. Destination port number

While developing the internet servers or client, we can use some standard and well accepted protocols:

  • At data link layer level, use Ethernet.
  • At the network layer level, use IP
  • At the transport layer level, use TCP.
  • At the application layer level, we can use sockets. There are different APIs for socket present. Like in windows, we can Winsock.

When 2 processes located on two different machines communicate, we define association and socket association is basically as 5 tuple. These 5 things are nothing but the components of connection.

Socket is also called half association. Here is the explanation for that:

  • Source side socket when created will know protocol, local IP and local port number.
  • Destination side socket when created will know protocol, remote IP and remote port number.
  • In a typical scenario, source opens a socket, destination opens a socket and by some methods in socket API both of them establish the full association.

What is the best way of understanding the Socket?

Lets us start learning about Stream Sockets. These are ones which are used maximum number of times. We will understand by taking an analogy with the buying a phone connection.

Steps in setting up the phone connection and corresponding analogy for Sockets.

Buy a PhoneCreate a socket
Get a phone numberbind
Start waiting for the calllisten
Answer the callaccept
Call to a phoneconnect
Speak on the Phonesend/sendto
Listen on a Phonereceive/recievefrom
End the callclosesocket

Now, we will try to write the programs putting together whatever theory we have learned till now. The real fun starts now.

Let us try to write a small server client application. This is the requirement:

  • The server will only cater to one client.
  • After the server is started, it should wait for the client to connect to it.
  • As soon as the client is connected, the client will send a message which the server will receive and send back a reply to the client. After that, the server and client finish up.

In the package, there is a folder named 1. Inside that folder, there are 2 folders, namely TCPClient and TCPServer. Build TCPClient Visual Studio project which is in TCPClient folder. In the folder TCPServer, there are 2 projects, namely TCPNonLiveServer and TCPLiveServer. Build these 2 projects too.

Let us try to understand Non Live Server first. It is non live because it accepts one client reply back to client and finishes up. Let’s try to see the code of TCPNonLiveServer.cpp.

  • WSAStartup() initializes the winsock library.
    • If some error occurs, we can get it by using WSAGetLastError().
  • Create a socket by using socket().
    • It takes 3 arguments:
      1. First one is Address format. We have used AF_INET which means the address format is in the form of IP and port number. There are other formats too.
      2. Second is type of connection. For TCP, we specify SOCK_STREAM. For UDP, SOCK_DGRAM is used.
      3. The protocol to be used. The possible options for the protocol parameter are specific to the address family and socket type specified. Possible values for the protocol are defined in the Winsock2.h. If a value of 0 is specified, the caller does not wish to specify a protocol and the service provider will choose the protocol to use.
  • Bid the socket by bind().
    • It takes 3 arguments:
      1. First argument is socket descriptor.
      2. Second argument is sockaddr_in which is a structure containing local IP, port number and address format. Details can be found on the web.
      3. Size if the struct passed in 2nd argument.
  • Listen for the incoming connections on the port by using listen().
    • It takes 2 arguments:
      1. Socket descriptor.
      2. The maximum length of the queue of pending connections.
  • This function will block the code flow till the new connection arrives.
    • Accept the incoming connections by using accept().
    • It takes 3 arguments:
      1. First argument is socket descriptor.
      2. Second argument is sockaddr_in which will be filled by the client details.
      3. Size if the struct passed in 2nd argument.
  • It returns a socket which will be used specifically for the client which has connected.
    • Receive the client message by using receive.
    • It takes 4 arguments:
      1. First one is the client specific socket descriptor which we have got as return value by accept().
      2. Character array for receiving the message.
      3. Size of the character array.
      4. Flag. More details can be found on the web.
  • It returns the size of the data received.
    • Send reply to the client by using send().
    • It takes 4 arguments:
      1. First one is the client specific socket descriptor which we have got as return value by accept().
      2. Character array which has the message which we have to send.
      3. Size of the character array.
      4. Flag. More details can be found on web.
  • Close the socket by using closesocket().
  • WSACleanup() to clean up the things which were initialized.

Let’s look at the client. Have a look at the TCPClient.cpp. I will not explain the functions which are already explained above.

  • Initialize the winsock library.
  • Create a socket.
  • Connect to the remote server by using connect.
    • It takes 3 arguments:
      1. Socket descriptor
      2. Second argument is sockaddr_in which is a structure containing remote IP (which the client wants to connect to), port number and address format. Details can be found on the web.
      3. Size of the struct passed as 2nd argument.
  • Send the message.
  • Receive the reply.
  • Close the socket.
  • WSACleanup() to clean up things.

Let’s run the server first. It can be done by running TCPNonLiveSrever.exe from command prompt. It will be waiting for the incoming connection. Now, go to the client and run the client by running TCPClient.exe. This EXE takes 1 argument which is the IP of the server (if we are running server and client on the same machine, it's nothing but the IP of the machine). Here are the outputs...

Client

TCPClient.exe 10.16.15.58
IP address is:10.16.15.58
Initialising Winsock...
Initialised.
Socket created
Connected
Data Send
Reply received
Hello Client , I have received your message. But I have to go now, bye

Server

TCPNonLiveSrever.exe
Initialising Winsock...
Initialised.
Socket created
Bind done
Waiting for incoming connections...
Connection accepted
Received message from client
Hello From client
Reply to the client

By this example. we have learnt how a small server and client can be written and they can communicate with each other. The server which we have written accepts 1 client and then just goes away. Now, let us try to enhance this server so that it is always waiting for the clients to come in. It can cater to many clients.

Here are the requirements:

  • The server should cater to many clients.
  • After the server is started, it should wait for the client to connect to it.
  • As soon as the client is connected, client will send a message which server will receive and send back a reply to client. In replying to the client, server should say what the number of clients which are connected is.
  • Server should be live. It should be running indefinitely (non stop).

Let us try to understand Live Server now. Let’s try to see the code of TCPLiveServer.cpp. It is similar to the above non live server which we have already learnt about. We have made some enhancements to that.

  • For making the server to run infinitely, we introduce infinite loop. We have put some code in the while loop. The while loop will run indefinitely and this is how the server keeps on running.
  • For each client connection, we will start a new thread.
C++
CreateThread(0, 0,(LPTHREAD_START_ROUTINE)InterpretClientMessage,(void*)client_message, 0, &threadID)
  • The threads call a method InterpretClientMessage() and in the thread, we pass the client message as argument. Inside the method, we just print the client message (you could have done more by using client message in some way). We also increase a counter (count) in a thread safe way using critical section. For synchronization objects in threads, please refer to my earlier article.
  • It is always good to understand threads as they will be always used in any real programming scenario now days. The article tells you some basics about Threads and there synchronization objects.

Let’s run the server first. It can be done by running TCPLiveSrever.exe from command prompt. It will be waiting for the incoming connections. Now go to the client and run the client by running TCPClient.exe. This EXE takes 1 argument which is the IP of the server (if we are running server and client on the same machine, it's nothing but the IP of the machine). Here is the output when we run the client 2 times.

Server

TCPLiveServer.exe	
Initialising Winsock...
Initialised.
Socket created
Bind done
Waiting for incoming connections...
I got a connection from10.16.15.58 :61622
Data received from client to server is :
Hello From client
Starting a thread for a connection
Inside InterpretClientMessage
LLLLLLLLL::::Hello From client
Count: 1
Reply from server to client: Hello Client , I have received your connection. You are the client no. 1
Data send from server to client
I got a connection from10.16.15.58 :61623
Data received from client to server is :
Hello From client
Starting a thread for a connection
Inside InterpretClientMessage
LLLLLLLLL::::Hello From client
Count: 2
Reply from server to client: Hello Client , I have received your connection. You are the client no. 2
Data send from server to client
Client1:
TCPClient.exe 10.16.15.58
IP address is:10.16.15.58
Initialising Winsock...
Initialised.
Socket created
Connected
Data Send
Reply received
Hello Client , I have received your connection. You are the client no. 1

Client2:
TCPClient.exe 10.16.15.58
IP address is:10.16.15.58
Initialising Winsock...
Initialised.
Socket created
Connected
Data Send
Reply received
Hello Client , I have received your connection. You are the client no. 2

Now, let’s try to create to enhance the client and server in the following way.

  • We will build a chat application by which many clients can connect to the server and chat can happen. Many clients should be able to connect.
  • In doing so, we will write some wrapper classes which will give us real feel of programming and will be very easy to use afterwards.

In the package, there is a folder named 2. Inside that folder, there are 2 Visual Studio projects, namely TCPDetailClient and TCPDetailsServer. Build both of them.

Let’s try to see the classes which are used in both of these projects one by one:

  • myException class (myException.h and myException.cpp) is straightforward. This is used to throw an exception. There is a test case nyExceptionTest in the Tests folder in the project. You can uncomment it and try to see the usage of the exception class.
  • myLog class (myLOg.h and myLog.cpp) is used to write the output in the log file.
    • We can declare the log object in the main (myLog winLog) and for printing in the log, we have to use << operator. You can see that in many places in the code.
  • myHostInfo class (myHostInfo.h and myHostInfo.cpp) is used to get the following things:
    • Retrieve the host name and host address given the host name.
    • Retrieve the host name and host address given the IP address.
    • We have used winsock library for this on Windows and we have put the exceptions if some errors occur.
    • There is a test case called myHostInfoTest in Tests folder. You can uncomment it and run it to understand the class more clearly. To run the test, you have to uncomment the main method and make sure all other main methods are commented in the project.
    • The code is written in such a way that this file can be used in WINDOWS and Linux both.
  • mySocket class (mySocket.h and mySocket.cpp) is a general socket class. This class contains methods for basic socket operations. There are more methods which are there which can be used for various other fine socket operations. The class myTcpSocket (myTcpSocket.h and myTcpSocket.cpp) is having same functionalities which were explained in first example. This class is derived from mySocket class. It uses some basic functionalities from that class.
    • All the methods in this class are kind of wrapper over basic socket functionalities which we looked into example 1.
    • All the functions in myTcpSocket class use the exception class to throw any error if happened.
    • The method sendMessage sends the message by appending the length of the message in the start of the message. This is just to make sure the integrity of message when receiver tries to process the message. This method uses socket send method. I think this should be very easy to understand. One important thing in here is that we append the length of the message in the start of the message (we reserve 6 characters for that). At this point of time, you should understand that the length of message which needs to be sent can be very big and it is quite possible that full message can’t be sent in one go (send message returns the length of message sent). This can be enhanced to accommodate the sending of big message.
    • Let us have a look at recieveMessage(). This method is designed to receive the message however big it is. The idea is we keep on receiving the message till we have got the full message. The size of the message which we appended in the start of message helps in that.
    • There is a file mySocketTest.cpp in the Tests folder. Uncomment and run it to get more idea on Socket class. I advise you to once debug all the Tests for better understanding.
  • In my previous article, I have used some of these classes which are used here as well.
    • The Thread class (myThread.h and myThread.cpp) is enhanced a bit as we have added some more functions. They are more or less self explanatory.
      • However we have introduced a new class myThreadArguemnt which can be used to pass the data in the threads. This class helps us in keeping the data passage to threads easy and simple.
    • The Event class is same as mySmartEvent class which is explained in the earlier article.
    • I recommend that you read this article first to get a full understanding about the Threads and Events.
    • The class mySemaphore is also explained in the previous article.

Now that we have an understanding of all the classes needed to write our chat application, we can start writing the Chat Server first.

Have a look at the winServerForMultipleClients.cpp in the TCPDetailServer project.

  • In the main function, we are starting a server thread. We pass socket, coutsemaphore (This is used to write in output in a thread safe way) and the server name.
C++
myThreadArgument* serverArgument = new myThreadArgument(&myServer,&coutSemaphore,serverName);
	myThread* serverThread = new myThread(serverHandleThread,(void*)serverArgument);
		serverThread->execute();
  • Keep the main flow to do the other task (This was the reason we have choose to start a new thread to run the server). The other task is to print the clients which are connected and which got disconnected. You can see that we are using the while loop to print the statistics infinitely.
  • Inside the server Thread, we bind the socket and start listening to the client connection.
C++
myServer->bindSocket();
myServer->listenToClient();
  • We add the client arguments to the server arguments (a list is maintained in serverarg class). This will be used to get the clients from the server while printing the statistics.
  • For each client who connects to server, we will start a new thread to cater to that client’s requirements. The client thread takes client socket (which is created for that client only), coutSemaphore and the client name.
  • The client thread does the following in a while loop (that will run infinitely):
    • It receives the message from client. Put it in the output (in console or in log).
C++
clientConnection->recieveMessage(messageFromClient);
    • It will send the message to the client whatever we type in console.
    • If client message is “quit”, it will break from the while loop and will set the exit event to be in signaled state.
C++
clientArgument->getExitEvent()->setEvent();
    • In the main function, we keep on looking for this event and if that is found to be in signaled state, we print that the client has closed the conncetion.
C++
If ( clientInfo && clientInfo->getExitEvent()
->waitForEvent(0) )
{
   clientInfo->setSignalToEnd(true);
   cout   << "         " << clientInfo->getHostName() << endl;
   winLog << "         " << clientInfo->getHostName() << endl;
	}
  • The log will be formed in the same directory in which TCPDETAILSrevr.exe is present with the name of syslog.log.

Let us now have a look at the myClient.cpp file in TCPDetailClient project.

  • Read the IP address of the server from the client config file (serverConfig.txt). It has to be present in the same folder as TCPDETAILClient.exe. A sample config file is in the project folder. You have to manually put it in the same directory as client EXE.
  • Create the socket for the client by using port number read above and the port number 1201. That can be changed if you want to. It has to be changed on both server and client side. As of now, it is hard coded at both places.
  • Inside the while loop, we keep on sending the message and receiving the message from the server. If the client is sending message “quit” to the server, we break from the loop hence ending the client.
  • Log file can be found in the same directory as client EXE with name syslog.log.

Lets run the server first. Put the IP address of the machine on which server is running in the client’s config file and run the client.

Client Output

TCPDETAILClient.exe
Name: INBAN-JW8B7BS.sncrcorp.net
Address: 10.16.15.58
Name: INBAN-JW8B7BS.sncrcorp.net
Address: 10.16.15.58

Summary of socket settings:
   Socket Id:     1884
   port #:        1201
   debug:         false
   reuse addr:    false
   keep alive:    false
   send buf size: 8192
   recv bug size: 8192
   blocking:      true
   linger on:     false
   linger seconds: 0

connecting to the server [INBAN-JW8B7BS.sncrcorp.net] ...
[SEND] hello server
[RECV:INBAN-JW8B7BS.sncrcorp.net]: hi client how are you
[SEND] I am fine
[RECV:INBAN-JW8B7BS.sncrcorp.net]: ok
[SEND] quit

Server Output

TCPDETAILServer.exe
my localhost (server) information:
        Name:    INBAN-JW8B7BS.sncrcorp.net
        Address: 10.16.15.58

Summary of socket settings:
   Socket Id:     1880
   port #:        1201
   debug:         false
   reuse addr:    false
   keep alive:    false
   send buf size: 8192
   recv bug size: 8192
   blocking:      true
   linger on:     false
   linger seconds: 0

server finishes binding process...
server is waiting for client calls ...

? A client from [INBAN-JW8B7BS.sncrcorp.net-A] is connected!

[RECV fr INBAN-JW8B7BS.sncrcorp.net-A]: hello server
[SEND to INBAN-JW8B7BS.sncrcorp.net-A]: hi client how are you
[RECV fr INBAN-JW8B7BS.sncrcorp.net-A]: I am fine
[SEND to INBAN-JW8B7BS.sncrcorp.net-A]: ok
[RECV fr INBAN-JW8B7BS.sncrcorp.net-A]: quit

server (name:INBAN-JW8B7BS.sncrcorp.net) status report:
   the following clients have successfully connected with server:
         INBAN-JW8B7BS.sncrcorp.net-A
   the following clients have shutdown the connection:
         INBAN-JW8B7BS.sncrcorp.net-A

You can connect many clients at a time and can connect to server. With this, we have finished our chat application.

Let us look at the socket which is created for a client. The server socket sends the message and waits for the reply. Once reply comes, it can send message again. This process is happening synchronously. The socket which we are using here sends a message and then waits. Till the reply comes, the program is blocked. As you would have guessed, till now, there can be different kinds of sockets.

Let’s try to understand different kinds of sockets:

  1. Blocking Sockets: The blocking sockets are those which block the execution of the program till it has finished its task or has failed while trying. The above example which we have seen is using these kinds of sockets.
  2. Non Blocking Sockets: Calls on non blocking socket returns immediately, even if they cannot complete their tasks immediately. Although this allows program to do other things while network operations finish, it requires that the program repeatedly poll to find out when each request has finished.
  3. Asynchronous Sockets: These are the non blocking sockets, except that you don’t have to poll, the stack sends the program special message when something interesting happens. I will explain more about these in detail in my next part of this series.

Let us try to learn about non blocking Sockets:

The ultimate aim of all this is that the server should be able to accept multiple clients and process them.

One way we have already seen in the Live Server where we start a thread for each connection.

The second way could be Polling. Polling involves monitoring of multiple sockets to see if something has happened on them.

To solve this issue, the idea of non blocking sockets came. Here is what the server will do:

  1. It will maintain a collection of master sockets and the entire clients socket at any time.
  2. If anything happens on the socket, the system gets informed (this is done by select method of winsock API).
    1. On master socket, we can have the activity that a new client has connected.
    2. On the client’s socket, the activity can be that client sends some message.
  3. For getting the information, we keep on polling (this is generally done by using an infinite loop in the code) on all the sockets.

We have learnt about the non blocking sockets. Let’s try to implement whatever we have learned.

Here are the requirements:

  1. The server should run and wait for incoming client connections.
  2. N number of clients can be connected at the same time.
  3. As soon as the client is connected, server sends a welcome message to the client.
  4. Client can send some message to the server which server will echo back.
  5. If client disconnects, the server should disconnect it gracefully.

In the package, we have a folder called NonBlockingServer which contains a Visual Studio project called TCPServerMultipleConnections. Build the project.

Let’s have a look at the TCPServerMultipleConnections.cpp.

  • In the main function, we create a set (fd_set readfds) which will hold the sockets which have any read activity. Details of fd_set is mentioned in the CPP file.
  • Create a master socket and bind it to local IP address and port 8888. After that is done, start listening for the incoming connection.
  • Next, you will notice that we have kept the code in the while loop. This way, we are polling for the activities on all the sockets (master and clients).
  • First things we do in loop is clear the set, add the master socket and client sockets present in the client socket array at any point of time.
C++
//clear the socket fd set
        FD_ZERO(&readfds);
  
        //add master socket to fd set
        FD_SET(master, &readfds);
         
        //add child(clients) sockets to fd set
        for (i = 0 ; i < max_clients ; i++)
        {
            s = client_socket[i];
            if(s > 0)
            {
                FD_SET( s , &readfds);
            }
        }
  • We use select method and pass the setoff sockets to it. When select returns, it re-fills the same readfds array with the readable sockets. Readable sockets means that there was some activity on those sockets and the activity is read. We can get the write and errored out sockets too. I have given a brief explanation about the select in the code.
  • Now, we know which all sockets have an activity.
    • If the socket is master socket, the activity will be that a client will be trying to connect. So server will accept the connection, sends client welcome message and then adds the client socket to the client socket list (This will be used).This is done from line 140 to 172.
    • If the socket is client socket, the activity will be that client will be trying to send some data. We check all connected client sockets for activity one by one. This is done by seeing if the client socket is present in the set returned by select function. If there is an activity on the client, we print the client details and send the same message to the client which server receives. This is done from line 174 to 226.
      • Client socket can close the connection. If server gets that client is closing the connection, it closes the client socket gracefully and removes the entry from client socket list.
C++
if (valread == 0)
{
   //Somebody disconnected , get his details and print
cout<<"Host disconnected unexpectedly , ip "<< inet_ntoa(address.sin_addr)<<", 
port "<< ntohs(address.sin_port)<endl; client_socket[i]="0;"
  • We have understood the code, now it’s time to run the server.
  • We will use the putty client for testing our server. Launch putty and go to Connection > Telnet and select Passive mode. This will make putty line mode. Then, come back to Session tab and enter the hostname and port on which server is running and click open. It will connect to the server and start a black telnet like terminal.
  • We will run 2 clients simultaneously. You will see server is able to cater to both clients simultaneously.
  • You can close one of the clients and you will notice that server has closed the connection gracefully and printed in the console the details of the client which closed the connection. Try it yourself.

Server Output

TCPServerMultipleConnections.exe
Initialising Winsock...
Initialised
Socket created
Bind done
Waiting for incoming connections...
New connection , socket fd is : 1896 , ip is : 192.168.1.4, port : 18764
Welcome message sent successfully
Adding to list of sockets at index: 0
New connection , socket fd is : 1880 , ip is : 192.168.1.4, port : 18765
Welcome message sent successfully
Adding to list of sockets at index: 1
192.168.1.4 18764 hello

192.168.1.4 18765 hey

Client 1 Output

Hello Client You are connected
hello
hello

Client 2 Output

Hello Client You are connected
hey
hey

Conclusion

With this, I will conclude my article. Hope this article will be useful for people who want to learn network programming in an easy and simple but comprehensive way. Please post your comments and suggestions. In my next article, I will be explaining asynchronous sockets. We will be learning about http protocol and will be writing a real working http server. We will use asynchronous sockets in writing our server.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India
I am currently working as Technical Expert with a Product Based firm and have primarily worked in C, C++ & COM. Having been a part of the IT industry for more than 7 years, I have a keen interest in learning different technologies and concepts and translating them into real life examples. I believe that anything well understood remains with you forever and is easy to recall whenever necessary. As Einstien has famously quoted, "If you can't explain it to a six year old, you don't understand it yourself."

I hope you find my blog useful. Please feel free to post comments and questions or any findings you may have found to my posts.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Eric Jiang20-Oct-13 4:12
Eric Jiang20-Oct-13 4:12 
GeneralMy vote of 5 Pin
Balkrishna Patel8-Jul-13 18:53
Balkrishna Patel8-Jul-13 18:53 
GeneralRe: My vote of 5 Pin
Priyank Purwar8-Jul-13 19:40
Priyank Purwar8-Jul-13 19:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.