JDK-6600423 : [1.4.2]packets are lost in socket communication in java.nio
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.2_08
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux_redhat_3.0
  • CPU: itanium
  • Submitted: 2007-09-04
  • Updated: 2010-08-05
  • Resolved: 2007-09-04
Related Reports
Duplicate :  
Description
Packets are lost when a server and client communicate in socket in nio in 1.4.2_08.
The customer thinks,
"write() in nio should check written byte count andremaining byte count. 
If there are remaining bytes, the write should re-try to write all the bytes."

CONFIGURATION:
JDK : 1.4.2_08
OS  : Linux RHEL4 (IA64)

INVESTIGATION :
What customer recognize when they saw this issue are
1)Server was heavily-loaded
2)Client connect to server in TCP/IP
3)Server sent data to cient

<< Data flow when the error occurs >>

SERVER                                        CLIENT
+---------------------+                      +-------------------+
|PQ Linux64 JDK1.4.2  |                      |Windows XP/Vista   |
|                     |                      |                   |
|                     |                      |                   |
|Socket.              |                      |                   |
| getOutputStream().write(b) +--------+      |Receive()          |
|   +--------+        |      |HEAD-111|      |   +--------+      |
|   |HEAD-111| 32KB   |      +--------+      |   |HEAD-111| 32KB |
|   |22222222|        |      |22222222|      |   |22222222|      |
|   |33333333| -------+-==>  +--------+ -----+==>|33333333|      |
|   |44444444|        |      |33333333|      |   |44444444|      |
|   |55555555|        |      +--------+      |   |HEAD-666| *1   |
|   +--------+        |      |44444444|      |   +--------+      |
|                     |      +--------+      |                   |
|Socket.              |      |HEAD-666|      |                   |
  getOutputStream().write(b) +--------+      |Receive()          |
|   +--------+        |      |77777777|      |   +--------+      |
|   |HEAD-666| 32KB   |      +--------+      |   |77777777| *2   |
|   |77777777|        |      |88888888|      |   |88888888|      |
|   |88888888| -------+-==>  +--------+ -----+==>|99999999|      |
|   |99999999|        |      |99999999|      |   |00000000|      |
|   |00000000|        |      +--------+      |   +--------+      |
|   +--------+        |      |00000000|      |                   |
|                     |      +--------+      |                   |
+---------------------+    tcpdump on linux  +-------------------+
                           

NOTE:
 *1 This is Incorrect data. This should be "55555555", not "HEAD-666".
 *2 Because there is no header of thes packet, this is not delt correctly.


The server sends data to the client by the 32kBytes 
in Socket.getOutputStream().write(byte[] b).
The Socket is gotten from ServerSocketChannel.accept method.
There has not been any exception on this error.

They look into the source code portion and the write sequence is as follows.

.....
SocketChannelImpl.write()
 -> IOUtil.write()
 -> IOUtil.writeFromNativeBuffer()
 -> NativeDispacher.write()
 -> SocketDispacher.write()
 -> FileDispacher.write0()

The number of written bytes does not seem checked.
Specifically speaking, the return value of write(2) is transferred to 
java.nio.channels.Channels#write, but this value seems ignored in write 
in getOutputStream created in Channels#newOutputStream.

In order to confirm the above, they modify Channels.java.

[j2se/src/share/classes/java/nio/channels]
===============================================================
*** Channels.java.orig  Sat Mar  5 10:44:43 2005
--- Channels.java       Sat Aug 18 09:52:37 2007
***************
*** 131,137 ****
                      bb.position(off);
                      this.bb = bb;
                      this.bs = bs;
!                     Channels.write(ch, bb);
                  }
  
                public void close() throws IOException {
--- 131,143 ----
                      bb.position(off);
                      this.bb = bb;
                      this.bs = bs;
!                     int n = Channels.write(ch, bb);
!                     if (n != len) {
!                         String msg = "@@@ DEBUG : Channels.write(bs," + 
!                                       off + "," + len + ") return " + n +
!                                       " @@@";
!                         new IOException(msg).printStackTrace();
!                     }
                  }
  
                public void close() throws IOException {
===============================================================

They can get the following message. This shows the program exits before all the requested 
bytes has no been written.

java.io.IOException: @@@ DEBUG : Channels.write(bs,0,32767) return 8777 @@@
	at java.nio.channels.Channels$1.write(Channels.java:139)
	at java.io.OutputStream.write(OutputStream.java:58)
	at com.fffffff.systemwalker.listworks.server.protocol.Protocol.send(Unknown Source)
	at com.fffffff.systemwalker.listworks.server.framework.ClientCommunicator.nextCommand(Unknown Source)
	at com.fffffff.systemwalker.listworks.server.framework.CommandCommunicator.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:534)


They look into the similar portion in java.net.*.
For example, java.net.SocketOutputStream checks remaining byte count in NET_Send().
If there is remaining bytes which has not been written, the loop which try to write all the byte 
is implemented. However, there is no similar implementation in nio.
( Note:
  SocketOutputStream_socketWrite0 is in 
  [j2se/src/solaris/native/java/net/SocketOutputStream.c] )


REQUEST:
 As java.net.SocketOutputStream does,
 the implementation which re-tries to write all the requested byte 
 somewhere in the following sequence.
----
 SocketChannelImpl.write()
 -> IOUtil.write()
 -> IOUtil.writeFromNativeBuffer()
 -> NativeDispacher.write()
 -> SocketDispacher.write()
 -> FileDispacher.write0()
----