最近写服务器时想到一个问题:用Java Bio(即Socket)写服务器,怎么一次性完整读取已经到达的Socket流。
对这个需求有很多角度的设定,也有很多解法。我们来一一具化这个需求:
(1)
解法:依赖http协议的content-length。
分析:很直观的想法,可以根据http请求头给定一个固定长度的字节或字符缓存,从中获取content-length,就知道往后要在从流中读多少字节了。
设定:如果不考虑http,设置也不考虑任何定制的协议(流的开头给长度或者用特殊的字符标志留的结尾),仅仅考虑一次socket流的到达,如何才能用完整读取这次到达流的内容。
(2)
解法:上nio上mina上netty。
分析:又直观的想法。不过开篇设定好了用阻塞读来实现非阻塞完整读取到达流。
设定:如何用java阻塞读(bio)达到不阻塞的完整读取一次socket到达流。
(3)
解法:依赖流的结尾返回-1。
分析:更直观的想法,java.io(也就是bio啦)提供的各种XXStream和XXReader都提供read(XX)的函数,并提示如果读到结尾就返回-1。那在服务器端直接不断read(),直到-1就好了。问题是对于文件的读取,到达结尾会返回-1(好像是文件末尾的EOF?具体没研究过)。但是socket流,是没有结束符的(其实也有,一个socket关闭后再read就会返回-1,但是这里对于一个已经到达的流的完整读取,肯定不能依赖于网络对面对socket关闭)。虽然文件流是连续的,但是网络流肯定不能保证,即便通过socket发送一个完整的文件,由于网络原因,这个文件可能分几个部分到达socket,而本文的需求就设定在对一次到达的流,怎么保证完整的读取。
(4)
解决:用大缓存。
分析:更加直观的想法。上个足够大的缓存,一次性把到达流的内容全部读出来&#