Using sockets and writing clients

Sockets

A socket is an abstract input-output device. A socket may correspond to a display, a keyboard, a printer or a data communications line. A socket is used by a program to input or output a stream of data.

A client socket can:

A client socket is normally used as follows:

  1. the new socket is created - using the Java Socket constructor
  2. the socket attempts to connect to a remote host
  3. once the connection has been established, the local machine and the remote machine send and receive data. The connection is full-duplex - data can be sent in both directions simultaneously.
  4. When the transmission is complete, one or both machines closes the connection.

 

In Java, the class Socket provides a way of using TCP sockets. (There are other types of socket, including datagram sockets.) The constructor method:

When a socket is created, either the host name is supplied or an INetAddress object.


Port Scanner

The following program attempts to connect to ports 1 to 256 of a specified host. It thus checks to see which ports are open for business.

It works by inputting a host name, then trying to create an InetAddress object. If this fails, the host is unknown and an error message is displayed. The program then tries to open ports 1 to 256 on the host. If a socket is created, there is assumed to be an open port and a message is displayed. If a socket cannot be opened on a port, an exception is raised and the port is ignored.

Psuedocode

input host name
check that host exists
for port = 1 to 256 do
    try to open socket to port
    if socket opens, display port number
    close socket
endfor

 

Java


import java.awt.*;  
import java.awt.event.*;
import java.net.*;
import java.io.*;

public class PortScanner extends Frame implements ActionListener {

    private TextField hostInput;
    private TextArea report;
    private Button button, exit;
 
    public static void main(String [] args) {
        PortScanner lfp = new PortScanner();
        lfp.makeGUI();
        lfp.setSize(300,400);
        lfp.setVisible(true);
    }
 
    public void makeGUI() {
        setLayout(new FlowLayout());
        hostInput = new TextField("holly.shu.ac.uk", 40);
        add(hostInput);
        button = new Button("Look for server ports on this host");
        add(button);
        report = new TextArea("", 0, 0,
                        TextArea.SCROLLBARS_VERTICAL_ONLY);
        add(report);
        button.addActionListener(this);
 
        exit = new Button("exit");
        add(exit);
        exit.addActionListener(this);
 
     }
 
     public void actionPerformed(ActionEvent event) {
        if (event.getSource() == exit)
            System.exit(0);

        report.setText("");
        String host = hostInput.getText();
        try {
            InetAddress theAddress = InetAddress.getByName(host);
 
            for (int i = 0; i < 256; i++) {
               try {
                   Socket socket = new Socket(host, i);
                   report.append("There is a server on port "
                                + i + "\n");
                   socket.close();
                }
                catch (IOException e)
                     {}// no server on this port
            }
          }
        catch (UnknownHostException e)
                     {report.setText("unknown host");}
     }
 }


Details about sockets

When a socket is created and a connection established, the server host name and port are specified. But there is also implicitly a client host port number involved, and this is part of the information encapsulated in a socket. This is chosen by the system at run time from available unused ports. The local port number is embedded in the IP packet along with the local host's IP address, so that the server can send the reply back to the right place. This port number can be accessed using getLocalPort, one of the Socket classes object methods (see the reference summary).

A socket is closed when:

Once a socket has been used to connect to a host, the program should close it. This is just good housekeeping.

The methods getInputStream and getOutputStream of the class Socket create objects of the classes InputStream and an OutputStream respectively. These can then be used to do stream input and output. It is usually best to create stream objects which provide better functionality.

Now the world is your oyster - you can write programs to open connections to any host on any port and engage in conversation according to one of the established protocols (or your own special protocol if you can write a server).


Example - the Daytime Protocol (RFC867)

As an example of writing a client, protocol daytime enables a client to obtain the date and time from port 13 of a host. The client merely opens the connection, and sends nothing. In response the server sends a line of text, giving the current date and time on the server. Here is the program:

Pseudocode

open a socket to port 13 on the desired computer
open an input stream
read the date and time from the stream
display
close the socket

 

Java

import java.awt.*;  
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;

public class GetRemoteTime extends Frame implements ActionListener {

    private TextField hostInput;
    private TextArea display;
    private Button button;
    private Button exit;

    public static void main(String [] args) {
        GetRemoteTime gr = new GetRemoteTime();
        gr.makeGUI();
        gr.setSize(300,400);
        gr.setVisible(true);
    }

    public void makeGUI() {
        setLayout(new FlowLayout());
        
        hostInput = new TextField(50);
        add(hostInput);
        button = new Button("get time from this host");
        add(button);
        display = new TextArea("", 0, 0,
                       TextArea.SCROLLBARS_VERTICAL_ONLY);
        add(display);
        button.addActionListener(this);

        exit = new Button("exit");
        add(exit);
        exit.addActionListener(this);
    }

    public void actionPerformed(ActionEvent event) {
        if (event.getSource() == exit)
            System.exit(0);

        String theTime;
        String host = hostInput.getText();

        try {
            Socket socket = new Socket(host, 13);
            BufferedReader input = new BufferedReader(new
                     InputStreamReader(socket.getInputStream()));
            theTime = input.readLine();
            display.append("The time at " + host + " is " + theTime);

            socket.close();
        }
         catch (UnknownHostException e)
            {display.setText("no such host"); }
         catch (IOException io)
            {display.setText(io.toString());}
    }
}

 

When you run this program, try entering localhost as the host name, to connect back to your own machine.


Errors and Exceptions

Several things can go wrong when a socket is created (connection sought) or used:

ConnectException - the connection is refused by the remote host. This may be because the host is busy or because there is no server on the port.

NoRouteToHostException - the connection has timed out.

Normally when a program reads from a socket, the call on read blocks (waits) until the data has been obtained. But if the remote host crashes, the program is left hanging. An alternative is to call setSoTimeout to set the maximum waiting time. When this time expires, an InterruptedException is thrown.


Inputting and outputting with sockets

A socket connection can be used in both directions at once.

For input, it is most convenient to create a BufferedReader as follows:

BufferedReader in = new BufferedReader(
    new InputStreamReader(
    socket.getInputStream()));

 

Thereafter, method readLine can be used to input a complete line as a string (minus the terminating character(s):

String string = in.readLine();

 

The end of the stream is signalled by a string equal to the null object. Thus a common pattern for input is:

String string;
while((string = in.readLine()) != null) {
    processLine();
}

For output to a stream, it is most convenient to create a PrintWriter object:

PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

and thereafter use method println to output a line (the line terminator is automatically added):

out.println("string");

Closing a socket closes all of the associated streams.


Summary of class Socket

class name

Socket

import

import java.net

 

description

example

constructors

 

 

public Socket(String host, int port)

creates a new Socket object corresponding to destination host and port

Socket socket = new Socket("www.shu.ac.uk", 80);

public Socket(InetAddress host, int port)

creates a new Socket object corresponding to the InetAddress object and port

Socket socket = new Socket(inetAddress, 80);

 

 

 

object methods

 

 

public InetAddress getInetAddress()

returns the InetAddress object corresponding to the host that is, or will be, connected to.

InetAddress iNE = socket.getInetAddress();

public int getPort()

returns the port number of the host to which the socket is, or will be, connected.

int port = socket.getPort();

public int getLocalPort()

returns the local port number.

int port = socket.getLocalPort();

public InputStream getInputStream()

returns an input stream that can be used by the program for input.

InputStream is = socket.getInputStream();

public OutputStream getOutputStream()

returns an output stream that can be used by the program for output.

OutputStream os = socket.getOutputStream();

public synchronized void close()

disconnects a socket. This also closes the streams associated with the socket.

socket.close();

public String toString()

returns a string representation of a socket with remote host name, remote IP address, remote port, local port.

String details = socket.toString()

public synchronized void setSoTimeout(int ms)

sets the time-out to the parameter value (in milliseconds)

socket. setSoTimeout(18000);

 

Summary of class BufferedReader

 

description

example

public String readLine()

inputs a string that is followed by a line terminator.

The string does not include the terminator.

in.readLine();

 

Summary of class PrintWriter

 

description

example

public void println(String)

outputs a string, followed by a line terminator.

out.println("output line");

 


Quiz - to clarify the understanding of ports and sockets.

What are the differences between ports and sockets?

For both sockets and ports. Answer these questions:

Exercises - to help clarify the meaning of a connection

1 Run the program PortScanner give above in the text. This interrogates ports 1 to 256 of a particular host to see if it possible to connect a socket to each of the ports. If so, a connection is established and it implies that a service is available on the port.

2 Enhance the program so that it displays which ports it has looked at and which port it is looking at.

 

3 Enhance the program so that it displays the local port number that has been used to connect to each remote port. (There will be a different local port number for every connection, chosen from those that are unused.)

 

Exercises using sockets

1 run the program getRemoteTime given in the text.

2 write a program that uses the echo protocol to communicate with a server, one line at a time. Use separate text areas for the text sent to the server and the text sent from the server. Use a text field to specify the host name. Use a button to close the connection. (See reference summary for protocol.)

 

3 write a program that uses the Finger protocol to get information about users of a host. (See reference summary for protocol.)

4 write a program that acts as a general-purpose telnet client. (See reference summary for protocol.)The program should provide:

 

5 Write a web browser that understands HTTP (See reference summary for protocol.), including any errors, but simply displays the raw HTML. The program should provide:

P.S. To get a feel of the size of this sort of software, note that Netscape Navigator is 1.5 million lines of source code.