inputstream.read() -1 问题

问题:
问题是关于从socket得到的inputStream的,想把inputstream转换成字符串打印出来,其中in表示已经从socket取到的inputstream:
Java code
StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048];try{ i = in.read(buffer); } catch(IOException e) { e.printStackTrace(); i = -1; } for(int j = 0; j < i; j++) [code=Java]
{
request.append((char) buffer[j]);
}
System.out.println(request.toString());[/code]
上面的代码可以打印出来,毫无问题.
于是我想这里面的buffer,也就是byte数组的作用是什么?无非是从inputstream中取字节流再append到StringBuffer中,那么可不可以不要这个中间过程呢?于是我这么写:
Java code
StringBuffer request = new StringBuffer(2048); int i;try { i = in.read(); while (i != -1) { request.append((char) i); i = in.read(); } } catch (Exception e) { e.printStackTrace(); }System.out.println(request.toString());

我想应该没有问题,因为读到inputstream流结束时,read()方法应该返回-1,while循环结束,但是却并没有返回-1,程序就在read最后一个字节那儿停住了,也不抛异常也不继续.
我把inputstream换成了读一个文件的FileInputStream,读取时就没有问题,说明这种取字节流的方式是可行的.那么为什么在读socket的输入流时就不返回-1呢?
究竟是为什么呢?请大家帮帮我!

-----------------------------------------------------------------------
答案1:
中间粘代码时不小心格式弄错了,不好意思啊

-----------------------------------------------------------------------
答案2:
在while里打印点东西出来看看

-----------------------------------------------------------------------
答案3:
第二个问题没有看明白..

第一个,加那个buffer的意思是不用每次读到一个字节,就处理一次,而是放到一个缓冲区中.

引用马老师的一个例子:就像接水,为什么我们总习惯于拿个小桶接满之后再去处理,而不是每一滴水都进行处理..

我想用个小桶,效率应该高点..


-----------------------------------------------------------------------
答案4:
楼上您说的对,但我现在就想每次读一个字节试一下,结果就没成功,我想知道为什么.
StringBuffer request = new StringBuffer(2048);
int i;
try {

i = in.read();
while (i != -1) {
request.append((char) i);
i = in.read();

}

} catch (Exception e) {
e.printStackTrace();
}

-----------------------------------------------------------------------
答案5:
StringBuffer request = new StringBuffer(2048);
int i;
try {
while ((i = in.read()) != -1) {
request.append((char) i);
}

} catch (Exception e) {
e.printStackTrace();
}
System.out.println(request.toString());


-----------------------------------------------------------------------
答案6[推荐答案]:
Java code
StringBuffer request = new StringBuffer(2048); int i; try { while ((i=in.read()) != -1) { request.append((char) i); } } catch (Exception e) { e.printStackTrace(); }


这样试试呢?

-----------------------------------------------------------------------
答案7:
没环境,都是猜的..不行就期待楼下吧..

-----------------------------------------------------------------------
答案8:
i = in.read();

你知道这个返回什么吗?

他可以返回任何东西,直到链接中断。


-----------------------------------------------------------------------
答案9:
引用 8 楼 java2000_net 的回复:

i = in.read();

你知道这个返回什么吗?

他可以返回任何东西,直到链接中断。

read
public abstract int read()
throws IOException从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
子类必须提供此方法的一个实现。


返回:
下一个数据字节;如果到达流的末尾,则返回 -1。
抛出:
IOException - 如果发生 I/O 错误。

-----------------------------------------------------------------------
答案10:
while循环中确实可以返回数据,但是确达不到流的末端!流的末尾是指什么条件啊?

-----------------------------------------------------------------------
答案11:
我看到的现象似乎是read()方法在阻塞,但是为什么呢?附全代码如下:
ServerSocket server = null;
int port = 8080;
String host = "localhost";
int backLog = 1;
try{
server = new ServerSocket(port, backLog, InetAddress.getByName(host));
Socket socket = null;
InputStream in;
OutputStream out;
while (true) {
socket = server.accept();
in = socket.getInputStream();
out = socket.getOutputStream();
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];

while ((i=in.read()) != -1) { //读完最后一个字节后阻塞了
request.append((char) i);
}

System.out.println(request.toString());

socket.close();


}
}


}catch(Exception e) {
e.printStackTrace();
}

Request的parse方法:


这里read()方法在读完最后一个字节后,就阻塞了,我想知道为什么?用
byte[] buffer = new byte[2048];
i = in.read(buffer);
就能准确读入,i是读入的字节流的长度,但read()无参方法一个一个字节的读,到最后一个字节后,就阻塞了。

-----------------------------------------------------------------------
答案12[推荐答案]:
Java code
/* * To change this template, choose Tools | Templates * and open the template in the editor. */package javaapplication1;import java.io.*;/** * * @author Administrator */public class teststream { public static void main(String[] args){ StringBuffer request = new StringBuffer(2048); BufferedInputStream in=new BufferedInputStream(System.in); int i; try { while ((i=in.read()) != 10) { request.append((char) i); System.out.println(i); } } catch (Exception e) { e.printStackTrace(); } System.out.println(request); }}

当输入回车(10)时退出while循环

-----------------------------------------------------------------------
答案13:
楼上先谢谢你,你说的方法确实能解决问题了。但是我想知道为什么会阻塞呢?是因为client端还在连接状态下吗?
看了类似的一篇文章:http://topic.csdn.net/u/20070703/16/576171b3-3b6f-4545-9018-c82e09637bbb.html
如果说client端处在连接状态下,inputstream就没有结束,于是就阻塞了的话,用
byte[] buffer = new byte[2048];
i = in.read(buffer);
又会准确得到inputstream的字节长度,搞不懂了。

-----------------------------------------------------------------------
答案14[推荐答案]:
引用 11 楼 dagouaofei 的回复:

我看到的现象似乎是read()方法在阻塞,但是为什么呢?附全代码如下:
ServerSocket server = null;
int port = 8080;
String host = "localhost";
int backLog = 1;
try{
server = new ServerSocket(port, backLog, InetAddress.getByName(host));
Socket socket = null;
InputStream in;
OutputStream out;
while (true) {
socket = server.accept();
in = socket.getInputStream();
out = socket.getOutputStream…

你一个字节一个字节的读,就把
Java code
byte[] buffer = new byte[2048];
这句去掉试试?

-----------------------------------------------------------------------
答案15:
mark 学习

-----------------------------------------------------------------------
答案16:
你向java2000_net提问,好像他没出来回答哦

-----------------------------------------------------------------------
答案17:
对呀,这个问题可能比较偏激吧。
其实我就是在想InputStream中的read方法的实现机制,应该是这样吧:
1,当读入流结尾,超时或抛出异常时,返回-1。
2,当流没有结尾也没有关闭时,阻塞。
我觉得应该是这样,但是疑问又来了,就是用带参数的read方法,就没有产生阻塞。
然而不带参数的read方法最终还是用的带参数的read方法:
Java code
public int read() throws IOException { if (eof) { return -1; } temp = new byte[1]; [color=#FF0000]int n = read(temp, 0, 1);[/color] if (n <= 0) { return -1; } return temp[0] & 0xff; }

这样我就不明白了,既然最终都用的一个方法,为什么不带参read就阻塞了,但直接用带参read方法,就不阻塞

-----------------------------------------------------------------------
答案18[推荐答案]:
读文件是这样吧 while ((i=in.read()) != -1) 。
怎么不这样读啊:
final char[] cbuf = new char[2024]; //字符缓冲数组
while(status){
int len = 0;
try{
len = br.read(cbuf,0,cbuf.length);
StringBuilder sb = new StringBuilder(len);
sb.append(cbuf, 0, len);
String str = sb.toString().trim();
System.out.println(str);
}...
}

-----------------------------------------------------------------------
答案19[推荐答案]:
参考这个吧

InputStream如何判断数据已经读取结束呢?


-----------------------------------------------------------------------
答案20[推荐答案]:
引用 4 楼 dagouaofei 的回复:

楼上您说的对,但我现在就想每次读一个字节试一下,结果就没成功,我想知道为什么.
StringBuffer request = new StringBuffer(2048);
int i;
try {

i = in.read();
while (i != -1) {
request.append((char) i);
i = in.read();

}

} catch (Exception e) {
e.printStackTrace();
}


我也象这样试过,也是读不了,不知道为什么?


而象这样就可以

StringBuffer request = new StringBuffer(2048);
int i;
try {

i = in.read();
while (i= in.read() != -1) {
request.append((char) i);

}

} catch (Exception e) {
e.printStackTrace();
}

-----------------------------------------------------------------------
答案21:
看了java2000_net的帖子,有点明白了。
但是还有个疑问,就是用read(byte b[])这个带参数的方法时,为什么没有产生阻塞?
我看了一下SocketInputStream的代码。
其中read不带参方法:
Java code
public int read() throws IOException { if (eof) { return -1; } temp = new byte[1]; [color=#FF0000]int n = read(temp, 0, 1); //这里调用了read(byte b[], int off, int length)[/color] if (n <= 0) { return -1; } return temp[0] & 0xff; }

还有read(byte b[], int off, int length)方法的代码:
Java code
。。。 try { [color=#FF0000]n = socketRead0(fd, b, off, length, impl.getTimeout());[/color] if (n > 0) { return n; } } catch (ConnectionResetException rstExc) { gotReset = true; } finally { impl.releaseFD(); }。。。

其中的 socketRead0是一个本地方法:
Java code
private native int socketRead0(FileDescriptor fd, byte b[], int off, int len, int timeout) throws IOException;

也就是说,使用阻塞的read()无参数方法和不阻塞的read(byte b[])带参数方法的区别,仅仅是最后调用本地方法socketRead0时的参数不同。
前者的参数b是在read()方法中定义的长度为1的byte数组,off和len分别为0,1,后者的参数是用户程序里传进来的。
仅此而已,一个阻塞了,另一个没阻塞,socketRead0是本地方法,方法体看不到,所以我很难下这个结论,为什么一个阻塞一个不阻塞?

-----------------------------------------------------------------------
答案22:
已经回复

-----------------------------------------------------------------------
答案23:
发送前加上长度就不会有问题了

-----------------------------------------------------------------------
答案24:
堵塞是因为没有缓冲区

-----------------------------------------------------------------------
答案25:
准备结帖了。经过一天来的研究,大致总结如下:
1,socket的getInputStream得到的是一个SocketInputStream类的实例,这个类中有三个read的重载方法,分别是:
Java code
public int read() throws IOExceptionpublic int read(byte b[]) throws IOExceptionpublic int read(byte b[], int off, int length) throws IOException

但前两个最终都会调用第三个read方法。
在第三个read方法中,会调用本类中的
Java code
private native int socketRead0(FileDescriptor fd, byte b[], int off, int len, int timeout) throws IOException;

这个方法需要说明一下,当其无法正确读取流的第一个字节(并非是因为流到了末尾结束)时,将会阻塞,等待client端再次输入;当可以正确读取第一个字节并且字节流长度大于len参数的大小时,它将把到达字节流长度时读取的字节存储在 fb 中并返回实际读取的字节数。

2,根据java2000_net的解释,socket读入的inputstream,不会主动结束,所以read方法总是不能返回-1,除非抛出异常或超时。
3,无论是带参数或不带参数的read方法,在接收socket的读入流时,只要不能正常读第一个字节时都会阻塞。

-----------------------------------------------------------------------
答案26:
不能正常读第一个字节时
答:是这个原因吗?什么叫做:不能正常读第一个字节.楼主能否解释一下呢?

-----------------------------------------------------------------------
答案27:
准备结帖了。经过一天来的研究,大致总结如下:
1,socket的getInputStream得到的是一个SocketInputStream类的实例,这个类中有三个read的重载方法,分别是:

Java code
public int read() throws IOException public int read(byte b[]) throws IOExceptionpublic int read(byte b[], int off, int length) throws IOException


但前两个最终都会调用第三个read方法。
在第三个read方法中,会调用本类中的

Java code
private native int socketRead0(FileDescriptor fd, byte b[], int off, int len, int timeout) throws IOException;


这个方法需要说明一下,当其无法正确读取流的第一个字节(并非是因为流到了末尾结束)时,将会阻塞,等待client端再次输入;当可以正确读取第一个字节并且字节流长度小于len参数的大小时,它将把到达字节流长度时读取的字节存储在 fb 中并返回实际读取的字节数。
例如字节流中有效字节数为2,fd的长度是4,off和len分别为0,4,这时候把字节流的两个字节填入fd的前俩个元素并返回2。

2,根据java2000_net的解释,socket读入的inputstream,不会主动结束,所以read方法总是不能返回-1,除非抛出异常或超时。
3,无论是带参数或不带参数的read方法,在接收socket的读入流时,只要不能正常读第一个字节时都会阻塞。
4,不能正常读取第一个字节,也许这么说不太贴切,我想表达的意思是第一个字节如果在流的有效字节之后,并且流没有结束,处于等待状态,例如:
写了这样一个client,
Java code
public class Client { static Socket server; public static void main(String[] args) throws Exception { server = new Socket("127.0.0.1", 8080); PrintWriter out = new PrintWriter(server.getOutputStream()); BufferedReader wt = new BufferedReader(new InputStreamReader(System.in)); while (true) { String str = wt.readLine(); out.println(str); out.flush(); if (str.equals("end")) { break; } } server.close(); }}

在server端修改代码,改变buffer的长度:
Java code
ServerSocket server = null;int port = 8080;String host = "localhost";int backLog = 1;try{server = new ServerSocket(port, backLog, InetAddress.getByName(host));Socket socket = null;InputStream in;OutputStream out;while (true) { socket = server.accept(); in = socket.getInputStream(); out = socket.getOutputStream(); StringBuffer request = new StringBuffer(2048); int i; [color=#FF0000]byte[] buffer = new byte[4]; i = in.read(buffer); //第一次读 System.out.println("第一次读"); i = in.read(buffer); //第二次读 System.out.println("第二次读");[/color] socket.close();}}}catch(Exception e) {e.printStackTrace();}

把buffer的长度改成了4,运行server和client程序,在client端输入"abcde"五个字符,则两次read都OK,第一个read读取了"abcd";第二个read读取"e",虽然buffer的长度是4,但是第一个字节"e"可以正常读取,所以正常读取并返回1。
但如果在client端输入"ab",则第一次read成功,读取了"ab"填入buffer的前两个元素中,但运行到第二个read时,因为输入流没有到结尾也没有结束,保持连接等待状态,所以无法正确读取第一个字节,这时候read方法阻塞住,等待client端再次输入。当client端再次输入"cde"时,第二个read方法正常读取返回3。
就像第1条说明的那样读取。


-----------------------------------------------------------------------
答案28:
mark

-----------------------------------------------------------------------
答案29:
String s = new String(buffer,0,i);

-----------------------------------------------------------------------
答案30:
引用 27 楼 dagouaofei 的回复:

准备结帖了。经过一天来的研究,大致总结如下:
把buffer的长度改成了4,运行server和client程序,在client端输入"abcde"五个字符,则两次read都OK,第一个read读取了"abcd";第二个read读取"e",虽然buffer的长度是4,但是第一个字节"e"可以正常读取,所以正常读取并返回1。
但如果在client端输入"ab",则第一次read成功,读取了"ab"填入buffer的前两个元素中,但运行到第二个read时,因为输入流没有到结尾也没有结束,保持连接等待状态,所以无法正确读取第一个字节,这时候read方法阻塞住,等待client端再次输入。当client端再次输入"cde"时,第二个read方法正常读取返回3。


答:楼主的总结,实际上什么都没有说明了.你上边的总结,就是read(..)的基本功能啊.即:read()的阻塞性读,这不是很正常嘛,与是不是第一个字节,一点儿关系都没有的.

我认为楼主反而忘记了一件十分重要的事件:(从你的程序代码中,明显反映了这个问题)

返回-1是由流的close()操作引起的.网络程序代码正规设计方式是:先关闭网络流,再关闭socket. 显然楼主对网络内部究竟是如何通信的(尤其TCP内部过程)是不清楚的.只能浮于表面的网络代码的设计.

以上仅供你参考

你可能感兴趣的:(inputstream.read() -1 问题)