Size: 11715
Comment:
|
Size: 10612
Comment: Significant update to reflect current status of the module.
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
[[TableOfContents()]] | [[TableOfContents(3)]] |
Line 7: | Line 7: |
This page descibes a new implementation of non-blocking sockets for jython. Hopefully, this new support should make possible the use of event-based server frameworks on jython. Examples of cpython event-based frameworks are [http://twistedmatrix.com/ Twisted], [http://www.zope.org/ Zope] and [http://www.amk.ca/python/code/medusa.html Medusa]. | This page descibes the socket module for jython 2.2, which for the first time has support for [http://en.wikipedia.org/wiki/Non-blocking_I/O asynchronous or non-blocking operations]. Hopefully, this new support should make possible the use of event-based server frameworks on jython. Examples of cpython event-based frameworks are [http://twistedmatrix.com/ Twisted], [http://www.zope.org/ Zope] and [http://www.amk.ca/python/code/medusa.html Medusa]. It is considered by some that Asynchronous IO is the only way to address [http://www.kegel.com/c10k.html The C10K problem] |
Line 9: | Line 9: |
The implementation currently resides in the jython sandbox because | The socket module is pretty much feature complete. It is written to use java.nio apis as much as possible, i.e. all reads and writes are carried out through the sockets java.nio.channel based interface. However, there are some circumstances, specifically timeout operations, where the java.net.socket interfaces have to be used. |
Line 11: | Line 11: |
* The modules are new, and need testing on multiple Operating Systems and Java Virtual Machines (JVMs) before being committed to the core language. * The version of jython in which they will be available, and the mechanism by which the support will be integrated, is yet to be decided. |
The socket module is quite stable; a [http://bugs.jython.org/issue?%40search_text=&title=&%40columns=title&id=&%40columns=id&creation=&creator=&activity=&%40columns=activity&%40sort=activity&actor=&nosy=&type=&components=&versions=&severity=&dependencies=&assignee=amak&keywords=&priority=&%40group=priority&status=&%40columns=status&resolution=&%40pagesize=50&%40startwith=0&%40action=search number of bugs in the functionality have been found and fixed]. |
Line 14: | Line 13: |
There are necessarily some differences between the behaviour of the cpython and jython socket modules, because jython is implemented on the java socket model, which is more restrictive than the C language socket interface that cpython is based on. It is the purpose of this document to describe those differences. If you find a difference in behaviour between cpython and jython that is not documented here, and where jython is behaving differently to the cpython socket documentation, then [http://bugs.jython.org/ that should be considered a bug and reported, please]. |
|
Line 17: | Line 18: |
* JVM version with [http://java.sun.com/j2se/1.4.2/docs/guide/nio/ java.nio] support, i.e. >= version 1.4 (> 1.4.2_13 highly recommended) * Any jython version >= 2.1 |
* JVM version with [http://java.sun.com/j2se/1.4.2/docs/guide/nio/ java.nio] support, i.e. >= version 1.4 * Any jython version >= 2.2 |
Line 22: | Line 23: |
* Any jython version >= 2.1 | * Any jython version >= 2.2 |
Line 24: | Line 25: |
== How to get these modules == If you want to use these modules, then you should download them from Subversion at this location. https://svn.sourceforge.net/svnroot/jython/trunk/sandbox/kennedya/asynch_sockets Store them on your local system in some directory, named say '''/path/to/asynch_sockets'''. To ensure that jython finds the modules, you will have to set the '''python.path''' variable to include the '''asynch_sockets''' directory. You can do this at runtime by appending the directory name to the sys.path list, like this {{{ import sys sys.path.append("/path/to/asynch_sockets") }}} Or you can set the python.path variable in the '''registry''' file in the jython home directory. Since jython finds modules in its '''Lib''' directory before modules on the python.path list, you will possibly need to delete or rename the '''socket.py''' module in the '''Lib''' directory so that it is not picked up first. == How to use these modules == As before, the socket module is imported like this {{{ import socket }}} However, the socket module is now a wrapper for two different modules, '''net_socket.py''' and '''nio_socket.py'''. The former is the old jython 2.1 socket module, and the latter is the new module with non-blocking support. The socket module now simply imports all socket definitions from either one of these two modules, using the following code {{{ try: raise ImportError from nio_socket import * except ImportError: from net_socket import * }}} As you can see, the socket support will default to the old socket module (net_socket.py), because of the explicitly raised !ImportError. If you remove or comment out the '''raise !ImportError''' command, then the new non-blocking socket module will be used instead. (But if you run this code on a pre-1.4 JVM, the lack of a java.nio module will raise an !ImportError exception, and the old net_socket.py module will still be used). |
== Cpython compatibility == |
Line 71: | Line 31: |
If the new jython modules exhibit behaviour that differs from that described in the cpython documentation, then that should be considered a bug and reported as such. | If the new jython module exhibits behaviour that differs from that described in the cpython documentation, then that should be considered a bug and reported as such. |
Line 75: | Line 35: |
These modules include an implementation of client side SSL support, which is compatible with the cpython SSL API. The client side ssl example from the cpython documentation should just work. | The module includes an implementation of client side SSL support, which is compatible with the cpython SSL API. The client side ssl example from the cpython documentation should just work. |
Line 90: | Line 50: |
== Jython versions == | === Certificate Checking === |
Line 92: | Line 52: |
These modules were developed on jython 2.1. They were tested on all available later versions, which at this time is 2.2beta2 and 2.3 (a0?). | By default, the cpython socket library does not carry out certificate checking, which means that it will accept expired certificates, etc. This is acceptable when the validity of the remote certificate is not important, such as in testing. |
Line 94: | Line 54: |
The modules should work identically on all versions, and have passed all available unit tests on all versions. | In Java, by default, certificate checking is '''enabled'''. This means that the jython ssl routines *will* verify the validity of the remote certificate, and refuse to form the connection if it is not valid. If this is a problem for you, then you can get around as described here. ==== By configuring your JVM ==== If you are using self-generated certificates for testing, then you can import those certificates into the JVM running your jython scripts, and the certificate will then be recognised. See this article from Sun on [http://java.sun.com/j2ee/1.4/docs/tutorial/doc/Security6.html how to install certificates on a JVM]. ==== BY installing your own SecurityManager ==== TODO: Need to write some code and documentation for how to install your own [http://java.sun.com/j2se/1.4.2/docs/api/java/lang/SecurityManager.html SecurityManager] so that it simply accepts all certificates. Here is a java article on [http://scv.bu.edu/Doc/Java/tutorial/networking/security/index.html Providing Your Own Security Manager] |
Line 101: | Line 69: |
== Why two socket modules? == In java version 1.3 and previous, the only socket support available was through the java.net package, which only provides support for blocking sockets. Support for timeouts was also provided. The existing jython socket module is written against the java.net API, and hence does not support non-blocking sockets. In java 1.4, the New I/O package java.nio was introduced. It provides support for non-blocking or asynchronous sockets. Therefore, non-blocking and multiplexing support can only be provided on JVMs >= 1.4. Since jython still supports JVM 1.3, it must retain socket support for that version. Initially, I tried to upgrade the existing jython 2.1 socket module to support non-blocking IO AND to support graceful degradation to blocking behaviour on JVMs where java.nio was not available. However, the code became too complex to manage, so I decided to abandon that approach. Instead, I decided to maintain two different versions of the socket module, the old java.net based module and a new java.nio based module, for these reasons 1. The old jython socket module is very stable, having been in the standard library for many years. It is desirable to retain this stability for users who require it. This module has been renamed '''net_socket.py'''. 1. The new socket module will need a significant amount of field-testing before it is ready for the prime time. Although it is fully passing the cpython test suites. This module is called '''nio_socket.py'''. 1. If and when jython ends support for JVM 1.3, as Sun have already done, the transition will be a simple case of deleting the old net_socket.py module and renaming nio_socket.py to socket.py. == JVM bug fixes == The java.nio package was introduced in java in 200?. This implementation of non-blocking sockets for jython was written in 2004, and was essentially complete back then. However, whenever it was run against JVMs available at that time, and on JVMs up to and including version 1.4.2_09 (release date), it would hang when the unit tests were run (On Windows Server 2003 and Windows 2000 Server). The main symptom was that server sockets would remain in existence long after they had been closed, in CLOSE_WAIT and TIME_WAIT states which would time out after a long period of minutes. I tore my hair out for a long time trying to figure out what the problem was, and tried all kinds of approaches to fix the problem, i.e. adding, omitting or changing the various operations involved in closing a non-blocking socket. You can read more detail here http://www.nabble.com/Non-blocking-IO-update.-tf389917.html#a1074623 But when I upgraded my JVM to jdk 1.4.2_13, and ran the tests on that, the whole problem disappeared! I still do not know why; possibilities include * There was a bug in previous JVMs that caused the problem. * There was a bug in my development platform (Windows Server 2000 and 2003) that caused the problem. But you should know that if you use this non-blocking support on JVM versions prior to 1.4.2_13, it is quite possible that you will have the same problem with sockets not closing properly. Therefore, I recommend running this code only on JDK 1.4.2_13 or greater; if you successfully run it on previous versions, especially on different platforms, let me know about. |
|
Line 150: | Line 83: |
== Unit tests == | == Differences between cpython and jython == |
Line 152: | Line 85: |
If you plan to make use of these modules on any platform, I '''highly''' recommend running the unit tests first; they should flush out any problems you might have. | === Error handling === |
Line 154: | Line 87: |
There are four test modules in the '''test''' subdirectory | These modules have been coded so that the error handling is as close to the error handling of cpython as possible. So, ideally, cpython socket and select code run on jython should exhibit identical behaviour to cpython, in terms of exception types raised, error numbers returned, etc. |
Line 156: | Line 89: |
1. '''test_socket_from_cpython24.py'''. This module, as the name suggests, is ported from the cpython 2.4 code base; all tests in it pass on all available versions from 2.1 on. The ported module still runs on cpython, for compatibility checks. (Actually, it fixes a couple of small bugs in the original cpython test module). | However, due to the different semantics of java and C, there will be differences in the error handling, which will be documented here as they are discovered. |
Line 158: | Line 91: |
1. '''test_select_from_cpython24.py'''. This module is the cpython '''test_select''' module, ported to work on jython. This modules does not use a unit test framework. | === Differences in the treatment of zero timeout values === |
Line 160: | Line 93: |
1. '''test_select_from24_as_unittest.py'''. This module contains the same tests as the module above, but rewritten so that they work with the unittest module. | On cpython, when you specify a zero (i.e. 0 or 0.0) timeout value, the socket should behave the same as if the socket had been placed in non-blocking mode. See the [http://www.python.org/doc/lib/socket-objects.html cpython socket object documentation] for details. |
Line 162: | Line 95: |
1. '''test_select_new.py'''. This is a brand new module that I wrote to test the select API. It has been written to work on cpython and jython, and passes all tests on both. | However, [http://java.sun.com/j2se/1.4.2/docs/api/java/net/Socket.html#setSoTimeout(int) Java interprets a zero timeout value as an infinite timeout], i.e. the socket is placed in blocking mode. |
Line 164: | Line 97: |
== Platform notes == | To solve this conflict, I decided that the best thing to do with zero timeouts is to adjust them to the smallest possible timeout in java, which is 1 millisecond. So if you do socket.settimeout(0) with the new jython socket module, what you will really get is equivalent to the cpython call socket.settimeout(0.001). |
Line 166: | Line 99: |
* TBD | This means that you may get differing exception signatures when using zero timeouts under cpython and jython. 1. Cpython: socket operations with zero timeouts that fail will raise socket.error exceptions. This is equivalent to a -1 return from the C socket.recv call, with errno set to EAGAIN, meaning "The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received." 1. Jython: socket operations with zero timeouts that fail will generate socket.timeout exceptions. == Known issues and workarounds == === Null address returned from UDP socket.recvfrom() in timeout mode === This bug is reported on the jython bug tracker: [http://bugs.jython.org/issue1018 UDP recvfrom fails to get the remote address]. ==== Description ==== When an UDP socket is in timeout mode, the address returned from socket.recvfrom() is always (None, -1). This only happens in timeout mode, because that code path uses java.net.DatagramSocket.receive() to receive packets. For some reason, DatagramPackets receive()d in this way either * Return null from the DatagramPacket.getAddress() method, thus preventing us from obtaining the source address * Cause an exception when DatagramPacket.getSocketAddress() is called. This bug has been reported to Sun, but is not yet public on the Java bug database. ==== Workaround ==== 1. Until the java bug is fixed, only use sockets in either blocking or non-blocking mode; the problem will not occur in this case. 1. If you need timeouts, then until the java bug is fixed, you should probably create your own DatagramSockets, directly through the java.net APIs. |
New Socket Module
Introduction
This page descibes the socket module for jython 2.2, which for the first time has support for [http://en.wikipedia.org/wiki/Non-blocking_I/O asynchronous or non-blocking operations]. Hopefully, this new support should make possible the use of event-based server frameworks on jython. Examples of cpython event-based frameworks are [http://twistedmatrix.com/ Twisted], [http://www.zope.org/ Zope] and [http://www.amk.ca/python/code/medusa.html Medusa]. It is considered by some that Asynchronous IO is the only way to address [http://www.kegel.com/c10k.html The C10K problem]
The socket module is pretty much feature complete. It is written to use java.nio apis as much as possible, i.e. all reads and writes are carried out through the sockets java.nio.channel based interface. However, there are some circumstances, specifically timeout operations, where the java.net.socket interfaces have to be used.
The socket module is quite stable; a [http://bugs.jython.org/issue?%40search_text=&title=&%40columns=title&id=&%40columns=id&creation=&creator=&activity=&%40columns=activity&%40sort=activity&actor=&nosy=&type=&components=&versions=&severity=&dependencies=&assignee=amak&keywords=&priority=&%40group=priority&status=&%40columns=status&resolution=&%40pagesize=50&%40startwith=0&%40action=search number of bugs in the functionality have been found and fixed].
There are necessarily some differences between the behaviour of the cpython and jython socket modules, because jython is implemented on the java socket model, which is more restrictive than the C language socket interface that cpython is based on. It is the purpose of this document to describe those differences. If you find a difference in behaviour between cpython and jython that is not documented here, and where jython is behaving differently to the cpython socket documentation, then [http://bugs.jython.org/ that should be considered a bug and reported, please].
Requirements
Non-blocking support
JVM version with [http://java.sun.com/j2se/1.4.2/docs/guide/nio/ java.nio] support, i.e. >= version 1.4
Any jython version >= 2.2
SSL support
- JVM version: any version on which jython runs.
Any jython version >= 2.2
Cpython compatibility
The new socket module has been written to comply as closely as possible with the cpython 2.4 API for non-blocking sockets, therefore you should use the cpython socket documentation as your reference when writing code.
http://www.python.org/doc/2.4/lib/module-socket.html
If the new jython module exhibits behaviour that differs from that described in the cpython documentation, then that should be considered a bug and reported as such.
SSL Support
The module includes an implementation of client side SSL support, which is compatible with the cpython SSL API. The client side ssl example from the cpython documentation should just work.
http://docs.python.org/lib/socket-example.html
However, the cpython SSL API is extremely basic, and essentially only permits the formation of SSL wrapped sockets. It does NOT include support for any of the following
- Management of Certificates, i.e. loading, storing, manipulating certificates.
- Verification of Certificates, i.e. verifying the chain of trust.
- Non-blocking SSL support.
- Server side SSL support.
All of the above are possible, but since no other python version includes that support in the base distribution, I'm not going to do it for jython either; trying to design an API would complex enough; implementing would be a lot of work beyond that.
If you have serious SSL or crypto requirements, then I strongly recommend using the java crypto libraries, or one of the excellent third-party crypto libraries for java, such as that from the [http://www.bouncycastle.org Legion of the Bouncy Castle].
Certificate Checking
By default, the cpython socket library does not carry out certificate checking, which means that it will accept expired certificates, etc. This is acceptable when the validity of the remote certificate is not important, such as in testing.
In Java, by default, certificate checking is enabled. This means that the jython ssl routines *will* verify the validity of the remote certificate, and refuse to form the connection if it is not valid. If this is a problem for you, then you can get around as described here.
By configuring your JVM
If you are using self-generated certificates for testing, then you can import those certificates into the JVM running your jython scripts, and the certificate will then be recognised. See this article from Sun on [http://java.sun.com/j2ee/1.4/docs/tutorial/doc/Security6.html how to install certificates on a JVM].
BY installing your own SecurityManager
TODO: Need to write some code and documentation for how to install your own [http://java.sun.com/j2se/1.4.2/docs/api/java/lang/SecurityManager.html SecurityManager] so that it simply accepts all certificates. Here is a java article on [http://scv.bu.edu/Doc/Java/tutorial/networking/security/index.html Providing Your Own Security Manager]
Cpython versions
It is the intention that these modules be fully API compatible with cpython, as far as is possible or sensible. This means that any cpython socket code that is syntax compatible with your selected jython version should produce identical behaviour to the same code running on cpython.
The unit-tests provided with these modules should also run on all versions of cpython. See below under unit-tests for more details.
Other I/O
The design of the cpython non-blocking API is derived from the UNIX C api, which deals with FILE DESCRIPTORS. Since file descriptors can describe any type of I/O channel on unix OSes, cpython can deal with selecting and polling on multiple channel types, such non-blocking files, pipes and named-pipes, fifos, etc.
The java model for selecting on channels is much more restrictive. There is a java abstract class, java.nio.channels.SelectableChannel, which other channel classes must subclass if they are to be multiplexed. On 1.4 JVMs, only the following classes subclass [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/SelectableChannel.html SelectableChannel].
[http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/SocketChannel.html Socket channels]
Pipe [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/Pipe.SourceChannel.html source] and [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/Pipe.SinkChannel.html sink] channels
Specifically, [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/FileChannel.html FileChannel's] do not subclass [http://java.sun.com/j2se/1.4.2/docs/api/java/nio/channels/SelectableChannel.html SelectableChannel], and thus it is not possible to include files in non-blocking multiplex operations on Java platforms.
Of the two channel types listed above, these modules only support socket channels. This is because it was necessary to rewrite all of the socket creation calls to return SelectableChannels. To do so for Pipes, which are used for communication with sub-processes, it would be necessary to rewrite the jython sub-process creation modules, i.e. popen, etc, to create SelectableChannels. Although it should be reasonably straightforward to implement this, I have no plans to do this work.
Differences between cpython and jython
Error handling
These modules have been coded so that the error handling is as close to the error handling of cpython as possible. So, ideally, cpython socket and select code run on jython should exhibit identical behaviour to cpython, in terms of exception types raised, error numbers returned, etc.
However, due to the different semantics of java and C, there will be differences in the error handling, which will be documented here as they are discovered.
Differences in the treatment of zero timeout values
On cpython, when you specify a zero (i.e. 0 or 0.0) timeout value, the socket should behave the same as if the socket had been placed in non-blocking mode. See the [http://www.python.org/doc/lib/socket-objects.html cpython socket object documentation] for details.
However, [http://java.sun.com/j2se/1.4.2/docs/api/java/net/Socket.html#setSoTimeout(int) Java interprets a zero timeout value as an infinite timeout], i.e. the socket is placed in blocking mode.
To solve this conflict, I decided that the best thing to do with zero timeouts is to adjust them to the smallest possible timeout in java, which is 1 millisecond. So if you do socket.settimeout(0) with the new jython socket module, what you will really get is equivalent to the cpython call socket.settimeout(0.001).
This means that you may get differing exception signatures when using zero timeouts under cpython and jython.
- Cpython: socket operations with zero timeouts that fail will raise socket.error exceptions. This is equivalent to a -1 return from the C socket.recv call, with errno set to EAGAIN, meaning "The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received."
- Jython: socket operations with zero timeouts that fail will generate socket.timeout exceptions.
Known issues and workarounds
Null address returned from UDP socket.recvfrom() in timeout mode
This bug is reported on the jython bug tracker: [http://bugs.jython.org/issue1018 UDP recvfrom fails to get the remote address].
Description
When an UDP socket is in timeout mode, the address returned from socket.recvfrom() is always (None, -1).
This only happens in timeout mode, because that code path uses java.net.DatagramSocket.receive() to receive packets. For some reason, DatagramPackets receive()d in this way either
Return null from the DatagramPacket.getAddress() method, thus preventing us from obtaining the source address
Cause an exception when DatagramPacket.getSocketAddress() is called.
This bug has been reported to Sun, but is not yet public on the Java bug database.
Workaround
- Until the java bug is fixed, only use sockets in either blocking or non-blocking mode; the problem will not occur in this case.
If you need timeouts, then until the java bug is fixed, you should probably create your own DatagramSockets, directly through the java.net APIs.