对于InputStream的 read(b, off, len) 方法 public int read(byte[] b, int off, int len) throws IOException,Javadoc的说明为:
If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available becausethe stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b.那么对于服务端Socket的输入流来说,什么是 end of file - EOF?首先说明一点,没有所谓的标识字符是EOF,对于字节流来说,从0~255的每个字节都是正常的数据,EOF只是输入流的一种状态。
当Socket客户端关闭的时候,服务端输入流在读完所有数据之后就会检测到EOF,然后服务端输入流返回-1。如果客户端Socket没有关闭,并且没有数据可读取的情况下,read方法会阻塞,等待有数据可读。如果设置了SoTimeout,那么直到超时抛出异常,如果没有设置超时,那么会一直等待数据到达。————————————————————————
今天开发一个Socket通讯agent,Java 程序中启动一个ServerSocket,用来与shell脚本中"nc"命令通讯并交换数据.ServerSocket可以收到"nc"发送的数据,但是"nc"却接受不到数据..代码样例:
shell调用方式:
代码非常简单, 可是为什么不行呢?通过跟踪,Java代码中可以从socket中read到"ping"字符串,但是为什么out.write(">>>")的数据不能被shell获取呢??OK,换一下代码风格,再试一试:
不好意思,成功了...为什么BufferedWriter不行,反而OutStream行呢?后来简单的翻阅了一下源码,简单的修改一下即可:
加上"out.flush()"即可,对于BufferedWriter而言,write()方法并没有直接将数据写入到物理的IO流中,而是首先cache在了一个字节数组中,只有当cache的数据量达到buffer-size时才会触发flush,flush的作用就是将多个字节依次写入到IO中,原以为socket.close()方法会执行flush(),尽管OutputStream中有flush方法,事实上没有执行.
所以使用BufferedWriter的时候,一定要在IO关闭之前,调用flush()方法,否则将会丢失部分数据...真是费劲.
不过顺便还要提一个问题,如果JAVA Socket向远端write数据后,却始终收不到远端发来的数据,还可能有下面的一种情况:
上面的代码中,socket向远端write数据之后,开始read远端返回的数据,但是始终无法read到数据,是怎么回事?其实原因也很简单,这就是典型的"Socket死锁": socket write()时,同时远端也在read,因为socket通讯是基于流(frame)的,如果远端read时没有收到EOF,那么read操作将一直阻塞,直到socket关闭.上面的代码,就是出现了远端socket read一直阻塞而没有机会执行write,同时本地socket也因为read阻塞,导致了"死锁",代码调整如下:
在writer数据结束后,执行shutdownOutput,将output流通道关闭,此时远端socket就会read到EOF, 认为通道中不会再有数据可读.shutdownOutput()方法不会关闭整个socket,只是关闭了socket中的outputStream,不过一旦调用了此方法,此后将不能再调用writer.write()方法.
此外还可以通过socket IO字节成帧手段来解决上述问题.