inputstream.read() -1 问题

本文探讨了从socket inputStream转换为字符串的过程,重点分析了字节流在这一过程中扮演的角色。通过对比在读取文件输入流与socket输入流时的不同表现,揭示了为何在socket输入流中read()方法返回值不会为-1的原因。文章深入解析了Java中InputStream类的read方法的实现机制,特别是针对socket InputStream的特殊行为进行了详细阐述。最后,文章总结了在处理socket输入流时的几个关键点,帮助读者更好地理解和解决相关问题。
问题:
问题是关于从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.youkuaiyun.com/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内部过程)是不清楚的.只能浮于表面的网络代码的设计.

以上仅供你参考
### Java 中 `InputStream.read` 的使用方法及常见问题 #### 方法说明 `InputStream.read()` 是 Java IO 流中的基础方法之一,用于从输入流中读取单个字节的数据。该方法会返回一个整数值,范围为 0 到 255(表示字节的无符号值),如果到达流的末尾,则返回 `-1` 表示结束[^4]。 以下是其常见的几种重载形式及其功能描述: 1. **`int read()`**: 从输入流中读取下一个字节并作为 int 值返回。如果没有更多数据可读,则返回 -1。 2. **`int read(byte[] b)`**: 将最多 `b.length` 字节数组填充到数组 `b` 中,并返回实际读取的字节数量;如果已达到流末尾则返回 -1。 3. **`int read(byte[] b, int off, int len)`**: 从输入流中读取至多 `len` 个字节存储到缓冲区 `b` 中,起始位置偏移量为 `off`,返回实际读取的字节数量或 -1 如果已经到了文件结尾。 #### 示例代码 下面展示如何利用这些方法来实现简单的文件读取操作: ```java import java.io.*; public class InputStreamExample { public static void main(String[] args) throws IOException { byte[] buffer = new byte[1024]; try (InputStream is = new FileInputStream("example.txt")) { int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { System.out.write(buffer, 0, bytesRead); } } } } ``` 此例子展示了通过循环调用 `read(byte[])` 来逐步处理大文件的内容[^1]。 #### 常见问题分析 1. **阻塞行为** 当尝试从网络套接字或其他慢速设备读取时,可能会遇到长时间等待的情况即所谓的“阻塞”。这是因为底层实现了同步机制,在未收到任何新数据前一直保持挂起状态直到超时发生或者连接关闭为止[^5]^。 2. **编码错误** 对于文本型数据来说仅仅依靠原始字节序列并不足以正确解析字符串内容因为不同平台之间可能存在差异化的默认字符集定义所以建议总是显式指定所需的Charset对象实例给相应的构造器比如 InputStreamReader(InputStream in , Charset cs). 3. **资源泄漏风险** 忘记及时释放打开过的流可能导致内存泄露等问题因此推荐采用try-with-resources语句自动管理生命周期从而减少潜在隐患[^2]. ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值