These are some notes I was writing while viewing an amazing youtube video series from IIT Khanpur: Client Server Programming in Java Lesson # 30. I thought I should share it with you for those who do not have an hour to watch the entire lecture:
Recap on client/server process: Remember, a client and a server: A service is providing services to entities that are called clients, requesting the service, may be run on different machines, servers wait for requests from clients. Iterative servers know how much time between a request, & can handle the service itself, because it knows how long it would be pre-occupied with a request, one client at a time (server is busy), other clients have to wait. In concurrent servers you cannot predict the time to handle a request, it will create another process, and thee server itself will go back and wait. For example, if there are 100 concurrent client requests, it can provide 100 different copies of the server catering to a client request in a dedicated fashion, no sharing or making other clients wait. In a typical secnario, the server initializes and goes in a sleep state waiting for a client request.
A client process starts, either on same machine or other, and sends request to the server. When the server process has finished, it will go back to sleep waiting for the next request.
Using TCP or UDP?
Before start of communication, a connection has to be established between the two hosts. If you want protocol itself should handle reliability, flow control, guaranteed order of delivery of packets, then use TCP. But if you have some app where if some packets are lost, you dont care, you want to provide all these services that TCP provides normally, you want to provide them in your application layer, then possibly you can use UDP, because in general UDP is faster, it will try to deliver packets as fast as the network can, it will use best parts of network.
5 components in a connection: Protocol used, Source IP address, Source port number, Dest. IP address, Dest. port number.
What is a Socket?
A socket is a term which was initially coined by the BSD (Berkeley Software Distr.) method for achieving inte-process communication (IPC).
It is used to allow one process to speak to another on the same or diff machine. Good analogy: In a telephone conversation (you, the client), you are speaking and the other person (server) is listening, then will respond.
Using a socket is convenient because once you have opened the socket by speciying all the relevant info, you can treat the network connection the same way you are reading or writing a file, the same way you can read or write to a socket. Socket is an abstraction you can use at the "transport" layer level, to make developing your tasks similar.
The basic idea when two processes located on two machines communicate with each other, we define association and socket, association: Protocol, Local IP address, Local port number, Remote IP Address, Remote Port Number. Socket: also called "half-association" as a 3-tuple, Protocol , local IP address, local port number & the other side, the protocol, remote IP address, remote port number.
With the summary of what sockets are, lets get into Networking Programming with Java.
Networking Programming with Java : Sockets
In Java & C, as well as other programming languages, the way we write network programs is similar to how we access and read files.
Real programs have to access external data to accomplish their goals, which Java provides a number of ways for accessing external data. Java provides a number of libraries and classes to facilitate accessing data. It's handled in a very uniform way, does not make the distinction whether we are reading from a file, reading from a keyword, or reading from a socket.
Important: An object from which we can read a sequence of bytes is called an input stream (example, a file, keyboard, Scanner, a network socket). An object to which we can write a sequence of bytes is called an output stream (example, the scren, file, network socket, etc). Whenever developing a network app in Java, we are actually concerned with I/O streams and how to handle them.
Input and output streams are implemented in Java as part of the abstract classes InputStream and OutputStream . Java provides a large number of concrete subclasses of InputStream and OutputStream to handle a wide variety of input-output option. InputStream and OutputStream are generic classes for inputs and outputs of bytes, there can be separate subclasses for console i/o, networks, files, etc.
Using DataInputStream
Many applications need to read in an entire line of text at a time.
DataInputStream class and its readLine() method can be used (readLine() can read one line of text from some input source at a time, it will read the characters up to the end of the end line character, when its written back it depends on the way you store it, whether you actually store the end line character or not.
The readLine() method reads in a line of ASCII text & converts it to a Unicode string (16-bit unicode characters).
The following example shows how we can read a line of text from a given file:
DataInputStream inp = new DataInputStream (new FileInputStream("student.dat"));
String line = inp.readLine();
We create a new object of DataInputStream of variable inp, we then tell it the kind of Input Stream inside with FileInputStream. FileInputStream is a subclass of InputStream, and takes a parameter of the name of the file. It assumes the file you are opening, will be opened for input and you can start reading the characters from there, you can use FileInputStream or FileOutputStream, as input/output.
The java.net package uses networking. It supports the TCP and UDP protocol families.
An example is shown next: Connecting to a specified host over a specified port, and printing out whatever is returned. There are two things to remember when connecting to a server: The IP address of the server and the port number which the particular processes on the server needs to be contacted. After the connection is established, the server will be responding back with some string and it will be displayed on your screen.
In the following example, the socket class is to be used by the client program because when the client program starts, it should start by trying to connect to the server. All networking code should be encased in a try.. catch block, & most of the network-related methods throw IOException whenever some errors occur.
import java.io.*;
import java.net.*;
class ConnectDemo
{
public static void main(String args[])
{
try
{
Socket s = new Socket("10.5.18.213", 225);
DataInputStream inp = new DataInputStream(s.getInputStream());
boolean more_data = true;
System.out.println("Established connection");
while (more_data)
{
String line = inp.readLine();
// if line == null means no more text to come
if (line == null) more_data = false;
else System.out.println(line);
}
}
catch (IOException e)
{
System.out.println("IOerror " + e) ;
}
}
}
Implementing Servers
We will now show an implementation of a server program in Java, when a client program connects to this server, it sends a welcome message and echoes client input one line at a time. When you are writing the program and sequencing, there will be an asymmetric relationship, the coding is a bit different.
In this server, we are creating a ServerSocket object a different class. We do not need to specify an IP address, because we are not connecting to other servers, a single parameter (7500 here) , is the port number it should be continuously listening to. sock.accept() is the function call or method that makes the server wait for the client request to come, otherwise blocked. It will move on to the next line only after the client request come, a newsocket descripter will contain detailed information about the client request at newsock, and the server creates a new input stream. Then we will echo it to the screen with PrintStream with get the newsock.getOutputStream().
import java.io.*;
import java.net.*;
public class SimpleServer // If this were looping, example of an iterative server
{
public static void main(String args[])
{
try
{
ServerSocket sock = new ServerSocket(7500);
Socket newsock = sock.accept();
DataInputStream inp = new DataInputStream(newsock.getInputStream());
// will take in input from client?
PrintStream outp = new PrintStream(newsock.getOutputStream());
outp.println("Hello :: enter QUIT to exit");
// client will receive outp on the other side.
boolean more_data = true;
while (more_data)
{
String line = inp.readLine();
if (line == null) more_data = false;
else
{
outp.println("From server: " + line + "\n");
if (line.trim().equals("QUIT"))
more_data = false;
}
}
newsock.close();
}
catch (Exception e)
{ System.out.println("IO error " + e); }
}
}
Some points:
Once accept() call returns a Socket object newsock, the getInputStream() and getOutputStream() methods are used to get an input stream and an output stream respectively from that socket.
Everything the server program sends to the output stream will become the input of the client program. Outputs of the client program become the input stream of the server.
Part 2: Concurrent Server