Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

How can you establish a network connection using Java?


Nov00: Java Q&A

Mike is an independent consultant and developer in Victoria, BC. He can be reached at [email protected].


Java applets are small Java applications designed to run inside a web browser whenever it encounters a web page that contains an applet. Since Java applets can come from anywhere and be written by anyone, it only makes sense to restrict what an applet can do on your computer. These safety restrictions are built into the Java platform and referred to as the "security sandbox."

One of the consequences of this sandbox is that a Java applet cannot establish a network connection to a computer other than the web server hosting it. Unfortunately, it is often the case that a Java applet needs to make a network connection to a computer other than its hosting web server -- a database server, for example.

After encountering this problem a few times, I opted for a simple solution that I implemented both as a standalone Java application and as a Windows NT service using the framework described in a previous article (see "Java Q&A," DDJ, March 2000).

A Simple Solution

If a Java applet hosted on http://www .???.com/ needs to access a database on oracleserver.???.com/, it cannot do so because of the security restrictions that are placed on Java applets by the Java security model (the sandbox). The easiest solution is to create a server program that runs on the web server and listens for socket connections on a particular port. When the server program gets a socket connection, it establishes a second socket connection to the database server and then spins off two threads that effectively tie the input and output streams of the two sockets together.

The program I wrote to do this task I refer to as an "IP redirector" because it effectively redirects incoming socket connections to their desired host.

Java Service Revisited

In my previous article, I described a framework for writing Windows NT services in Java (javaservice). Since my IP redirector needs no user interface and, for the most part, runs in the background, it is a perfect candidate for implementation as a service. The javaservice framework I developed has gone through some substantial changes since it was first published. System properties are now added from a file called "javaservice.properties" that exists in the same directory as javaservice.exe. Also, all .jar files in the same directory as javaservice.exe automatically get added to the classpath. Javaservice also uses the new invocation API and is now compatible with Hotspot JVM. The newly enhanced version of javaservice will be available as a commercial product from http://hisitech.com/. In the meantime, the IPRedirectDaemon class can be invoked as a standalone with the following commands:

On Windows:

java -cp .\javaservice.jar;.\ipredirect.jar IPRedirectDaemon

On Unix:

java -cp ./javaservice.jar;./ipredirect.jar IPRedirectDaemon

The IPRedirectDaemon class

The Java class I wrote to do the socket redirection is IPRedirectDaemon. Because system properties are the easiest way to pass information to any Java service, IPRedirectDaemon was written to use system properties to determine which ports to listen on and which IP address/ port to redirect a given port to.

Example 1 shows a few IP address redirection properties. In this example, the IPRedirectDaemon listens on ports 2433 and 1521. If IPRedirectDaemon gets a connection on port 2433 (the accepted socket), it tries to establish a connection to port 1433 on sqlserver.???.com (the real socket). Once both sockets are connected successfully, they are tied together and the input from one socket becomes the output for the other. The Java class responsible for tying the two sockets together is SocketTie.

The SocketTie Class

The class that does the actual work of redirecting bytes from one socket connection to another is called "SocketTie.java" (Listing One) and is trivial.

The operation of SocketTie is pretty straightforward. Two Thread-derived inner classes (A_run and B_run) do the actual data pumping from one socket's InputStream to another socket's OutputStream. If either socket's InputStream or OutputStream throws an IOException, both streams are immediately closed in that thread.

The Java class that listens on a specific port and establishes remote connections when a socket is accepted is SocketListen. The constructor for SocketListen looks like the following:

public SocketListen(int listenport,InetAddress remoteAddress,int remoteport)

The listenport parameter is the port that the SocketListen class listens on; the remoteAddress and remoteport parameters are the IP address and port of the remote host to connect to when an incoming connection is established on listenport.

When the start() method of SocketListen (inherited from its base class Thread) is invoked, it creates a ServerSocket on listenport and begins accepting connections. When a connection is received, SocketListen attempts to establish another connection to remoteAddress/remoteport. If the second connection to remoteAddress is successful, a SocketTie object is created to tie the two sockets together.

Listing Two is the listen() method of the SocketListen class, while Listing Three shows the IPRedirectDaemon class.

To use the IP redirector on Windows NT, create a directory and unzip the ipredirect.zip file (available electronically; see "Resource Center," page 5) into that directory. One of the files in that directory will be a text file called "javaservice.properties" and will contain three lines as follows:

redirect1.listenport=1521

redirect1.address=oracleserver.???.com

redirect1.port=1521

Edit the javaservice.properties file using Notepad (or your favorite text editor, being sure to save as plain text) and change oracleserver.???.com to either the IP address or domain name of the host you want to redirect connections to. Change the listenport and port values to whatever values you like.

Open a Command Prompt window and change the current directory so that you are in the same directory as the one you unzipped the ipredirect.zip file into.

At the command prompt, enter: javaservice /install. You should see "Service 'Socket Redirector' installed" as the last line printed out. To Uninstall the socket redirector, simply type javaservice/remove.

Conclusion

The security model for Java applets prevents them from accessing network resources from any computer other than the hosting web server. By configuring and running the IP redirector on a web server, this limitation can be overcome easily. Because the IP redirector itself is written entirely in Java, it would be fairly trivial to adapt the IP redirector to run on any operating system with a Java run time available for it. I have already started an all-Java version of my javaservice framework and tested my services successfully on Linux.

DDJ

<H4><A NAME="l1">Listing One</H4>
import java.net.*;
import java.io.*;

public class SocketTie
{
InputStream isa,isb;
OutputStream osa,osb;

public SocketTie(Socket a,Socket b) throws IOException
  {
    init(a.getInputStream(),a.getOutputStream(),
      b.getInputStream(),b.getOutputStream());
  }
class A_run extends Thread
  {
    public void run()
      {
        readFromA();
      }
  }
class B_run extends Thread
  {
    public void run()
      {
        readFromB();
      }
  }
A_run arun;
B_run brun;
private void init(InputStream _isa,OutputStream _osa,
  InputStream _isb,OutputStream _osb)
  {
    isa=_isa; osa=_osa;
    isb=_isb; osb=_osb;
    arun=new A_run();
    brun=new B_run();
  }
public void start()
  {
    arun.start();
    brun.start();
  }
private void readFromA()
  {
    int abytes;
    byte[] buffer=new byte[1024];
    try
      {
        for (;;)
          {
            abytes=isa.read(buffer,0,1024);
            osb.write(buffer,0,abytes);
          }
      }
    catch(Exception ioe)
      {
        // underlying stream is closed
        try
          {
            osb.close();
          }
        catch(IOException ioe2)
          {
          }
        try
          {
            isa.close();
          }
        catch(IOException ioe3)
          {
          }
        return;
      }            
  }
private void readFromB()
  {
    int bbytes;
    byte[] buffer=new byte[1024];
    try
      {
        for (;;)
          {
            bbytes=isb.read(buffer,0,1024);
            osa.write(buffer,0,bbytes);
          }
      }
    catch(Exception ioe)
      {
        // underlying stream is closed
        try
          {
            osa.close();
          }
        catch(IOException ioe2)
          {
          }
        try
          {
            isb.close();
          }
        catch(IOException ioe3)
          {
          }

        return;
      }            
  }
}
<A HREF="#rl1">Back to Article</A>
<H4><A NAME="l2">Listing Two</H4>
package slickjava.net;

import java.net.*;
import java.io.*;
import slickjava.net.*;

public class SocketListen extends Thread
{
int listenport;
InetAddress remoteAddress;
int remoteport;

public SocketListen(int listenport,InetAddress remoteAddress,int remoteport)
  {
    this.listenport=listenport;
    this.remoteAddress=remoteAddress;
    this.remoteport=remoteport;
    System.out.println("listening on port "+listenport+
    ", redirecting to "+remoteAddress+","+remoteport);
    System.out.flush();
  }

public void run()
  {
    try
      {
        listen();
      }
    catch(IOException ioe)
      {
      }
  }

ServerSocket serversocket;
private void listen() throws IOException
  {
    serversocket=new ServerSocket(listenport);
    for(;;)
      {
        Socket acceptedsocket=serversocket.accept();
        Socket realsocket;
        try
          {
            realsocket=new Socket(remoteAddress,remoteport);
          }
        catch(IOException ioe2)
          {
            try
              {
                acceptedsocket.close();
              }
            catch(IOException ioe1)
              {
              }
            continue;
          }

        SocketTie tie=new SocketTie(acceptedsocket,realsocket);
        tie.start();
      }
  }

public void stopListening()
  {
    try
      {
        serversocket.close();
      }
    catch(IOException ioe)
      {
      }
  }

}
<A HREF="#rl2">Back to Article</A>
<H4><A NAME="l3">Listing Three</H4>
import java.util.*;
import java.net.*;
import java.io.*;
import com.slickjava.jdaemon.*;
import slickjava.net.SocketListen;

public class IPRedirectDaemon implements JDaemon
{

public IPRedirectDaemon()
  {
  }

public String getDaemonName()
  {
    return "IPRedirectDaemon2";
  }

public String getDisplayName()
  {
    return "Socket-Redirector";
  }

public String[] getDependentDaemons()
  {
    return null;
  }

public void parseCommandLine(String[] args)
  {
  }

Vector listenlist=new Vector();
private void load_socketlist() throws IOException
  {
    int i,listenport,remoteport;
    String key,value;
    InetAddress remotehost;

    for (i=1;i<100;i++)
      {
        key="redirect"+i+".listenport";
        value=System.getProperty(key);
        if (value==null) break;
        listenport=Integer.parseInt(value);
        key="redirect"+i+".address";
        value=System.getProperty(key);
        try
          {
            remotehost=InetAddress.getByName(value);
          }
        catch(UnknownHostException uhe)
          {
            System.out.println("can't resolve '"+value+"'");
            break;
          }
        key="redirect"+i+".port";
        value=System.getProperty(key,""+listenport);
        remoteport=Integer.parseInt(value);

        listenlist.addElement(new SocketListen(listenport,
remotehost,remoteport));
      }
  }

public boolean tryToStart(JDaemonController c)
  {
    int checkpoint=0;
    c.daemonStarting(checkpoint++, 4000);

    try
      {
        System.out.println("Loading socket list...");
        load_socketlist();
        System.out.println("Socket list loaded.");
      }
    catch(IOException ioe)
      {
        ioe.printStackTrace();
      }

    int i,n=listenlist.size();
    if (n==0)
      {
        System.err.println("No ports to redirect!!");
        return false;
      }


    for (i=0;i
    c.daemonStarted();
System.out.println("IPRedirectDaemon started.");
System.out.flush();
    return true;
  }

Object waitobject=new Object();
boolean running;
public void main()
  {
  	running=true;
		while(running)
  		{
		    synchronized(waitobject)
		      {
		        try
		          {
		            waitobject.wait(2000);
		          }
		        catch(InterruptedException ie)
		          {
		            ie.printStackTrace(System.err);
		          }
		      }
Date rightnow=new Date();
System.out.println("now="+rightnow.toString());
System.out.flush();
			}
  }

public boolean tryToStop(JDaemonController c)
  {
// first tell main it can exit
    System.out.println("IPRedirectDaemon: got signal to stop...");
    synchronized(waitobject)
      {
      	running=false;
        waitobject.notify();
      }

// now stop all of the socket listeners
    int i,n=listenlist.size();
    c.daemonStopping(0, 700);
    for (i=0;i
public boolean tryToPause(JDaemonController c)
  {
    return false;
  }

public boolean tryToContinue(JDaemonController c)
  {
    return false;
  }

public boolean tryToShutdown(JDaemonController c)
  {
    c.daemonNothingInteresting();
    return true;
  }

public boolean tryToGetStatus(JDaemonController c)
  {
    c.daemonNothingInteresting();
    return true;
  }

public static void main(String[] args)
	{
 		try
   		{
     		IPRedirectDaemon redirector=new IPRedirectDaemon();
    		redirector.launchStandalone();
    	}
		catch(Exception ex)
  		{
    		ex.printStackTrace(System.err);
      	System.err.flush();
      	System.exit(1);
    	}
 	}

private void launchStandalone() throws Exception
	{
 		File propsfile=new
File(System.getProperty("user.dir"),"javaservice.properties");
		System.out.println("adding "+propsfile+" to system properties");
  	Properties systemprops=System.getProperties();
   	FileInputStream fis=new FileInputStream(propsfile);
    systemprops.load(fis);
    fis.close();
		load_socketlist();
  	if (listenlist.size()==0)
   		throw new Exception("No ports to redirect!");
		Enumeration enum=listenlist.elements();
  	while(enum.hasMoreElements())
   		{
     		SocketListen sl=(SocketListen)enum.nextElement();
       	sl.start();
     	}
 	}
}
Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.