Duplicate :
|
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() ----