CHAPTER 15

Networking

What is networking? Is it connecting more than one machine? Networking is about communication between two processes which may be on different machines.

15.1 NETWORKING CONCEPTS

What is required for networking? There are various things which are required to achieve communication between two processes across machines. These functionalities have been divided into various layers. We have the OSI standard, which has specified seven layers for the communication between two processes across machines.

15.1.1 Various Layers/Protocol in Communication

Most commonly on the Internet the TCP and UDP-based services are available, and here we discuss these from the point of view of these IP-based protocols, which are supported in the Java API. So, here we divide these functionalities into five layers.

15.1.1.1 Physical Layer

The first requirement for networking would be related to the hardware required for connecting the machines. This includes the various connectors, cables, hub, NIC, etc. This layer is responsible for providing the medium on which the communication can take place. This is about physical connectivity between the machines. The medium used could be cables or perhaps wireless also; anything which is capable of carrying signals. The hardware includes a Network Interface Card (NIC). What is the role of the NIC?

15.1.1.2 Datalink Layer

The main role of the NIC is to convert the signals which are available from the medium connecting the machines into digital data and data to signals which can be transmitted on the media connecting the two communicating machines. The code for converting data to signals and signals to data is in the ROM of NIC. So this layer mostly exists in the firmware. There would be different kinds of NIC according to the medium over which the connection has been provided in the physical layer.

For the purpose of communication, some identification is required between the communicators. Each NIC has a unique 48-bit MAC-address by which it is identified. This MAC address is divided into two parts. Initial 24 bits of the MAC address identify the manufacturer and the last 24 bits are the serial number assigned by the manufacturer, i.e. by looking at the first 3 bytes (24 bits), we can identify the manufacturer of the NIC, whether it is manufactured by D-Link, Novell, Intel, etc. and the next 3 bytes are uniquely assigned by the manufacturer.

Now the communication is required between processes which are running on different machines. The identification of machine participating in communication cannot be dependent on the identity of the NIC used by the machine.

15.1.1.3 Network Layer — IP Protocol

The network layer identifies each machine which can participate in networked communication. We have a logical set of machines among the machines which have been physically connected using the lower layers. A logical set of machines which are physically connected can form a network. Communication can take place between processes of machines which are part of the same network. A single machine can also assume multiple identities, which may be in different networks. Such machines help in achieving communication between different networks. Such machines’ main role is to enable communication between machines in different networks. These machines are referred to as routers. When machines want to communicate across a network, then it would be done using the router, which is available in its own network. Thus, communication between machines in different networks requires a router.

There are various protocols available for network layer. The most common protocol being the IP protocol. In IP protocol each machine which is participating in the network is assigned an IP address. This IP address has to be unique within the network. There are various versions of the IP protocol, more commonly it is IPv4 protocol which is used. In IPv4 the size of IP-address is 32 bits. The 32-bit IP-address is denoted in decimal-dot notation by writing the values of each byte separated by ‘.’ e.g. 192.168.88.1 is used for denoting the 32 bits 11000000 10101000 01010000 00000001 of an IP-address. In case of IPv6, 128 bits are used for the IP address. There is no IPv5 protocol.

The IP address assigned to a machine has two parts. The most significant bits are used to identify the network and the rest of the least significant bits are used to identify the host within the network. The logical set of machines which form a network have the same network bits, and each machine within the network will have unique host bits. The number of bits to be used for network is configurable and is normally decided by a network administrator. This is decided by considering, whether there is requirement of more number of small networks or less number of larger networks. The number of bits used for the network is specified by using a subnet-mask, e.g. in case of IPv4, if we want to specify that 10 bits are to be used for network. Then the subnet-mask would be specified as 255.192.0.0. Here, the most significant 10 bits is 1 and the rest all are 0. The initial number of 1s in the subnet-mask specifies the number of bits to be used for the network. Within a network there are two host addresses which are reserved and cannot be assigned to any host within the network. These are the network address and the broadcast address for all the machines within the network. The network address is the address where the host bits are all zeros and the broadcast address is the address where host bits are all ones.

So if we consider a machine having an IP address of 192.168.88.1 and having a subnet-mask as 255.192.0.0, then its network address would be 192.128.0.0 and the broadcast address will be 192.191.255.255. All IP addresses in the range between 192.128.0.1 and 192.191.255.254 are valid IP addresses for machines within that network.

The purpose of networking is to have communication between two processes. For this, there must be a mechanism for the processes on a machine to be able to exchange bytes.

15.1.1.4 Transport Layer — TCP and UDP Protocols

The transport layer achieves an exchange of bytes between the two processes. There are two common protocols which are used at the transport layer — TCP and UDP. Both the TCP and the UDP use a set of 16-bit port numbers, which are used by processes interested in communicating over the network. The set of port numbers are separately maintained for TCP and UDP.

TCP and UDP: The TCP protocol is comparable to the telephonic communication and the UDP protocol is comparable to the postal or message-based communication. The TCP like the telephonic communication is a connection-based protocol, whereas UDP is a connectionless protocol. The TCP is a stream-based protocol, whereas UDP is a packet-based protocol. In case of UDP the sender prepares a packet containing bytes for the receiver and sends the packet. In case of TCP there is a stream available on which bytes can be written to or read from.

Any telephone service provider provides several services. These services are available on standard phone numbers. For example, we use 108 for emergency services. Similarly, on a host OS, there are several standard services available on standard port numbers. There are two kinds of processes, one is a server process and the other is a client process. Server processes are like the services, they are the ones which provide some kind of service. They do not initiate a connection, they listen for some client to connect. Just like the directory inquiry service is going to listen for someone to connect and is not the initiater of the connection. It waits for some client to connect, and then gives the service which it is meant for.

For a process to start using any of the above protocols, it has to obtain a port number (which is a resource with the OS). The port number is a 16-bit value. The port numbers are in the range of 1 – 65535. Port number 0 is reserved.

Communication normally takes place between server and client processes. The server processes are the ones which listen for a connection from the client. It would not initiate a connection. So server processes, whenever they start, first obtain a specific port number from the OS and start listening for connection on that port, e.g. WebServers by default would obtain port number 80 and start listening for connections on that port. Two different processes cannot use the same port number. Normally the servers would start on port numbers between 1 and 1024. The OS does not assign these port numbers to the client processes.

When a client wants to communicate with a server, the client must know the IP address and the port number of the server. It would have to first obtain a port number from the OS. It does not request a specific port number. The OS allocates any of the available port number to the client. Then the client can make a connection with the server using whatever port number is assigned to it.

The UDP protocol is like the postal communication, where the server process must first obtain the UDP port number on which it would like to receive the packets. The client process would need to know the UDP port number of the server to which it wants to communicate. The client process would also need to acquire some port number, and then it would prepare a UDP packet and send it to the server using its IP address and the UDP port number. The server on receiving the packet can examine the content of the packet and also the IP address and the UDP port number of the sender, after which the server would normally send a response back to the client using the IP address and the port number of the client.

15.1.1.5 Application Layer Protocols

The transport layer provides a means for sending and receiving bytes between two processes, but that is not sufficient for communication between processes. The communication between two processes would require that the bytes of data exchanged between the two processes can have a meaning for the two processes. The bytes of data are according to some format and convey some meaningful data which may be interpreted and used by the receiver. There has to be a pre-decided format according to which these bytes would be exchanged. The application layer is the layer which defines some kinds of rules and formats for the bytes being exchanged between the two processes. There are many protocols at the application layer which use TCP or UDP at the transport layer. The most common protocols are the HTTP, SMTP, POP3, FTP, DNS, tFTP, etc. Here DNS and tFTP protocols use UDP for communication between the client and the server, whereas all the other protocols mentioned use TCP. The HTTP protocol is the most commonly used protocol for communication between the browser and the server. The default port used by the HTTP protocol is 80. The SMTP and POP3 are used for mailing purpose. The SMTP having a default port number of 25 is used for sending mails, and the POP3 having a default port number of 110 is used for examining and getting the messages in a mail box. The FTP having a default port number of 21 (for commands) is used for sending and receiving files accross the network.

15.2 JAVA API FOR NETWORKING

The java.net package contains most of the core classes required to create network-based applications in Java. These classes include the classes like the InetAddress, Socket, ServerSocket, URL, URLConnection, DatagramSocket and DatagramPacket. The last two classes are used for communication using the UDP protocol. The ServerSocket and the Socket classes are used for client–server communication using the TCP protocol. The InetAddress class is used for representing the IP address and the host name of a machine. The URL is used to represent a URL for some of the standard protocols like the HTTP and the FTP. The URLConnection is used for carrying communication with standard servers like the HTTP server and the FTP server.

15.2.1 InetAddress Class

An instance of the InetAddress class encapsulates information about an IP address and its host name. The class does not provide any public constructor to create any instance of this class. Instead, we use the public and static factory methods to get instances of this class. These public and static methods are also useful for resolving the IP address from name or vice versa. This resolving of IP address from names or host name from an IP address may also use the Name Services like DNS, which is configured on the native machine, for resolution. The common methods of the InetAddress class are given in Listing 15.1.

 

Listing 15.1. Methods of InetAddress class

    1     public static InetAddress getByName(String name)
 2     public static InetAddress getByAddress(byte[] address)
 3     public static InetAddress getByAddress(String host, byte[] address)
 4     public static InetAddress getLocalHost()
 5     public static InetAddress[] getAllByName()
 6     public byte[] getAddress()
 7     public String getCanonicalHostName()
 8     public String getHostAddress()
 9     public String getHostName()

The getByName() method may be used to resolve an IP address from the name, e.g. we may use the method as given below to resolve the IP address of any host:

InetAddress add = InetAddress.getAddressByName("mail.yahoo.com");

System.out.println(Arrays.toString(add.getAddress()));

The getByAddress() method may be used to resolve the name of the host by using its IP address, e.g. we may use the method as given below to resolve the name by using its IP address:

byte[] ipaddress = byte[]202, 164, 27, 54;

InetAddress add = InetAddress.getByAddress(ipaddress);

The getByAddress() with two parameters creates an instance of the InetAddress with the specified host name and IP address. This method does not carry out any check with the name server and does not attempt any kind of name or address resolution. The getLocalHost() creates an instance of the InetAddress class corresponding to the local host. The last static method getAllbyName() returns an array containing the InetAddress instances corresponding to all the IP addresses configured on the specified host name.

The instance of InetAddress encapsulates the values of the IP address and the name of the host. The methods getAddress() and getHostName() return the encapsulated values of the IP address and the host name. The method getCanonicalHostName() returns the full domain name of the host whose IP address it represents. The getHostAddress() returns in String from the IP address value. For IPv4 this would be the four byte values in decimal format separated by dots.

15.2.2 Using Socket and ServerSocket for Client–Server Communication

In the java.net package the class ServerSocket is used by a server process to reserve a TCP port for listening. We also have a class called Socket. The instance of Socket class represents one end of a TCP connection. An instance of Socket is connected to another instance of Socket using the streams. The instance of Socket encapsulates two streams which are used for exchanging bytes between the two processes.

The communication always takes place between two Socket instances. The ServerSocket created by the server is used for accepting connections from the client. Whenever a client connects, a Socket would be created on the server. The client connects to a server by calling the constructor of the Socket class.

Sequence of steps:

  1. The server process reserves a port for itself. This is done by calling the constructor of the ServerSocket class.
  2. The server process starts listening for connection from a client. This is done by calling the accept() method on the ServerSocket. accept() is a blocking method. It would wait till a client connects. As soon as the client connects, the accept() method returns an instance of Socket, which is a connection to the client who has connected.
  3. The client connects to the server by calling the constructor of the Socket class, where he specifies the host and the port number of the server.

Serverside:

    1     ServerSocket ss = new ServerSocket(portno);
 2     Socket socket = ss.accept() // the server is waiting

Clientside:

    1     Socket socket = new Socket(ipaddress, portno)

When a client successfully calls the constructor of the Socket class, then two connected Socket instances are created simultaneously. One on the client machine, which, is because of the constructor and the other on the server machine, which is created by the accept() method on the ServerSocket instance. These two Socket instances are connected by streams. A connected Socket instance has two streams, an InputStream and an OutputStream (Figure 15.1). The InputStream of a Socket reads the data which are written to the connected socket’s OutputStream.

Figure 15.1 Sockets connected using streams

Figure 15.1 Sockets connected using streams

Step 1 would fail if the port number specified in the constructor is already being used by some other process. Step 3 would fail if there is no server listening on the specified port number and IP address.

Some of the common methods of the Socket class are given in Listing 15.2.

 

Listing 15.2. Methods of the Socket class

    1     public InputStream  getInputStream()
 2     public OutputStream getOutputStream()
 3     public InetAddress  getHost()
 4     public int          getPort()
 5     public InetAddress  getLocalHost()
 6     public int          getLocalPort()

The getInputStream() and the getOutputStream() methods are used to obtain the InputStream and the OutputStream of the Socket instance, which are connected to the remote Socket. These are used for sending to and receiving bytes from the connected Socket.

Let us now try to create a simple server, which can be used for accepting connections and serving the clients. The server may simply read the input coming from the client, display it locally on the server’s standard output and then send the received input back to the client. Let us call this server as an EchoServer. One such possible implementation is given in Listing 15.3.

 

Listing 15.3. A simple TCP-based EchoServer implementation

    1 package com.classofjava.net;
 2
 3 import java.io.*;
 4 import java.net.*;
 5
 6 public class EchoServer {
 7     private ServerSocket board;
 8     private Socket phone;
 9     private InputStream receiver;
 10    private OutputStream transmitter;
 11    private boolean connected;
 12
 13    public EchoServer(int portno) throws IOException {
 14        board = new ServerSocket(portno);
 15    }
 16
 17    public void getNextConnection() throws IOException {
 18        phone = board.accept();
 19        receiver = phone.getInputStream();
 20        transmitter = phone.getOutputStream();
 21        connected = true;
 22    }
 23
 24    public void serviceConnection() throws IOException {
 25        if (!connected) {
 26             throw new IOException("Not connected");
 27        }
 28        int input = receiver.read();
 29        while (input != -1) {
 30             System.out.print((char)input);
 31             transmitter.write(input);
 32             input = receiver.read();
 33        }
 34        transmitter.close();
 35        receiver.close();
 36        phone.close();
 37        connected = false;
 38    }
 39
 40    public static void main(String[] args) throws IOException {
 41        String portNumberString = args[0];
 42        int portNumber = Integer.parseInt(portNumberString);
 43        EchoServer server = new EchoServer(portNumber);
 44        while (true) {
 45             server.getNextConnection();
 46             server.serviceConnection();
 47        }
 48    }
 49 }

In the above code, the constructor for the EchoServer simply reserves a port. The method getNextConnection() waits for next client to connect and as soon as some client connects, it would initialize the receiver and the transmitter variables from the InputStream and the OutputStream of the socket connection. It also sets the connected flag. The serviceConnection() method checks if EchoServer is currently connected; if it is connected, then it services the connection by reading bytes from the receiver, displaying them locally on the standard output of the server and then writing the byte back to the client by writing to the transmitter. Once the client disconnects, the server would close the streams and the socket, and would finish the serviceConnection() method.

The main() method has an infinite loop where it again calls the getNextConnection() method to wait for the next client to connect. Once the client connects, it would again service the client. This way the server is able to service multiple clients. However, the server is only able to service one client connection at one time, i.e. while the first client is connected and is being serviced, and another client tries to connect to the server, then the above code would not be able to handle simultaneous connections to the clients. In order to be able to service multiple clients simultaneously, we would require multi-threading, which is explained in Chapter 16. We will develop a multi-threaded version of the EchoServer when we look at multi-threading. The multi-threaded version of EchoServer is given in Listings 16.1 and 16.2.

To test the above code, we can first compile the above code and then execute the main() by passing a command line argument for some port number. On Linux and UNIX machines the port numbers from 1 to 1023 are privileged ports (would require root permission), so we may use a port number greater than 1023. Once we start the server application as given above, we can then connect to the server by using the telnet client. We will initially use the telnet as a client for the server application, and later we will try to write our own general purpose TCP client which will be able to connect to any TCP server. The TCP client would require multiple threads, and we would develop it when we look at multi-threading.

The Socket and ServerSocket are classes which enable a Java application to use the TCP protocol for sending and receiving bytes between two processes. The bytes are sent and received over the stream instances available from the connected Socket instances.

EXERCISE 15.1 Update the BankServer class of Exercise 14.4 so that it can accept connections on a TCP port and accept commands from the connection rather directly from the keyboard, and similarly gives response back over the connection rather on the standard output. Overload the constructor of BankServer to accept the port number as the parameter.

The BankServer class updated for Exercise 15.1 is given in Listing 15.4.

 

Listing 15.4. BankServer.java

    1 package bank;
 2
 3 import java.util.*;
 4 import java.util.regex.*;
 5
 6 public class BankServer {
 7     private Bank bank;
 8     private Scanner commandScanner;
 9     private ServerSocket ss;
 10    private PrintWriter responseWriter;
 11    private static Matcher commandMatcher = Command.QUIT.getSyntax
          ().matcher("something");
 12
 13    public enum Command {
 14        OPEN("([Oo][Pp][Ee][Nn])\s+([SsCc])\s+(\w+)\s+(\d
                +(\.(\d)1,2)?)"),
 15        DEPOSIT("([Dd][Ee][Pp][Oo][Ss][Ii][Tt])\s+(\d+)\s+(\d
                +(\.(\d)1,2)?)"),
 16        WITHDRAW("([Ww][Ii][Tt][Hh][Dd][Rr][Aa][Ww])\s+(\d+)\s
                +(\d+(\.(\d)1,2)?)"),
 17        DISPLAY("([Dd][Ii][Ss][Pp][Ll][Aa][Yy])\s+(\d+)"),
 18        PASSBOOK("([Pp][Aa][Ss][Ss][Bb][Oo][Oo][Kk])\s+(\d+)"),
 19        LIST("([Ll][Ii][Ss][Tt])"),
 20        CLOSE("([Cc][Ll][Oo][Ss][Ee])\s+(\d+)"),
 21        SAVE("[Ss][Aa][Vv][Ee]"),
 22        QUIT("[Qq][Uu][Ii][Tt]"),
 23        ;
 24        private Pattern syntax;
 25        private Command(String pattern) {
 26            this.syntax = Pattern.compile(pattern);
 27        }
 28        public Pattern getSyntax() {
 29            return syntax;
 30        }
 31        public static Command getMatchingCommand(String input) {
 32            commandMatcher.reset(input);
 33            Command[] allCommands = Command.values();
 34            for (Command command : allCommands) {
 35                if (commandMatcher.usePattern(command.getSyntax()).
                      matches()) {
 36                    return command;
 37                }
 38            }
 39            return null;
 40        }
 41    }
 42    public BankServer(int portNo, Bank bank) throws IOException {
 43        this.bank = bank;
 44        ss = new ServerSocket(portNo);
 45    }
 46    public void getAConnection() {
 47        Socket s = ss.accept();
 48        commandScanner = new Scanner(s.getInputStream());
 49        responseWriter = new PrintWriter(s.getOutputStream(), true);
 50    }
 51    public void service() throws NoSuchAccountException,
           NegativeAmountException, IOException {
 52        String input = commandScanner.nextLine();
 53        Command command = Command.getMatchingCommand(input);
 54        while (command != Command.QUIT) {
 55            int acno = 0;
 56            double amt = 0;
 57            char type = ’s’;
 58            String name = "";
 59
 60            if (command != null) {
 61                switch(command) {
 62                    case OPEN:
 63                        type = commandMatcher.group(2).charAt(0);
 64                        name = commandMatcher.group(3);
 65                        amt = Double.parseDouble(commandMatcher.
                              group(4));
 66                        switch(type) {
 67                           case ’S’:
 68                           case ’s’:
 69                               acno = bank.openSavingsAccount(name,
                                      amt);
 70                               break;
 71                           case ’C’:
 72                           case ’c’:
 73                               acno = bank.openCurrentAccount(name,
                                      amt);
 74                         }
 75                         bank.display(acno, responseWriter);
 76                         break;
 77                     case DEPOSIT:
 78                         acno = Integer.parseInt(commandMatcher.
                                group(2));
 79                         amt = Double.parseDouble(commandMatcher.
                                group(3));
 80                         bank.deposit(acno, amt);
 81                         bank.display(acno, responseWriter);
 82                         break;
 83                     case WITHDRAW:
 84                         acno = Integer.parseInt(commandMatcher.
                                group(2));
 85                         amt = Double.parseDouble(commandMatcher.
                                group(3));
 86                         if (bank.withdraw(acno, amt)) {
 87                             bank.display(acno, responseWriter);
 88                         } else {
 89                             responseWriter.println("Warning:
                                    Insufficient balance");
 90                         }
 91                         break;
 92                     case DISPLAY:
 93                         acno = Integer.parseInt(commandMatcher.
                                group(2));
 94                         bank.display(acno, responseWriter);
 95                         break;
 96                     case PASSBOOK:
 97                         acno = Integer.parseInt(commandMatcher.
                                group(2));
 98                         bank.printPassbook(acno, responseWriter);
 99                         break;
 100                     case LIST:
 101                         bank.listAccounts(reseponseWriter);
 102                         break;
 103                     case CLOSE:
 104                         acno = Integer.parseInt(commandMatcher.
                               group(2));
 105                         amt = bank.close(acno);
 106                         responseWriter.println(amt+" is the closing
                                balance");
 107                         break;
 108                     case SAVE:
 109                         bank.save();
 110                         responseWriter.println("Bank Object saved");
 111                         break;
 112                 }
 113             } else {
 114                 responseWriter.println("Invalid command");
 115             }
 116             input = commandScanner.nextLine();
 117             command = Command.getMatchingCommand(input);
 118        }
 119    }
 120    public static void main(String[] args) throws Exception {
 121        int portNo = Integer.parseInt(args[0]);
 122        String bankName = args[1];
 123        String bankFileName = bankName + ".bank";
 124        File bankFile = new File(bankFileName);
 125        Bank bank = null;
 126        if (bankFile.exists()) {
 127            bank = Bank.load(bankName);
 128        } else {
 129            int bankCode = Integer.parseInt(args[2]);
 130        }
 131        bank = new Bank(bankName, bankCode);
 132        BankServer server = new BankServer(portNo, bank);
 133        while (true) {
 134            server.getAConnection();
 135            server.service();
 136        }
 137    }
 138 }

15.2.3 URL

What is a URL? URL is an acronym for Uniform Resource Locator. The various kinds of standard servers service requests from clients when the client identifies the resource, which it would like to use, in the form of a URL. These URLs typically specify various information like the protocol, user information, path, file, etc.

The URL class in java.net package encapsulates the information within a URL specification. What are the different parts of a URL? Let us consider a typical URL:

http://forum.java.sun.com/forum.jspa?forumID=24

<protocol>://<host>/<file>?<querystring>

Another form of a URL could be

<protocol>://<host>:<port>/<file>?<querystring>

<protocol>://<userinfo>@<host>:<port>/<file>?<querystring>

<userinfo> is user-name:password

When we specify both the host and the port number, it would be able to identify the server with which it has to connect. If the port number is not specified, then for the various protocols, there is a default value of the port number, and in such a case the port number will be decided by the protocol.

So a URL has protocol, host, port, file, querystring or reference. A reference is mentioned as # followed by a reference within the file.

An instance of URL could be used to parse all the information in a URL specification string. We can create an instance of URL by calling the constructor with String as parameter. The common constructors for the URL class are given in Listing 15.5.

 

Listing 15.5. Constructors of URL

    1     public URL(String spec)
 2     public URL(URL context, String spec)
 3     public URL(String protocol, String host, String file)
 4     public URL(String protocol, String host, int port, String file)

The first two constructors take a URL specification as a parameter. These constructors parse the specification and extract the various information available from the specification. The other constructors take the various information in a URL as a parameter and create the instance with the specified values.

Some of the common methods for the URL class are given in Listing 15.6.

 

Listing 15.6. Methods of a URL class

    1     public String getProtocol()
 2     public String getHost()
 3     public int    getPort()
 4     public String getFile()
 5     public String getQuery()
 6     public String getPath()
 7     public String getRef()    // reference within the html file
 8     public String getUserInfo()
 9     public String getAuthority()

One of the most common protocols for which a URL is specified is the HTTP (Hyper Text Transfer Protocol). HTTP is a request–response-based protocol used for content delivery, mostly from the server to the client. The client would send a request, and the server would send a response back to the client. The HTTP response contains various parts. The response could broadly be divided into two parts: The initial lines in the response are the headers. The headers are terminated by a blank line and then is the second part which is the content. The content could be of different types. It may be text/plain, text/html, image/gif, etc. The information about the content type would be available from the headers. The header has a lot of other information such as content length, character set used in text content, etc. The content is supposed to be rendered by the client. When we want to get content in a Java application then we can use the URL and the URLConnection classes.

Just by creating an instance of URL, there is no connection request which is sent to the server. When we use one of the getContent() or the openStream() methods, such a connection is made to the server to fetch the response. For fetching the response, an instance of URLConnection is used by the URL class. When we call the openStream() method on the URL instance, then the request will be sent to the server based on the specifications in the URL like query, ref, file, path userinfo, and then it would receive the response. The InputStream returned by the openStream() method allows us to read the content received in the response to the HTTP request.

    1     public InputStream openStream()

Another method for getting the content part is:

    1     public Object getContent(),

This can be used only for standard content types like image, e.g. in case the content type is image/gif, then using getContent() method instead of the openStream() method would return an instance of java.awt.Image. It downloads the content of the image file and creates an instance of Image and returns the Image instance. Instead of the openStream() method, we can use the URLConnection class to get the content for the given URL.

15.2.4 URLConnection

The URLConnection class is an abstract class. The openConnection() method on the instance of URL returns the instance of the appropriate sub-class of URLConnection based on the protocol specified in the URL instance. HTTP is the most commonly used protocol, and there is a sub-class of URLConnection called HttpURLConnection.

    1     public URLConnection openConnection()

An instance of URLConnection maintains two areas. One area is for setting up the information which is to be sent as part of a request and the other area is the information which is part of the response received from the server. It has methods for setting up the information in the request, which can be sent when the connect() method is invoked on it. The URLConnection instance is initially in the request setup state. The invocation of connect() method puts it into the connected state. Once the URLConnection is in the connected state, then the methods for setting up the request would throw an exception. All methods which require information from the response area will work in the connected state only. In case a method which requries information from the response area is used in the request setup state, then the connection would be made to the server and the state would be changed to the connected state, e.g. we have a method called getResponseCode() (method in HttpURLConnection), which is a method that gets the status code in the response. If this method is invoked while the URLConnection is in the request setup state, then the connection would be made to the server to obtain the response and the method would return the value of the response code in the response and change the state to connected. If the URLConnection was already in the connected state, then there would be no new connection made to the server, only the value of the response code which was obtained earlier would be returned.

Some of the commonly used methods which are used in the request setup state are given in Listing 15.7.

 

Listing 15.7. Methods of URLConnection which can be used in the setup state

    1     public void addRequestProperty(String key, String value)
 2     public void setRequestProperty(String key, String value)
 3     public void setConnectTimeout(int timeout)
 4     public OutputStream getOutputStream()
 5     public void setRequestMethod(String method) // method in
           HttpURLConnection.

Some of the commonly used methods which are used in the connected state are given in Listing 15.8.

 

Listing 15.8. Methods of URLConnection which can be used in the connected state

    1     public int getResponseCode() // method in HttpURLConnection
 2     public InputStream getInputStream()
 3     public Object getContent()
 4     public long getLastModified()
 5     public Map<String, List<String>> getHeaderFields()
 6     public String getHeaderField(String name)
 7     public String getHeaderField(int fieldNumber)
 8     public long getDate()
 9     public getExpiration()

The getInputStream() returns an InputStream which allows us to read the content in the response. The various getHeader() methods allow one to read the headers from the response. The response of the HTTP would have some standard headers like:

Content-length: nnnnn // nnnnn is some numeric value

Content-type: text/html

Server: servername

These headers are available as key–value pairs. The URLConnection class has methods for getting the values of some of the standard headers like:

    1     public String getContentType()
 2     public int    getContentLength()
 3     public String getContentEncoding()

15.2.5 DatagramSocket and DatagramPacket for UDP-based Communication

The DatagramSocket and DatagramPacket classes are used for sending and receiving UDP packets. The DatagramPacket represents the datagram packet which may be sent or received using an instance of DatagramSocket.

For communication using the UDP protocol between a client and a server, the server must first create an instance of DatagramSocket by specifying the UDP port number on which it would like to receive the datagram packets. It would then have to create an instance of DatagramPacket with an appropriate buffer size and then wait for someone to send the datagram packet. It would use the receive() method on the instance of DatagramSocket, which is a blocking method. The client (sender) would also need to create an instance of DatagramSocket (here the DatagramSocket may be created without specifying any port number), create an instance of the DatagramPacket in which it would set up the data buffer with the data it wants to send. The destination host and port number of the server’s DatagramSocket would need to be set up on the instance of DatagramPacket and then the DatagramSocket’s send() method is used to send the DatagramPacket. Let us look at the DatagramPacket first.

An instance of DatagramPacket encapsulates a data buffer in the form of a byte array, an InetAddress instance about the remote host and the remote port number. There are different types of constructors of the DatagramPacket used to create them for the purpose of sending the datagrams or receiving datagrams. Some of the constructors of the DatagramPacket are given in Listing 15.9.

 

Listing 15.9. Constructors of DatagramPacket

    1     public DatagramPacket(byte[] buf, int length)
 2     public DatagramPacket(byte[] buf, int offset, int length)
 3     public DatagramPacket(byte[] buf, int length, InetAddress
           address, int port)
 4     public DatagramPacket(byte[] buf, int offset, int length,
           InetAddress address, int port)

The first two constructors are used for creating DatagramPacket meant for receiving the datagrams sent by the remote process, and the other constructors of DatagramPacket are used for creating DatagramPacket instances for sending the datagrams to the remote process. When preparing DatagramPacket for sending to the remote process, the destination host and UDP port number are specified in the constructor.

Some of the common methods of the DatagramPacket class are given in Listing 15.10.

 

Listing 15.10. Methods of DatagramPacket

    1     public byte[] getData()
 2     public void setData(byte[] buf)
 3     public void setData(byte[] buf, int offset, int length)
 4     public int getOffset()
 5     public int getLength()
 6     public void setLength(int length)
 7     public InetAddress getAddress()
 8     public void setAddress(InetAddress address)
 9     public int getPort()
 10    public void setPort(int port)

The getData() method is normally used for getting the data buffer in the datagram packet received from the remote process. The getOffset() and the getLength() methods are used to know the offset within the data buffer from where the datagram’s data begins and the length of the datagarm data. The setData() methods are used for setting up the data buffer in the DatagramPacket meant for sending to the remote process.

The getAddress() and getPort() methods are used for getting the host and port information from where the datagram has been received. The setAddress() and setPort() methods are used to set up the DatagramPacket’s destination before sending the DatagramPacket using some DatagramSocket instance.

The DatagramSocket instance is created by both client and server processes for sending and receiving DatagramPackets over the network. The common constructors of DatagramSocket are given in Listing 15.11.

 

Listing 15.11. Constructors of DatagramSocket

    1     public DatagramSocket()
 2     public DatagramSocket(int port)
 3     public DatagramSocket(int port, InetAddress iaddr)

The first constructor is normally used by the client. The DatagramSocket created using the first constructor would be bound to any available port. The other two constructors are normally used by a server, which would normally be receiving DatagramPacket first, and then may respond to the client from where the DatagramPacket is received. The other two constructors bind the DatagramSocket to a specific UDP port. If the specified UDP port is not available then an exception will be thrown.

The instance of DatagramSocket is used for sending and receiving DatagramPacket. The commonly used methods from the DatagramSocket are given in Listing 15.12.

 

Listing 15.12. Methods of DatagramSocket

    1     public void send(DatagramPacket p)
 2     public void receive(DatagramPacket p)
 3     public int getPort()
 4     public InetAddress getInetAddress()
 5     public int getLocalPort()
 6     public InetAddress getLocalAddress()
 7     public int getSoTimeout()
 8     public void setSoTimeout(int timeout)

The send() method is used to send a datagram over the network. When sending the datagram, the DatagramPacket instance which is passed as a parameter must have the address and port number set to the destination host and UDP port number.

When receiving a datagram using the receive() method, the DatagramPacket passed as a parameter must have sufficient buffer size. The getAddress() and getPort() on the Datagram instance will return the sender host and UDP port number details.

The receive() is a blocking method and it normally waits infinitely for the datagram to arrive. The setSoTimeout() method could be used to specify a timeout value for the receive() method. The positive value of the timeout specified in the setSoTimeout() method is the time in milliseconds for which the receive method blocks waiting for a new datagram to arrive. If the datagram does not arrive during the timeout duration, then the method throws a SocketTimeoutException. A timeout value of zero would indicate that receive() method should wait infinitely for the datagram packet to arrive. The getSoTimeout() method returns the current value of the timeout.

UDP is an asynchronous communication and the protocols based on UDP normally would require one to send and receive packets asynchronously, which would require multiple threads. Creating multiple application threads in Java is discussed in Chapter 16.

LESSONS LEARNED
  • The purpose of networking is to achieve communication between two processes, which may be on two different machines.
  • The process of communication between process is divied into serveral layers. Physical layer is about having a physical connectivity between using appropriate hardware. The datalink layer relates to the ability to convert between digital data and the signals available over the physical layer. The network layer is about a mechanism of identification of hosts, which would participate in the network; this is independent of the identity used at the datalink layer. Transport layer is about providing the mechanism for transportation of data between two processes which are on hosts identified in the network layer. The application layer is about utilizing the transport layer for exchange of data in a meaningful manner.
  • At the network layer, the most commonly used protocol is the IP protocol. There are various versions of the IP protocol. IPv4 is the most common. The identification of host using an IP protocol is done using an IP address. The size of IP address is 32 bits in IPv4 and 128 bits in IPv6.
  • At the transport layer, TCP and UDP are the most commonly used protocols. The TCP is a connection-oriented, stream-based, reliable protocol, whereas UDP is a connectionless, packed-based protocol. Both TCP and UDP use 16-bit port numbers.
  • An instance of InetAddress encapsulates host name and an IP address. The instance of InetAddress is created using various static methods available in the class. These static methods can use DNS query to resolve IP address from a name or vice versa.
  • For TCP connection, the ServerSocket in java.net is used by server processes to listen for connection from client processes; they use the accept() method to wait for the client connection. The client process makes the connection to a server by calling the constructor of the Socket class. Two connected Socket instances are created simultaneously, one for the server process, by using accept() method and one for the client process by using the constructor of Socket.
  • A connected Socket has an instance of InputStream and OutputStream. The InputStream is used to read bytes written on the connected socket’s OutputStream.
  • The instance of URL is used to represent resources available from servers using standard protocols. The information in an instance of URL includes protocol name, host name, port number, file, userinfo, etc.
  • The instances of sub-classes of URLConnection are used to enable a client process to make a request and receive a response from a standard server for which a URL exists. It is used to fetch the content available from the resource represented by a URL.
  • For UDP communication instances of DatagramSocket and DatagramPacket are used. UDP is a packet-based communication, in which packets of bytes are exchanged between the processes. The DatagramSocket is used by client and server processes to send and receive DatagramPacket containing the bytes of data being exchanged.
EXERCISES
  1. State which of the following are true or false:
    1. A single Socket instance can be used to connect to multiple servers.
    2. A call to accept() method on an instance of ServerSocket always blocks infinitely for a client to connect.
    3. The instance of ServerSocket can accept only one connection. To accept connection from a second client a new instance of ServerSocket would be required.
    4. The instance of InetAddress can resolve an IP address from a given domain name.
    5. UDP protocol is a stream-based protocol.
    6. TCP is a connection-oriented protocol.
  2. Fill in the blanks in the following:
    1. The size of IPv4 address is __________ bits.
    2. The size of MAC address is __________ bits.
    3. The two parameters required for connecting to a TCP server are __________ and __________.
    4. The ServerSocket class has a method __________ to set a timeout for its accept() method.
  3. Explain the steps for establishing a TCP connection between a client and a server using Socket and ServerSocket classes.
  4. State the differences between TCP and UDP.
  5. What are the different parts of a URL?
  6. If a timeout has been set on a ServerSocket with the setSoTimeout() method and the time expires with the accept() method, then
    1. The accept method returns null
    2. The accept method throws java.net.ServerSocketTimeoutException
    3. The accept method throws java.net.SocketTimeoutException
    4. None of the above
  7. What information is needed in order to form a Socket on the client side?
  8. Write a class in Java which accepts a socket connection from a client on port 13 and sends the IP address and portnumber of the client back to the client and then disconnects.
  9. Write a class in Java which connects to the above server and displays the data received from the server.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.255.87