Sunday 23 August 2015

Simple guide to TCP and UDP sockets

Introduction

Today we will analyze and build some simple TCP and UDP client and server based applications using Java in Netbeans.

Processes communicate with each other by exchanging messages across a computer network. The process that initiates this process are called the client and the one that waits for the response is called the server. One of the most basic conditions for programming in a distributed system is the possibility to transfer data from one process to another. In this situation the process that initiates the communication is labelled as the client and the one that waits to be contacted is the labelled as the server. Computers that are connected to a TCP/IP network are identified by their IP address and port number. The port number is managed by the operating system and is allocated to processes required to use the network.


A port number is a 16-bit unsigned integer from the range of 0 to 65535. Sometimes we refer to a port number simply as a port. A computer runs many processes, which commmunicate with other processes running on remote computers.When the transport layer recievives an incoming packet from the Internet layer, it needs to know which process on that computer should this packet be delivered to. A port number is a logical number that is used by the transport layer to recognize a destination process for a packet on a computer.

Each incoming packet to the transport layer has a protocol; for example the TCP protocol handler in the transport layer handles TCP packet and the UDP protocol handler in the transport layer handles a UDP packet.
Some well known Ports are: 0 - 1023
Registered Ports: 1024 - 49151
Dynamic or Private Ports: 49152 - 65535

echo: Port 7
FTP: Port 21
Telnet: Port 23
SMTP: 25
POP3: 110
NNTP: 119


UDP and TCP use socket abstraction for inter-process communication. A process sends messages and receives messages from the underlying network through its socket. The sending client must identify the receiving server. This requires two pieces of information, an IP address to identify the destination host, and a unique port number to identify the receiving process running in that host.

Following is an example of how a TCP Client/Server Application in Java is performed; first a client reads a line from standard input keyboard and sends the line out on its socket to the server. The server uses its listening socket to listen for a connection to be made. When a connection is made the server creates a connection socket between the client and server. The server then reads the client’s line from its connection socket. The server converts the line to uppercase. The server sends the modified line out on its connection socket to the client. The client reads the modified line from its socket and prints the line on its standard output monitor.

Socket Programming with TCP

The API (application programming interface) to the TCP protocol provides the abstraction of a stream of bytes to which data may be written and from which data may be read. There are two kindsm of sockets, a Connection-Oriented Socket and a Connectionless Socket.

A connection-oriented socket is also called a steam socket. A connectionless socket is also called a datagram socket.

Transmission Control Protocol (TCP), is used in the transport layer and is one of the most widely used protocols to provide connection-oriented sockets. The application hands over data to a TCP socket and then the TCP takes care of streaming the data to the destination host. TCP takes care of ordering, assembly, fragmentation, lost data and data detection.

User Datagram Protocol (UDP), is used in the transport layer, it is the most widely used protocol that provides a connectionless socket. It is unreliable but is much faster than TCP. It allows us to send limited sized data.

Deciding to use which protocol depends on how the application will be used. If data integrity is important than we will have to use TCP. If speed is a priority then we should UDP. For example for file transfer we may use TCP but for a video call we may use UDP.

In order to react to a client’s initial contact, a server program must be running as a process, and it must have a socket listening for a connection. The client program creates a stream socket bound to any port, specifying the IP address of the server host and the port number of the server process. The client then initiates a three-way handshake and establishes a TCP connection with the server.

During the three-way handshake (which is transparent to the client and server programs), the server hears the connection request on its listening socket, a ServerSocket object (named listenSocket in the example below). It reacts by accepting the connect setup, i.e. it invokes the listening socket’s accept() method, which creates a new connection socket, this time a Socket object, (named connectionSocket in the example below), dedicated to this particular client


From the application’s perspective, the TCP connection is a direct virtual pipe between the client’s socket and the server’s connection socket. Thus, TCP provides a reliable byte-stream service between client and server processes, in which each byte is delivered in the order sent. Client and server processes can each both send and receive bytes through the same socket. This is illustrated in Fig 1 below. Because sockets play such a central role in client/server applications, client/server development is also referred to as socket programming.

To be able to exchange data over sockets Java uses streams. A stream is a sequence of characters that flow into or out of a process. Each stream is either an input stream for the process or an output stream for the process. If it is an input stream, then it is attached to some input source, such as standard input (the keyboard) or a socket into which data flows from the Internet. If it is an output stream, then it is attached to some output source, such as standard output (the monitor) or a socket out of which data flows into the Internet. Streams transfer byte arrays of data across the network.

Client Socket, Server Listening socket, and Server Connection Socket


Example TCP Client/Server Application in Java
The following client/server application is used to demonstrate socket programming for TCP (and for UDP in a later section):
1.     A client reads a line from standard input (keyboard) and sends the line out on its socket to the server.
2.     The server uses its listening socket to listen for a connection to be made.
3.     When a connection is made the server creates a connection socket between the client and server
4.     The server reads the client’s line from its connection socket.
5.     The server converts the line to uppercase.
6.     The server sends the modified line out on its connection socket to the client.
7.     The client reads the modified line from its socket and prints the line on its standard output (monitor).

The client program is called TCPClient.java, and the server program is TCPServer.java. They are both presented below with extensive commenting. The two programs are first compiled on their respective hosts. Then the server program is executed to create a server process at the server host, which waits to be contacted. When the client program is executed, a process is created at the client, and this process immediately contacts the server and establishes a TCP connection with it. The user at the client may then use the application to send a line to the server which converts it to uppercase and returns this capitalized version to the client.

The java.io package provides classes for io (input/output) support and the java.net package provides classes for network support. These two packages must be imported in our client and server programs.


Code for the client side of the application: TCPClient.java:

import java.io.*; import java.net.*; public class TCPClient { public static void main(String argv[]) throws Exception { System.out.println("Please enter a sentence for the TCP server to capitalise:"); //Create an input stream and attach it to the keyboard InputStream inFromUser = System.in; //Create a byte array to hold the user's input byte inFromUserByteArray[] = new byte[100]; //Read the user's input from the input stream into the byte array inFromUser.read(inFromUserByteArray); //Create a socket object & initiate the TCP connection between client & server Socket clientSocket = new Socket("hostname", 6789); //Create an output stream and attach it to the socket object OutputStream outToServer = clientSocket.getOutputStream(); //Write the data in the byte array to the output stream outToServer.write(inFromUserByteArray); //Create an input stream to read in the server's reply from the socket InputStream inFromServer = clientSocket.getInputStream(); //Create a byte array to hold the server's repsonse byte inFromServerByteArray[] = new byte[100]; //Read the server's response in the input stream into the byte array inFromServer.read(inFromServerByteArray); //Create a string using the bytes read into the byte array String modifiedSentence = new String(inFromServerByteArray); //Print the server's reply on the terminal System.out.println("Reply from TCP server is : " + modifiedSentence); //Close the socket clientSocket.close(); } }  









Code for the server side of the application: TCPServer.java:

import java.io.*; import java.net.*; public class TCPServer { public static void main(String argv[]) throws Exception { //Create a listening socket object to listen for a client connection ServerSocket listenSocket = new ServerSocket(6789); while (true) { System.out.println(“TCP Server is waiting for a client to connect”); //When a connection is requested accept it and provide a connection socket for communication with the client Socket connectionSocket = listenSocket.accept(); //Attach an input stream to the connection socket InputStream inFromClient = connectionSocket.getInputStream(); //create a byte array to hold data from the input stream byte inFromClientByteArray[] = new byte[100]; //Read the data from the input stream into the byte array inFromClient.read(inFromClientByteArray); //Create a string from the bytes in the byte array String clientSentence = new String(inFromClientByteArray); System.out.println(“Recieved from client: + clientSentence); //Capitalise the string String capitalisiedSentence = clientSentence.toUpperCase(); //Create an output stream OutputStream outToClient = connectionSocket.getOutputStream(); //Using the output stream send the capiatalised sentence to the client outToClient.write(capitalisiedSentence.getBytes()); } } }
Note that the server’s connection socket has the same port number as the client’s socket. When the connection socket is created (by calling the listening socket’s accept() method) a direct virtual pipe between clientSocket at the client and connectionSocket at the server is established. The client and server can now exchange bytes, and all bytes sent arrive at the other side in order. With connectionSocket established, the server can continue to listen for requests from other clients for the application using listenSocket (though the server code above would need to be modified with threads to do so).

1.              Test this application
The code for the TCP client and server classes is presented. Create the appropriate files, and then compile and run the two programs in the correct order. Use the application by typing a sentence using the keyboard (standard input) at the client.
Note that in the client program, the name “hostname” must be replaced with the IP address of the server in order for the application to work. The host name localhost or the IP address 127.0.0.1 can be used if the client and server processes are run on the same machine.
2.              Test the application with a remote host
Close the previously running client and server processes. Now run the client program on one machine and the server process on another machine. In order to do this you will have to modify the client code to include the IP address of the server machine (run the DOS command ipconfig in order to find the IP address of a PC).


Socket Programming with UDP
When two processes communicate over TCP, they simply insert bytes in the pipe (stream) between them. No destination address is required for the bytes because the pipe is logically connected to the destination. The pipe provides a reliable byte stream channel – the sequence of bytes received is exactly the same (in the correct order) as the one sent.
Similarly to TCP, UDP allows two or more processes running on different hosts to communicate. As UDP is a connectionless service, no handshaking phase and no pipe is established. As there is no pipe, the destination address must be attached to each batch of bytes. The destination address consists of an IP address and port number, and these bytes along with the data bytes form a packet or datagram. UDP provides an unreliable message-oriented transport service in that it makes a best effort to deliver packets, but makes no guarantees.
The code for a UDP based application differs significantly from TCP:
  • There is no initial handshaking, so there is no need for a listening socket
  • Streams are not attached to sockets
  • The sending hosts create packets by attaching the IP destination address and port number to each batch of bytes sent
  • The receiving process must unravel each received packet to obtain the packet’s data bytes.
Example UDP Client/Server Application in Java
The previous client/server application is used again to demonstrate socket programming for UDP.
Code for the client side of the application: UDPClient.java:


  import java.io.*;
  import java.net.*;
  public class UDPClient {
     public static void main(String args[]) throws Exception {
       System.out.println("Please enter the sentence that the server should capitalise: ");

       InputStream inFromUser = System.in;
       byte sendDataByteArray[] = new byte[1024];
       inFromUser.read(sendDataByteArray);

       InetAddress IPAddress = InetAddress.getByName("127.0.0.1");
       DatagramPacket sendPacket =
          new DatagramPacket(sendDataByteArray, sendDataByteArray.length,
             IPAddress, 4444);
       DatagramSocket clientSocket = new DatagramSocket();
       clientSocket.send(sendPacket);

       byte[] receiveData = new byte[1024];
       DatagramPacket receivePacket =
          new DatagramPacket(receiveData, receiveData.length);
       clientSocket.receive(receivePacket);
       String modifiedSentence =
          new String(receivePacket.getData());
       System.out.println("Recieved from server: " + modifiedSentence);
       clientSocket.close();
    }
 }
Code for the server side of the application: UDPServer.java:

 import java.io.*;

 import java.net.*;

  public class UDPServer {

     public static void main(String args[]) throws Exception {

        DatagramSocket serverSocket = new DatagramSocket(4444);

        while (true) {

            System.out.println(UDP Server is up and running......);

           byte[] receiveData = new byte[1024];

           DatagramPacket receivePacket =

             new DatagramPacket(receiveData, receiveData.length);

          serverSocket.receive(receivePacket);

          String clientSentence = new String (receivePacket.getData());

           System.out.println(UDP Client sent :  + clientSentence);

          InetAddress IPAddress = receivePacket.getAddress();

          int port = receivePacket.getPort();

 

          String capitalisiedSentence = clientSentence.toUpperCase();

          byte[] sendData = new byte[1024];

          sendData = capitalisiedSentence.getBytes();

          DatagramPacket sendPacket =

             new DatagramPacket(sendData, sendData.length,

                IPAddress, port);

          serverSocket.send(sendPacket);

       }

    }

 }

No comments:

Post a Comment