Message3068
Author | amak |
---|---|
Recipients | amak, fwierzbicki, rluse |
Date | 2008-03-07.15:05:28 |
SpamBayes Score | 3.5880403e-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:29 | amak | set | spambayes_score: 3.58804e-05 -> 3.5880403e-05 recipients: + amak, fwierzbicki, rluse |
2008-03-07 15:05:29 | amak | set | spambayes_score: 3.58804e-05 -> 3.58804e-05 messageid: <1204902329.3.0.231857314258.issue1005@psf.upfronthosting.co.za> |
2008-03-07 15:05:29 | amak | link | issue1005 messages |
2008-03-07 15:05:28 | amak | create |
Supported by Python Software Foundation,
Powered by Roundup