问题

今天开始尝试用 Java 写 http 服务器,开局就遇到 Bug。

我先写了一个多线程的、BIO 的 http 服务器,其中接收请求的部分,会将请求的第一行打印出来。

下面是浏览器发出的请求和控制台的输出情况。我们竟然收到了一个空的请求!!这是为什么呢?

我解析请求的部分代码如下。

// request
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] buffer = new byte[bis.available()];
int len = bis.read(buffer);
String firstLine = new String(buffer).split("\n")[0];
System.out.println("".equals(firstLine) ? "EMPTY" : firstLine);

这是为什么呢?我们先打个断点看看是个什么状况。

刷新浏览器,重新请求一下试试。惊奇的发现,bug 消失了。。

如果把断点打在后面几行,bug 又出现了。

emmm,太迷了。

分析

我们仔细看看上面第二个断点的截图,我们会发现 buffer 的值为 {},这意味着申请的空间是 0!这就意味着 available 返回的是 0。

这个函数的注释中写到,返回一个可以被读取的字节数的估计值。这个估计值并不是实际的长度,在网络请求中,这个值有可能是 0,因此,我们申请的空间就是 0,之后调用 read 方法自然也读不到任何东西。

调用链是这样子的:

解决方法

我们自己开一个固定大小的缓冲区,然后读取就好了,read 方法会阻塞直到数据流进来。

// request
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] buffer = new byte[BUFFER_SIZE];
int len = bis.read(buffer);
System.out.println(new String(buffer).split("\n")[0]);

那这个方法就没有问题了吗?其实还是有问题的,比如 POST 提交文件的时候,显然文件的大小并不是只有 BUFFER_SIZE 那么小而已。我们还需要想办法来获取一个未知长度的 InputStream。未知长度的处理方法,可以使用 timeout 来做,如果过了一段时间还没有读入数据,那就不读了。比较稳妥的方法是,在请求头里面写好长度,这样就能知道要读多少了。

请谨慎使用 avaliable 方法来申请缓冲区的更多相关文章

  1. 请谨慎使用 @weakify 和 @strongify

    来源:酷酷的哀殿 链接:http://www.jianshu.com/p/d8035216b257 前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很 ...

  2. login控件“您的登录尝试不成功。请重试”的解决方法

    原文:login控件"您的登录尝试不成功.请重试"的解决方法 遇到login控件“您的登录尝试不成功.请重试”报错之后,在网上找了很久,也按照如下帖子设置了 application ...

  3. [HMLY]13.请谨慎使用 @weakify 和 @strongify

    前言 相信大部分见过 @weakify 和 @strongify 的开发者都会喜欢上这两个宏.但是很多人只知道它的强大威力,却没有意识到在特定环境下的危险性. 本文将通过代码测试的方式告诉读者,如何正 ...

  4. linux fork进程请谨慎多个进程/线程共享一个 socket连接,会出现多个进程响应串联的情况。

    昨天组内同学在使用php父子进程模式的时候遇到了一个比较诡异的问题 简单说来就是:因为fork,父子进程共享了一个redis连接.然后父子进程在发送了各自的redis请求分别获取到了对方的响应体. 复 ...

  5. 送大家几个cnblogs号,供快捷评论,请谨慎使用

    欢迎评论& 3461896724@qq.com互动 可以在我的首页看更多 #1先送大家几个号:(密码都是 MLdlight2020)请区分大小写(可以直接复制) 写过一篇 免费验证码接收网站& ...

  6. ios更新UI时请尝试使用performSelectorOnMainThread方法

    最近开发项目时发现联网获取到数据后,使用通知方式让列表刷新会存在死机的问题. 经过上网查找很多文章,都建议使用异步更新的方式,可是依然崩溃. 最后尝试使用performSelectorOnMainTh ...

  7. 谨慎重载clone方法

    本文涉及到的概念 1.浅拷贝和深拷贝 2..clone方法的作用和使用方式 3.拷贝构造器和拷贝工厂   1.浅拷贝和深拷贝 浅拷贝 一个类实现Cloneable接口,然后,该类的实例调用clone方 ...

  8. 喜提JDK的BUG一枚!多线程的情况下请谨慎使用这个类的stream遍历。

    你好呀,我是歪歪. 前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候,看到一个 pr,虽说是在 RocketMQ 的地盘上发现的,但是这个玩意吧,其实和 RocketMQ 没有任何关系. ...

  9. 关于discuz“终于解决“头像保存过程中发生网络错误,请重试"”的解决方法

    1 php.ini里面allow_url_fopen = On2 将php.ini中的;upload_tmp_dir = 该行的注释符,即前面的分号“:”去掉,使该行在php.ini文档中起作用.up ...

随机推荐

  1. 主数据管理(MDM)的6大层级简述,你不可不知的数据治理参考!

    前面我写了一篇关于对元数据和元数据管理的认知和理解的文章,有兴趣的朋友可以去看看.接下来我们讲一讲主数据管理(MDM). 主数据管理(MDM) 主数据是系统间共享数据,它是系统间信息交换的基准.主数据 ...

  2. linux操作系统及常用命令

    GUN:GUN is Not UnixGPL:General Public License.通用公共许可证,版权 Copyright,Copyleft 开源协议LGPL:lesserGPLv2GPLv ...

  3. (四)linux的常用环境变量及设置

    一.为什么要设置环境变量 1.环境变量能解决什么问题? 你是否经历过输入$python命令后,屏幕上打印出python:command not found的尴尬:每一次都要输入$/home/tools ...

  4. VC维相关知识

    假设空间H(Hypothesis Set) 输入空间D(X1...Xn) 1.增长函数(grown function) 是关于输入空间尺寸n的函数 假设空间对于D中所有实例实现分类(赋予标记)的分类方 ...

  5. 容器编排系统K8s之访问控制--RBAC授权

    前文我们了解了k8s上的访问控制机制,主要对访问控制中的第一关用户认证做了相关说明以及常规用户的配置文件的制作,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/ ...

  6. 华为存储18500 V5配置SNMP

    配置流程 配置SNMPV1/V2C 配置端口 admin:/>show snmp port SNMP Listening Port : 161 admin:/>change snmp po ...

  7. 关于BackTop按钮的实现

    今天在处理,首页面的制作的时候,在实现backtop按键的时候,有些思路,作为记录. 功能为,点击backtop即可,立马跳到首页的最上面,且backtop只有在页面后1/2处才显示出来. 首先,我们 ...

  8. Servlet3.0提供的@WebServlet注解引用参数详情介绍

    Servlet3.0提供的@WebServlet注解: servlet3.0所提供的@webservlet注解,用来将某个类注解为一个servlet类,简化了web.xml上的servlet配置, @ ...

  9. Spring Cloud Sleuth链路监控应用(十四)

    https://docs.spring.io/spring-cloud-sleuth/docs/2.2.5.RELEASE/reference/html/ 一.Sleuth介绍   为什么要使用微服务 ...

  10. Asp.net Core 2.0 实现Cookie会话

    与1.0版本相比微软做了一些调整.详细请参考官方文档,我这里就讲2.0的吧 1.首先要在 根目录下 Startup.cs 类中启用 cookie会话,有两处要配置 第一处在  public void ...