Java Network Programming Using Socket
In this tutorial, we will cover networking with Java Sockets. We will examine some of the classes in the java.net package, and show how to write simple network server and client programs using Java. First, however, we need to understand some background detail of networking.
Basics of Internet
The Internet comprises millions of computers, located all across the globe, communicating and transmitting information over a variety of computing systems, platforms, and networking equipment. Each of these computers (unless they are connecting via an intranet) will have a unique IP address (IPv4: 32 bit or IPv6: 128 bit). In this tutorial, 32-bit IPv4 addresses will be used.
IPv4 addresses are denoted by 32-bit binary numbers containing four octets (8 bit numbers), separated by a decimal dot. Each computer with a direct internet connection will have a unique IPv4 address, (e.g. 202.54.132.197). Some computers have temporary addresses, such as when we connect to the ISP through a modem. Others have permanent addresses, and some even have their own unique domain names (e.g. www.google.com).
The IP addressing allows us to uniquely identify a networking device or system connected to the Internet. If I would like to connect to a specific IP address, and send a message, I could do so. Without the IP addressing, the message would have no way of reaching its destination – just like leaving the address off a letter or parcel.
Sometimes, computer systems are connected to the Internet Service Providers (ISPs). Since computers are capable of providing more than one type of service, we need a way to uniquely identify each service. Like an IP address, we could use a number. We call this number a port. Common services such as HTTP, FTP, DNS, TELNET, SMTP etc. have well-known port numbers. For example, most web servers use port 80. Of course, we can use any port number we like – there’s no rule that says we must use port 80 for web services.
Figure 1: Ports help computers identify the specific service
There are many communications mechanisms that we can use to provide network services at the Transport Layer of TCP/IP Reference Model (i.e. Internet Model). Either we can use UDP (User Datagram Protocol) or TCP (Transmission Control Protocol). For the purpose of this selection, we will choose TCP, because it makes life much easier. TCP will ensure the successful delivery of a message at their destination. UDP is unreliable, and our application isn’t notified if the message is lost in transit. Also, many protocols (such as HTTP, SMTP, POP and FTP) use TCP, so it’s important that we are familiar with it for networking in Java.
Internet Addressing with Java
Dealing with internet addresses (domain names, and IP addresses) has been made easy with Java. Internet addresses are represented in Java by the java.net.InetAddress class. InetAddress class provides simple methods to convert between domain names, and numbered addresses. We start by importing the java.net package, which contains a set of pre-written classes (including InetAddress class).
import java.net.*;
Then, we declare an object of type InetAddress, which we assign the value of the local host machine (for machines not connected to a network, this should represent 127.0.0.1). Due to the fact that InetAddresses class can generate exceptions, we must place this code within a try .. catch UnknownHostException block.
// Obtain the InetAddress of the computer on which this program is running
InetAddress localaddr = InetAddress.getLocalHost();
The InetAddress class has methods that return the IPv4 address as an array of bytes (which can be easily converted into a string), as well as a string representation of its domain name (e.g. techguruspeaks.com ). We can display this InternetAddress, as well as the domain name of the local address.
System.out.println ("Local IP Address : " + localaddr );
System.out.println ("Local hostname : " + localaddr.getHostName());
Note: All the codes are placed within “C:\net” directory in MS Windows.
The complete code is given below:
MyFirstInternetAddress.java
import java.net.*;
public class MyFirstInternetAddress {
public static void main(String args[]) {
try {
InetAddress localaddr = InetAddress.getLocalHost();
System.out.println("Local IP Address : " + localaddr);
System.out.println("Local hostname : " + localaddr.getHostName());
}
catch (UnknownHostException e) {
System.err.println("Can't detect localhost : " + e);
}
}
// Converts a byte_array of octets into a string
public static String byteToStr(byte[] byte_arr) {
StringBuffer internal_buffer = new StringBuffer();
// Keep looping, and adding octets to the IP Address
for (int index = 0; index < byte_arr.length - 1; index++) {
internal_buffer.append(String.valueOf(byte_arr[index]) + ".");
}
// Add the final octet, but no trailing '.'
internal_buffer.append(String.valueOf(byte_arr.length));
return internal_buffer.toString();
}
}
If we compile and run this Java application and we could be able to know our local IP address, and hostname. For this, our computer needs not be connected to the Internet, though. Provided our system has a TCP/IP stack, it should give us back an IPv4 address even if we aren’t currently connected. On most systems, we can refer to our local machine (which often has the hostname “localhost”) as IP address 127.0.0.1, this address is known as a loopback address. Every time we connect to this address, we’re actually connected to our local machine. So, if we were running a local web server, and we pointed our browser to http://127.0.0.1, we should see our web site.
This is great when developing Java applications. We don’t need a permanent connection to the Internet – we can run client and server applications on our own machine.
The output is given below for ready reference:
C:\net>java MyFirstInternetAddress
Local IP Address : DESKTOP-8628OAC/192.168.43.129
Local hostname : DESKTOP-8628OAC
Socket programming
This section presents an introduction to Java Sockets Programming over TCP/IP networks and shows how to write client/server applications in Java.
Basics of Socket
The UNIX input/output (I/O) system follows a paradigm usually referred to as Open-Read-Write-Close. Before a user process can perform I/O operations, it calls Open to specify and obtain permissions for the file or device to be used. Once an object has been opened, the user process makes one or more calls to Read or Write data. Read reads data from the object and transfers it to the user process, while Write transfers data from the user process to the object. After all transfer operations are complete, the user process calls Close to inform the operating system that it has finished using that object.
When facilities for Inter Process Communication (IPC) and networking were added to UNIX, the idea was to make the interface to IPC similar to that of file I/O. In UNIX, a process has a set of I/O descriptors that one reads from and writes to. These descriptors may refer to files, devices, or communication channels (sockets). The lifetime of a descriptor is made up of three phases: creation (open socket), reading and writing (receive and send to socket), and destruction (close socket).
The IPC interface in BSD-like versions of UNIX is implemented as a layer over the network TCP and UDP protocols. Message destinations are specified as socket addresses; each socket address is a communication identifier that consists of a port number and an Internet address.
The IPC operations are based on socket pairs, one belonging to a communication process. IPC is done by exchanging some data through transmitting that data in a message between a socket in one process and another socket in another process. When messages are sent, the messages are queued at the sending socket until the underlying network protocol has transmitted them. When they arrive, the messages are queued at the receiving socket until the receiving process makes the necessary calls to receive them.
Description of Socket
A Socket is a combination of an IP address and a port number. A socket address lets other computers on the network locate a certain program running on a certain computer. We may represent a socket address like 64.104.137.58:80, where 64.104.137.58 is the IP address and 80 is the port number.
Therefore, Socket Address = IP Address + Port Number.
Where, An IP address = uniquely identifies a computer on the network.
and A Port Number = uniquely identifies a program running on a computer.
So, in other words,
A Socket Address = uniquely identifies a program on the network.
TCP and UDP communications
There are two communication protocols that one can use for socket programming: datagram based communication and stream based communication.
Datagram based communication:
The datagram communication protocol, known as UDP (user datagram protocol), is a connectionless protocol, meaning that each time we send datagrams, we also need to send the local socket descriptor and the receiving socket’s address. As we can tell, additional data must be sent each time a communication is made.
Stream based communication:
The stream communication protocol is known as TCP (Transmission Control Protocol). Unlike UDP, TCP is a connection-oriented protocol. In order to do communication over the TCP protocol, a connection must first be established between the pair of sockets. While one of the sockets listens for a connection request (server), the other asks for a connection (client). Once two sockets have been connected, they can be used to transmit data in both (or either one of the) directions.
Now, the question that arises in our mind is what protocol we should use — UDP or TCP? This depends on the client/server application we are writing. The following discussion shows the differences between the UDP and TCP protocols; this might help us decide which protocol we should use.
In UDP, as we have read above, every time we send a datagram, we have to send the local descriptor and the socket address of the receiving socket along with it. Since TCP is a connection-oriented protocol, on the other hand, a connection must be established before communications between the pair of sockets start. So there is a connection setup time in TCP.
In UDP, there is a size limit of 64 kilobytes on datagrams we can send to a specified location, while in TCP there is no limit. Once a connection is established, the pair of sockets behaves like streams: All available data are read immediately in the same order in which they are received.
UDP is an unreliable protocol — there is no guarantee that the datagrams we have sent will be received in the same order by the receiving socket. On the other hand, TCP is a reliable protocol; it is guaranteed that the packets we send will be received in the order in which they were sent.
In short, TCP is useful for implementing network services — such as remote login (rlogin, telnet) and file transfer (FTP) — which require data of indefinite length to be transferred. UDP is less complex and incurs fewer overheads. It is often used in implementing client/server applications in distributed systems built over local area networks.
Programming sockets in Java
In this section we will answer the most frequently asked questions about programming sockets in Java. Then we will show some examples of how to write client and server applications.
How to program
We are going to write some very simple Java code, that’ll demonstrate the use of sockets. Here is what is going to happen –
- One Java program will try to connect to another Java program (which is desperately waiting for someone to contact it). Let’s call the first program as Server, and the second as Client.
- Once connected, the client program is going to accept whatever we type, and send it dutifully to the server program.
- The server is going to send back the same text to the client, just to show that it is least interested in doing such an uninteresting thing.
- The client, after getting back the same text from the server, is going to throw it on our face, showing what the server thinks about us.
Let us create two fresh Java programs and call them Server.java and Client.java. I’ll describe the code below.
Server.java
import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) {
int port = 9999; // just a random port. make sure you enter something between 1024 and 65535.
try {
// create a server socket and bind it to the above port number.
ServerSocket ss = new ServerSocket(port);
System.out.println("Waiting for a client...");
// make the server listen for a connection, and let you know when it gets one.
Socket socket = ss.accept();
System.out.println("Got a client :) ... Finally, someone saw me through all the cover!");
System.out.println();
// Get the input and output streams of the socket, so that you can receive and send data to the client.
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
// Just converting them to different streams, so that string handling becomes easier.
DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);
String line = null;
while (true) {
line = in.readUTF(); // wait for the client to send a line of text.
System.out.println("The dumb client just sent me this line : " + line);
System.out.println("I'm sending it back...");
out.writeUTF(line); // send the same line back to the client.
out.flush(); // flush the stream to ensure that the data reaches the other end.
System.out.println("Waiting for the next line...");
System.out.println();
}
}
catch (Exception x) {
x.printStackTrace();
}
}
}
Client.java
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) {
int serverPort = 9999; // make sure you give the port number on which the server is listening
String address = "127.0.0.1"; // this is the IP address of the server program's computer
try {
// create an object that represents the above IP address
InetAddress ipAddress = InetAddress.getByName(address);
System.out.println("Any of you heard of a socket with IP address " + address + " and port " + serverPort + "?");
// create a socket with the server's IP address and server's port
Socket socket = new Socket(ipAddress, serverPort);
System.out.println("Yes! I just got hold of the program.");
// Get the input and output streams of the socket, so that you can receive and send data to the client
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
// Just converting them to different streams, so that string handling becomes easier
DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);
// Create a stream to read from the keyboard
BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
String line = null;
System.out.println("Type in something and press enter. Will send it to the server and tell ya what it thinks.");
System.out.println();
while (true) {
line = keyboard.readLine(); // wait for the user to type in something and press enter.
System.out.println("Sending this line to the server...");
out.writeUTF(line); // send the above line to the server.
out.flush(); // flush the stream to ensure that the data reaches the other end.
line = in.readUTF(); // wait for the server to send a line of text.
System.out.println("The server was very polite. It sent me this : " + line);
System.out.println("Looks like the server is pleased with us. Go ahead and enter more lines.");
System.out.println();
}
} catch (Exception x) {
x.printStackTrace();
}
}
}
Compile the programs with:
C:\net> javac Server.java Client.java
Open two command prompts (cmd). In one of those, enter
C:\net> java Server
and in the other,
C:\net>java Client
(Follow that order properly)
Type something on the client window and press enter. Observe both windows and see what happens. Finally, press Ctrl-C or Ctrl-Z to kill the programs.
Explanation
Let’s delve into the code now. We must have got some idea looking at the comments, still, lets try to analyze a few critical lines.
The server code has the lines:
ServerSocket ss = new ServerSocket(port);
Socket socket = ss.accept();
A ServerSocket class is slightly different from the Socket class. The Socket class is exactly what we think.. well, it represents a socket. The ServerSocket class is mainly for allowing a program to listen for connections from clients. we create it by assigning it a port number on which it can work on. Once created, we need to call its accept() method. This method will make the program listen on the given port for connections. It hangs there until it gets a client. Once a client gets in touch, it creates a normal Socket object, and hands it over to us so that we can start doing all the socket operations we want. Note that, this Socket object returned by accept() method, represents the other end of the connection. After all, if we want to send some data to the client, we can’t write it to our own socket!
Next is the Socket class. We create the Socket object by passing an IP address and a port number. Java gives us the InetAddress class to represent an IP address, so we better go their way and use it. To create an InetAddress object that represents an IP address, we can use this method –
InetAddress ipAddress = InetAddress.getByName(address);
Note that in our program, we have 127.0.0.1 for the address. This address is a special address called loopback address. Don’t panic, it is just an address that represents the local computer. If we intend to run the client and server on different machines, use the correct IP address of the server.
Once the InetAddress is created, we create the Socket,
Socket socket = new Socket(ipAddress, serverPort);
Once we have the Socket object, we can get the input and output streams of the socket. The input stream will let us read from the socket and the output stream lets us write to the socket.
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
The below lines are just for converting the above to different types of streams. So that it becomes easy for us to deal with String objects. This has got nothing to do with networking.
DataInputStream in = new DataInputStream(sin);
DataOutputStream out = new DataOutputStream(sout);
The rest is easy. Because it just deals with the stream objects we created, and not with sockets. We can use our favorite stream, call our favorite methods, and somehow ensure the data reaches the other end. Read up on streams if we are not comfortable.
Summary
Writing network applications in Java is extremely easy. Java takes away much of the implementation details which are operating system specific. There’s no need to worry about hostname lookup, size of IP address structures, or pointers like in C/C++. The java.net package provides all this functionality for us, and provides a simple way to write networking clients. However, when writing Java applets (as opposed to applications), remember that the browser may impose security restrictions – many applets can only communicate with the host from which they were downloaded from.