JDK-6421091 : (so) BindException on connect instead of the bind method (win)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2006-05-03
  • Updated: 2011-02-16
  • Resolved: 2007-05-01
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows 2000 [Version 5.00.2195]

A DESCRIPTION OF THE PROBLEM :
I create a server socket.  I create 2 client SocketChannels.  I bind both channels to the exact same SocketAddress, 127.0.0.1 port=8000.  I expect the second bind to fail, but it passes.  Failure does not occur until the connect.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run my JUnit test that I put together for this.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
BindException should happen on second bind to the same SocketAddress.
ACTUAL -
BindException happens on the second connect.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Created on May 1, 2004
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package biz.xsoftware.test.nio.suns;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.logging.Logger;

import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 *
 */
public class TestBindExceptionOnConnect extends TestCase {

	private final static Logger log = Logger.getLogger(TestBindExceptionOnConnect.class.getName());
	
	/**
	 * @param name
	 */
	public TestBindExceptionOnConnect(String name) {
		super(name);
	}

	public void setUp() {

	}
	
	public void tearDown() {

	}
	/**
	 * This tests BindException is thrown on connect.  This one
	 * is thrown because setReuse(true) and binding to an already used
	 * socket.
	 *
	 * This happens on windows.  need to test on linux.
	 * @throws Exception
	 */
	public void testBindExceptionOnConnect1() throws Exception {
		InetAddress loopBack = InetAddress.getByName("127.0.0.1");
		ServerSocketChannel svr = ServerSocketChannel.open();
		svr.socket().bind(new InetSocketAddress(loopBack, 0));
		SocketAddress svrAddr = svr.socket().getLocalSocketAddress();
		log.info("svrAddr = "+svrAddr);
		
		SocketChannel channel1 = SocketChannel.open();
		SocketChannel channel2 = SocketChannel.open();
		channel1.socket().setReuseAddress(true);
		channel2.socket().setReuseAddress(true);
		

		int port = 8000;
		InetSocketAddress addr1 = new InetSocketAddress(loopBack, port);
		channel1.socket().bind(addr1);
		channel2.socket().bind(addr1);
				
		channel1.connect(svrAddr);
		//let it connect and just discard the connection...
		svr.accept();
		
//NOTE: At home I keep the try....catch so my test fails when the bug is fixed.....
//		try {
			channel2.connect(svrAddr); //results in BindException
//			fail("Should have thrown a BindException");
//		} catch(BindException e) {
//			//gulp
//		}
		channel1.close();
	}
	
	public static void main(String[] args) {
		TestSuite suite = new TestSuite();
		suite.addTest(new TestDisconnectAfterRead("testReflection"));
		junit.textui.TestRunner.run(suite);
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
I am not sure there is one....just be ready for bind failures on the connect I guess though this can be difficult if bind and connect are done at different times to due protocols like H323 or something.

Comments
EVALUATION The setReuseAddress option toggles the SO_REUSEADDR socket option. Unfortunately on Windows this socket option does not provide BSD semantics and does not prevent multiple TCP sockets from binding to the same address/port. This explains why the bind method does not fail in this test case. The reason the connect method throws an exception stems from the fact that the TCP/IP protocol requires a unique 4-tuple to distinguish connections (4-tuple = local-addr/port and remote-addr/port). As both SocketChannels are bound to the same address/port and connect to the same remote address/port it means the second connect is required to fail. It fails with a bind error and that is why we throw a BindException. So is this problem fixable? In Windows 2000 SP1 Microsoft added the SO_EXCLUSIVEADDRUSE which prevents multiple sockets from binding to the same address/port. This socket option was initially only usable by processes with Administrator privileges but that was changed in Windows XP SP2 to allow non-Administrators use the option. Unfortunately this socket option has side effects. We can't use it for MulticastSocket for example as we need to be able to bind multiple UDP sockets to the same address/port for multicasting purposes. Also, the socket option creates problems for ServerSocket/ServerSocketChannel as it prevents the re-binding of the socket when a there is a previous connection in TIMED_WAIT state. One possible workaround for the submitter of this bug is to set the DisableAddressSharing registry. (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Afd\Parameters) and reboot. This registry setting prevents multiple sockets from binding to the same port and is essentially enabling SO_EXCLUSIVEADDRUSE on all sockets. We have not done any testing with this registry setting so there may be side-effects (like unable to re-bind when a previous connection is in timed wait for example).
03-05-2006