Alan,

I ran your program on Suse Linux 10.2 with JVM 1.5 update 12.  As you suspected , the program ran to completion with no paramter, but then when a parameter was passed to it, it stopped with the following utput:

Iteration: 0 Thread-0 ServChan .. ServSock .. ServBound .. Ready .. Cli accepted .. CliSock.reuse==true .. CliSock bound to(localhost:50007) ..  reset .. CliChan closed .. Closed ServSock ..
Thread-0 complete
Iteration: 1 Thread-2 ServChan .. ServSock .. java.net.BindException: Address already in useThread-2 complete

 - Bob


-----Original Message-----
From: Alan Kennedy <jython-dev@xhaus.com>
To: Charlie Groves <charlie.groves@gmail.com>
Cc: Raghuram Devarakonda <draghuram@gmail.com>; boblusebob@aim.com <boblusebob@aim.com>; jython-dev@lists.sourceforge.net; Pekka Laukkanen <peke@iki.fi>
Sent: Sun, 29 Jul 2007 7:28 am
Subject: Re: [Jython-dev] Socket bind problems on Linux.

[Charlie] 
> I think I fixed this in r3360. I was able to reproduce the errors 
> from Bob's script on my Ubuntu machine and they're fixed by that 
> change. 
 
> I'm a little perplexed as to how it fixes it though. The change just 
> fixes a bug where the client Java Socket returned from accept on a 
> ServerSocket would have its setReuseAddress value set to false 
> regardless of its initial value; now it keeps the value in the Socket. 
> It seems like if a ServerSocket has reuseAddress as true, its client 
> Sockets will have reuseAddress true as well. 
 
Well done Charlie, I think you nailed it. 
 
The SO_REUSEADDR status of 'accept'ed sockets is unclear in the java documentation. 
 
The javadoc for the java.net.Socket.setReuseAddress() method states that "When a Socket is created the initial setting of SO_REUSEADDR is disabled". This is the case for the docs for 1.4.x, 1.5 and 1.6 
 
http://java.sun.com/j2se/1.4.2/docs/api/java/net/Socket.html#setReuseAddress(boolean
http://java.sun.com/j2se/1.5.0/docs/api/java/net/Socket.html#setReuseAddress(boolean
http://java.sun.com/javase/6/docs/api/java/net/Socket.html#setReuseAddress(boolean
 
However, that appears not to apply to sockets created by 'accept'ing incoming connections, which seem to inherit the SO_REUSEADDR value of their parent ServerSocket. 
 
Furthermore, the documentation states that "The behaviour when SO_REUSEADDR is enabled or disabled after a socket is bound (See isBound()) is not defined". It appears that we are experiencing different behaviour across platforms, because setting SO_REUSEADDR after the creation (and implicit binding) of the 'accept'ed client socket gives different behaviour on different platforms. 
 
Windows and Mac seem to permit re-binding of the server socket, as long as SO_REUSEADDR is set on the server socket; the SO_REUSEADDR value of the 'accept'ed clients seems not to matter. 
 
Contrarily, Linux seems to NOT permit re-binding of the server socket if an 'accept'ed client socket has SO_REUSEADDR set to false after it is created (and presumably bound), and even if the server socket has an SO_REUSEADDR setting of true. 
 
Perhaps it depends on the address to which the newly 'accept'ed socket is bound? If it is (locally) bound to the same port as the server socket, then some platforms may consider that a clash of ports, whereas other don't, because one is a server and one is a client. I know that Windows uses a different model for permitting exclusive access to a given port: "In Windows 2000 SP1 Microsoft added the SO_EXCLUSIVEADDRUSE which prevents multiple sockets from binding to the same address/port". 
 
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6421091 
 
In order to try and get to the bottom of this bug, I wrote a small java program which uses the exact same sequence of operations as the socket and test_socket modules, in order to reproduce the problem. The code is attached. But I wasn't able to recreate the problem; the code ran smoothly on Windows and Red Hat, on both Sun and IBM JVMs. 
 
But now I have amended that code to re/set the SO_REUSEADDR flag on 'accept'ed client sockets (as well as display the value that they already have). The code, by default, leaves the value of the SO_REUSEADDR flag unchanged, which is the same behaviour as jython sockets have after Charlie made that change. 
 
So that java code *should* not fail with the same BindException as was troubling the test suite, when run on Linux. When I run it on Windows, it never fails. 
 
In order to reproduce the binding problems, pass any parameter to the class when it is run on the command line. The presence of any parameter will cause the resetting of the SO_REUSEADDR flag on all 'accept'ed sockets, which is the behaviour that jython sockets used to have before Charlies fix. So passing a parameter to this class should cause it to fail with BindExceptions. But I can only test it here at home on Windows (where it never fails); I don't have a Linux box here. I will run it on Red Hat in work on Monday; I expect it will reproduce the problem. 
 
Lastly, the code displays the bound status of the 'accept'ed client socket. This value is always displayed as bound on Windows, to the same port as the server socket; I would interested to know the bound status differs on other platforms. 
 
Good work Charlie! And many thanks to Raghu, Bob and Pekka for helping get to the bottom of this. 
 
All the best, 
 
Alan. 
import java.io.*;
import java.net.*;
import java.nio.channels.*;

public class BindBug
{
static final String HOST = "localhost";
static final int PORT = 50007;

static void log ( String message )
{
System.out.print(message);
System.out.flush();
}

public static class ServerThread extends Thread
{
public static boolean ready = false;
public static boolean setReuseFalse;

public ServerThread ( boolean pSetReuseFalse )
{
setReuseFalse = pSetReuseFalse;
}
public void run ( )
{
ServerSocketChannel serverChan;
ServerSocket serverSock;
try
{
synchronized (this)
{
log(getName() + " ");
serverChan = java.nio.channels.ServerSocketChannel.open();
log("ServChan .. ");
serverSock = serverChan.socket();
serverSock.setReuseAddress(true);
log("ServSock .. ");
serverSock.bind(new java.net.InetSocketAddress(HOST, PORT), 1);
log("ServBound .. ");
ready = true;
log("Ready .. ");
}
SocketChannel remoteCliChan = null;
while (remoteCliChan == null)
remoteCliChan = serverChan.accept();
log("Cli accepted .. ");
Socket remoteCliSock = remoteCliChan.socket();
log("CliSock.reuse==" + remoteCliSock.getReuseAddress()+ " .. ");
if (remoteCliSock.isBound())
log("CliSock bound to("+remoteCliSock.getLocalAddress().getHostName()+":"+remoteCliSock.getLocalPort()+")
.. ");
else
log("CliSock not bound .. ");
if (setReuseFalse)
{
remoteCliSock.setReuseAddress(false);
log(" reset .. ");
}
remoteCliChan.close();
log("CliChan closed .. ");
serverSock.close();
log("Closed ServSock .. \n");
}
catch (IOException iox)
{ System.err.println(iox); }
}
}

public static class ClientThread extends Thread
{
private static ServerThread server;
public ClientThread ( ServerThread pServer )
{
server = pServer;
}

public void run ( )
{
try
{
while (true)
{
synchronized (server)
{
if (server.ready)
break;
}
}
SocketChannel cliChan = java.nio.channels.SocketChannel.open();
cliChan.connect(new InetSocketAddress(HOST, PORT));
cliChan.close();
}
catch (IOException iox)
{ log("Client exception: " + iox); }
}
}

public static void main ( String[] args )
{
int numIterations = 10;
boolean setReuseFalse = false;
if (args.length > 0)
setReuseFalse = true;
for (int ix = 0 ; ix < numIterations ; ix++)
{
try
{
log("Iteration: " + ix + " ");
ServerThread servThread = new ServerThread(setReuseFalse);
servThread.ready = false;
servThread.start();
ClientThread cliThread = new ClientThread(servThread);
cliThread.start();
servThread.join();
log(servThread.getName() + " complete\n");
cliThread.join();
}
catch (Exception x)
{ log(x+"\n"); }
}

}

}

Check Out the new free AIM(R) Mail -- Unlimited storage and industry-leading spam and email virus protection.