I am using a servlet on a high-volume web server.

Peter Andrews

After further research I found that if I added a call to Thread.interrupt() after the specified timeout had elapsed this stopped the Socket.open. I monitored the number of socket connections using NetStat (addinga grep for the bad ip I was using for testing). Before I added the Thread.interrupt call the number of waiting socket connections (in the SYN_SENT state) could increase indefinitely until finally at ~700 threads my servlet engine blew up. With the call to interrupt the number of extand socket connections never exceeded thirty and performance remained good.
Here is my version of TimedSocket class. This originated from David Reilly's version but has been changed substantially. It uses the log4j package which is available from jakarta-apache. The log statements could also be removed easily enough. Needless to say this code is provided for informational purposes only, as is, with no warranty etc.:

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

/**
 * This class offers a timeout feature on socket connections.
 * A maximum length of time allowed for a connection can be
 * specified, along with a host and port.
 */
public class TimedSocket
{
	private static org.apache.log4j.Category log = org.apache.log4j.Category.getInstance(TimedSocket.class.getName());
	/**
	 * Attempts to connect to a service at the specified address
	 * and port, for a specified maximum amount of time.
	 *
	 *	@param	addr	Address of host
	 *	@param	port	Port of service
	 * @param	delay	Delay in milliseconds
	 */
	public static Socket getSocket(String host, int port, int delay)
	throws Exception
	{
		Socket socket = null;
		try
		{
			// Create a new socket thread, and start it running
			if (log.isDebugEnabled()) log.debug("TimedSocket:getSocket called with host='" + host + "',port='" + port + "', delay='" + delay + "'.");
			SocketThread st = new SocketThread(host, port );
			st.start();
			//wait for the SocketThread to finish or exceed its alloted time
			st.join(delay);
			if (st.isConnected())
			{
				// Yes ...  assign to socket variable, and break out of loop
				if (log.isDebugEnabled()) log.debug("TimedSocket:getSocket connected");
				socket = st.getSocket();
				return socket;
			}
			else if (st.isError())
			{
				// No connection could be established
				if (log.isDebugEnabled()) log.debug("TimedSocket:getSocket throwing exception '" + st.getException() + "'.");
				throw st.getException();
			}
			else // must have timed out
			{
				st.setDoneWaiting(new InterruptedIOException("Could not connect for " + delay + " milliseconds"));
				//make the thread trying to open the socket stop
				st.interrupt();
				if (log.isDebugEnabled()) log.debug("TimedSocket.getSocket successfully interrupted SocketThread.");
				throw st.getException();
			}	//end if socket open timed out
		}	//end try
		catch (Exception outerEx)
		{
			log.error("TimedSocket:getSocket caught an exception '" + outerEx.toString() + "'.");
			throw outerEx;
		}
	}

	// Inner class for establishing a socket thread
	// within another thread, to prevent blocking.
	static class SocketThread extends Thread
	{
		// Socket connection to remote host
		volatile private Socket m_connection = null;
		volatile private boolean doneWaiting = false;
		// Hostname to connect to
		private String m_host       = null;
		// Port number to connect to
		private int    m_port       = 0;
		// Exception in the event a connection error occurs
		private IOException m_exception = null;
		
		// Connect to the specified host and port number
		public SocketThread( String host, int port)
		{
			// Assign to member variables
			m_host = host;
			m_port = port;
		}
		
		public void setDoneWaiting(InterruptedIOException ioe)
		{
			doneWaiting = true;
			m_exception = ioe;
		}
		
		public void run()
		{
			// Socket used for establishing a connection
			m_connection = null;
			
			try
			{
				// Connect to a remote host - BLOCKING I/O
				m_connection = new Socket(m_host, m_port);
			}
			catch (IOException ioe)
			{
				// Assign to our exception member variable
				m_exception = ioe;
				if (log.isDebugEnabled()) log.debug("TimedSocket:SocketThread.run() with host='" + m_host + "', port='" + m_port + "' caught exception '" + ioe.toString() + "'.");
			}	//end if exception
			
			if (doneWaiting || isError())
			{
				//if the caller got tired of waiting then
				//immediately close the socket since the caller is no longer
				//waiting for the socket.
				//If there was an error but the socket was still somehow established also
				//close it
				if (m_connection != null)
				{
					try
					{
						if (log.isDebugEnabled()) log.debug("TimedSocket:SocketThread.run() with host='" + m_host + "', port='" + m_port +  "' attempting to close newly opened socket with doneWaiting='" + doneWaiting +  "'.");
						m_connection.close();
						m_connection = null;
					}
					catch (IOException e)
					{
					}
				}
			}	//end if done waiting or caught an exception
		}	//end run
		
		// Are we connected?
		public boolean isConnected()
		{
			if (m_connection == null)
				return false;
			else
				return true;
		}
		
		// Did an error occur?
		public boolean isError()
		{
			if (m_exception == null)
				return false;
			else
				return true;
		}
		
		// Get socket
		public Socket getSocket()
		{
			return m_connection;
		}
		
		// Get exception
		public IOException getException()
		{
			return m_exception;
		}
	}	//end SocketThread inner class
	
	
	
	public static void main(String[] args) throws Exception
	{
		org.apache.log4j.PropertyConfigurator.configure(com.vicinity.dash.stateEngine.AllFamilies.getLog4JConfPath());
		try
		{
			//InetAddress addr = InetAddress.getByName("192.168.0.3");
			//Socket s = TimedSocket.getSocket (addr, 80, 5000);
			Socket s = TimedSocket.getSocket("localhost", 80, 5000);
			s.close();
			System.out.println("localhost connected");
			s = TimedSocket.getSocket("192.168.200.243", 80, 5000);
		}
		catch (IOException ioe)
		{
			System.out.println("ioexception '" + ioe.toString() + "'.");
		}
		
	}	//end main

}	//end TimedSocket class

0 Comments  (click to add your comment)
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

About | Sitemap | Contact