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