欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

关于web请求中的getInputStream()和getReader()只能读取一次的原因

程序员文章站 2022-03-01 15:43:26
...
这里使用JAVA中的Socket写一个通过传输层TCP协议来向服务器进行request的代码

1、来一个没有bug的操作

  • 这里我们只展现server端的代码,客户端的request可通过浏览器直接发送至服务器
//看的懂的直接看这个吧
final int SIZE = 1024*1024*1024;	
byte[] buf = new byte[SIZE];
new ServerSocket(port:XXX).accept().getInputStream().read(buf);
//看不懂的可以瞟一眼这个
ServerSocket server = new ServerSocket(port:8888);	//端口号不唯一
Socket client = server.accept();					//将所访问的客户端的socket交给client
InputStream is = client.getInputStream();	//获取客户端发送的流

final int SIZE = 1024*1024*1024;	
byte[] buf = new byte[SIZE];
is.read(buf);

该Socket通过getInputStream()方法获得输入流InputStream,之后调用InputStream中的read()方法,将客户端发送的request请求存储在字节数组缓冲区buf中,到目前为止,由于我们的SIZE夸张地设置为1MB,所以一般来说足够一次性来存储request中的【请求行+请求头+请求正文】了。

2、开始来制造bug

  • 但如果SIZE设置的比较小的情况(一般的缓冲区大小也确实不会设置的很大),我们就需要通过循环语句来读取内容了。

这里我就不再创建一个更大的缓冲区来存储多次循环后的所有字节数组了,我们通过控制台输出的方式记录request的请求内容

final int SIZE = 32;

int len;
while( (len=is.read(buf)) != -1){		//这里的read(buf)其实是用read(buf,0,buf.length)进行封装重载的
	System.out.println(new String(buf,0,len));
}
System.out.println(“test1:”+"正常来说,在输出打印完所有的request内容后,这里应该是会被输出至控制台");

3、发现Bug

我们会发现test1的内容无法内输出,我个人运行完以上代码的情况是:
程序没有停止,而是在read()后全部数据后,就无法再往下运行了,debug的情况也是无法继续运行(但程序并未停止)
起初的感觉是线程被阻塞了,但我并未使用多线程,也看了一遍read()方法里面的源码,虽然里面有用到lock来互斥其他线程,但发现和这个无关,因此排除。

  • 这个bug总结起来就是read()方法无法被循环

4、揭露面纱

在使用其他InputStream的时候

final int SIZE = 32;
InputStream is = new 【OtherInputStream】();

int len;
while( (len=is.read(buf)) != -1){
	System.out.println(new String(buf,0,len));
}

以上代码反而没问题
甚至是我们在写聊天系统的Socket时,也都还没问题。

原因:

  • 原因在于web请求的流在网路中只传输一次,它从client端传输至server端的过程只传输一次,server端只能一次性接收并解析网路中传过来的数据包。
  • 我们比较<请求响应>和<聊天系统>,虽然正常来操作都是用的Socket进行代码编写,但是在写聊天系统时所写的代码中不会去掺杂应用层的HTTP/HTTPS协议,我们都是使用传输层的TCP/UDP协议进行编写后端代码。
  • 如果server端选择多次从流中读取,那么就会产生问题,因为网路中该流的多个数据包都没地方缓存。
  • 应用层方面,server端为了兼顾安全,不可能先去缓存client端发来的Request请求内容,再去解析/响应client端的Request是否合法
相关标签: 后端