Message3068

Author amak
Recipients amak, fwierzbicki, rluse
Date 2008-03-07.15:05:28
SpamBayes Score 3.58804e-05
Marked as misclassified No
Message-id <1204902329.3.0.231857314258.issue1005@psf.upfronthosting.co.za>
In-reply-to
Content
I've been reading up this bug some more, and realised that I've been
missing out on Bob's subtle point of implicitly created sockets being
bound to *some* address, even if not to a specific address, by virtue of
their creation; I'll refer to this address as the "assigned address".


So, what is the actual assigned address, in Java? Consider the following
code

//--------------------------------------------------------------
import java.io.IOException;

import java.nio.channels.DatagramChannel;

import java.nio.ByteBuffer;

import java.net.DatagramSocket;

import java.net.InetSocketAddress;



class udp_unbound_address

{

    public static void main ( String args[] )

        throws IOException

    {

        DatagramChannel chan = DatagramChannel.open();

        if (args.length > 0 && "sendto".compareTo(args[0]) == 0)

            chan.send(ByteBuffer.wrap("testdata".getBytes()), 

                new InetSocketAddress("www.jython.org", 8888));

        DatagramSocket sock = chan.socket();

        System.out.println("Is bound?: " + sock.isBound());

        System.out.println("Local address is: "

            + sock.getLocalAddress());

        System.out.println("Local port is: "

            + sock.getLocalPort());

        System.out.println("Local socket address is: "

            + sock.getLocalSocketAddress());

    }

}
//--------------------------------------------------------------

If you run this, you see that there is no assigned address when sockets
are created with java.nio. Here is the output from running on Unbuntu
7.10. (I get similar output on Windows Server 2003)

$ java -cp . udp_unbound_address 
Is bound?: false
Local address is: 0.0.0.0/0.0.0.0
Local port is: 0
Local socket address is: null

Making the socket do a sendto() first changes the picture slightly, in
that the socket now becomes bound, but there is still no assigned
hostname/IP or port.

$ java -cp . udp_unbound_address sendto
Is bound?: true
Local address is: 0.0.0.0/0.0.0.0
Local port is: 0
Local socket address is: 0.0.0.0/0.0.0.0:0

Note that this is different behaviour from the way old
java.net.DatagramSocket's behaved, which was to implicitly assign a port
to the socket, but not an IP address, regardless of the use of sendto.
Consider this code

//--------------------------------------------------------------
import java.io.IOException;

import java.net.DatagramPacket;

import java.net.DatagramSocket;

import java.net.InetSocketAddress;



class udp_unbound_address_net

{

    public static void main ( String args[] )

        throws IOException

    {

        DatagramSocket sock = new DatagramSocket();

        if (args.length > 0 && "sendto".compareTo(args[0]) == 0)

            sock.send(new DatagramPacket("testdata".getBytes(), 
                "testdata".getBytes().length,
                new InetSocketAddress("www.jython.org", 8888)
            ));

        System.out.println("Is bound?: " + sock.isBound());

        System.out.println("Local address is: "

            + sock.getLocalAddress());

        System.out.println("Local port is: "

            + sock.getLocalPort());

        System.out.println("Local socket address is: "

            + sock.getLocalSocketAddress());

    }

}
//--------------------------------------------------------------

Which gives the following results on Ubuntu, and similar on windows.

$ java -cp . udp_unbound_address_net
Is bound?: true
Local address is: 0.0.0.0/0.0.0.0
Local port is: 33066
Local socket address is: 0.0.0.0/0.0.0.0:33066

$ java -cp . udp_unbound_address_net sendto
Is bound?: true
Local address is: 0.0.0.0/0.0.0.0
Local port is: 33067
Local socket address is: 0.0.0.0/0.0.0.0:33067

(It is important to note that we cannot create UDP sockets using the old
java.net classes; to do so would prevent us from getting non-blocking
IO: it is only possible to do non-blocking IO on sockets created through
java.nio APIs).

Now lets look at cpython. If you try to get the address of a newly
created UDP socket, the result is an error on Windows

Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
win32

>>> import socket

>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

>>> s.getsockname()

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

  File "<string>", line 1, in getsockname

socket.error: (10022, 'Invalid argument')

>>>

And on Unbuntu, we get the same result as Java, i.e. a null address

Python 2.5.1 (r251:54863, Oct  5 2007, 13:36:32) 
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.getsockname()
('0.0.0.0', 0)
>>> 

If we do a sendto before getting the sockname, then the situation
changes slightly on cpython

On both windows and Unbuntu, we now get a assigned port number, but
still a null machine address

Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
win32

>>> import socket

>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

>>> s.sendto("testdata", ("www.jython.org", 8888) )

8

>>> s.getsockname()

('0.0.0.0', 1246)

>>>

Python 2.5.1 (r251:54863, Oct  5 2007, 13:36:32) 
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.sendto("testdata", ("www.jython.org", 8888) )
8
>>> s.getsockname()
('0.0.0.0', 33063)
>>> 

So, let's try to relate that to our problem about whether or not
implicitly create a socket on unbound recvfrom, or raise an exception.
Implicitly creating a socket is only useful if there is a usable (i.e.
connectable/sendable from a different socket) assigned address, that can
somehow be publicized (other than sent through the socket itself). But
there is no usable assigned address, in java or cpython, so I see no
point in implicitly creating the socket; it can be used for nothing.

It is interesting to note that there is an outstanding bug in the Sun
database which complains that "The DatagramChannel.receive method does
not specify the behavior when the channel is not bound."


http://bugs.sun.com/view_bug.do?bug_id=6621689

It says

"""
Since 1.4, our implementation has returned null, even for the blocking
case. Throw NotNotBoundException or explicitly binding the channel's
socket might have been better choices but it cannot now be changed.
"""

So the original java.nio interpretation was different again; it
recognised the unbound state of the socket and returned null for all
receive() operations . Of the two alternatives proposed, i.e.

1. throw a NotBoundException
2. explicitly binding the channel's socket

I still favour the first, because it clearly illustrates to the user
that whatever they think they are expecting is not going to happen.
Cpython favors the second option, but you get different behaviour on
different platforms (see below).

If it were possible to get an usable assigned address for the implicitly
created socket, then it *might* be useful to implicitly create the
socket. But, as demonstrated above, there is no usable address.

It is worth noting that bug #6621689 has been accepted as a bug, and
that the fix is in progress, but no indication is given of what the
chosen fix is.

Lastly, I wanted to point out that there is different behaviour from
cpython on different platforms when a recvfrom is attempted on an
unbound socket.

Windows raises an error

c:\Python24>python

Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
win32

>>> import socket

>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

>>> s.recvfrom(1024)

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

socket.error: (10022, 'Invalid argument')


Whereas Ubuntu hangs on the same code

Python 2.5.1 (r251:54863, Oct  5 2007, 13:36:32) 
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> result = s.recvfrom(1024)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyboardInterrupt

In Bob's original example, setting a timeout seems to have modified this
behaviour, so that both platforms run the code and timeout, i.e.

Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
win32

Type "help", "copyright", "credits" or "license" for more information.

>>> import socket

>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

>>> s.settimeout(1.0)

>>> s.recvfrom(1024)

Traceback (most recent call last):

  File "<stdin>", line 1, in ?

socket.timeout: timed out

>>>

Python 2.5.1 (r251:54863, Oct  5 2007, 13:36:32) 
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.settimeout(1.0)
>>> s.recvfrom(1024)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
socket.timeout: timed out

So, in summary, this whole area seems to be poorly understand, and
varyingly implemented.

Cpython gives different behaviour across different platforms, but gives
consistent behaviour for Bob's original code.

Java gives consistent behaviour on all platforms. 

But if we adopt the stance of "implicit create sockets for unbound
recvfrom()s", then there is nothing can be done with the resulting
socket; on the java platform anyway. (although perhaps the fix to bug
#6621689 might change this).

I need to think about this over the weekend.

All opinions welcome.

Alan.
History
Date User Action Args
2008-03-07 15:05:29amaksetspambayes_score: 3.58804e-05 -> 3.58804e-05
recipients: + amak, fwierzbicki, rluse
2008-03-07 15:05:29amaksetspambayes_score: 3.58804e-05 -> 3.58804e-05
messageid: <1204902329.3.0.231857314258.issue1005@psf.upfronthosting.co.za>
2008-03-07 15:05:29amaklinkissue1005 messages
2008-03-07 15:05:28amakcreate