问题

今天开始尝试用 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. Django中ORM的使用

    Django中ORM的使用 ORM orm(object-relation-mapping)对象关系映射,即用对象来表示关系数据库中的表: 类 --> 表, 对象-->一行数据 对象的属性 ...

  2. 这嘎哒真TM那啥!Python版东北话编程火爆网络

    还记得那个刷爆朋友圈的那个文言文编程语言么? 这个项目是一位名为Huang Lingdong的大四学生创建的,当时,就连中科院计算所研究员.机器翻译领域知名专家刘群老师都赞叹道: 后生可畏 近日,Gi ...

  3. 每天学习一点ES6(二)let 和 const

    let 命令 let 和 var 差不多,只是限制了有效范围. 先定义后使用 不管是什么编程语言,不管语法是否允许,都要秉承先定义,然后再使用的习惯,这样不会出幺蛾子.以前JavaScript比较随意 ...

  4. C#中更改DataTable列名的三种方法

    解决办法 直接修改列名 dt.Columns["Name"].ColumnName = "ShortName"; sql查询时设置别名 select ID as ...

  5. Pygame的简单总结

    Pygame learn from mooc 私货:在调用函数时,可以 1.import tkinter (不过在使用时还要加前缀如tkinter.Tk()) 2.import tkinter as ...

  6. 使用@Cacheable注解时,Redis连不上,直接调用方法内部的解决方案

    最近redis 域名一致解析错误,导致业务多了很多异常.那么如何在这种情况下直接访问数据库,而不是报错呢 1. 解决方案 其实很简单,在配置 redis 时,只需要多一项配置,继承 CachingCo ...

  7. easyui中加载table列表数据 第一次有数据第二次没有数据问题

    $('#allUsingProductTable').datagrid({  加载数据时,第二加载时table会发生变化会出现找不到问题.如果是弹框没有影响,弹框出现出现列表每次都会执行销毁方法. 解 ...

  8. JSP 的 4 种作用域?

    page:代表与一个页面相关的对象和属性. request:代表与客户端发出的一个请求相关的对象和属性.一个请求可能跨越多个页面,涉及多个 Web 组件:需要在页面显示的临时数据可以置于此作用域. s ...

  9. HashSet/HashMap 存取值的过程

    HashSet与HashMap的关系: (1)HashSet底层用的是HashMap来实现的 (2)这个HashMap的key就是放进HashSet中的对象,value就是一个Object类型的对象 ...

  10. TurtleBot3 Waffle (tx2版华夫)(2)系统安装

    Tx2系统默认是安装好的,由于镜像文件大于20G,无法上传百度网盘,所以如有需要请联系我们客服:下面主要是操作步骤: 2.1.准备工作 a.准备好利用Jetpack刷过机的Ubuntu的主机(HOST ...