问题

今天开始尝试用 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. 【Go语言绘图】图片添加文字(二)

    这一篇将继续介绍gg库中绘制文字相关的方法,主要包括:DrawStringAnchored().DrawStringWrapped().MeasureMultilineString().WordWra ...

  2. Python进阶学习_连接操作Redis数据库

    安装导入第三方模块Redis pip3 install redis import redis 操作String类型 """ redis 基本命令 String set(n ...

  3. Unity UI适配 之 GridLayoutGroup组件下的内容适配(进度条适配)

    好久没有更新博客了,蓝廋啊. 今天写一写关于GripLayoutGroup组件的屏幕适配问题,以在ARPG游戏中常用的经验条适配来举例子,以此来加深自己的记忆,以便在下次需要制作该功能时能够快速完成. ...

  4. 由innodb锁引起的数据库相关

    innodb 锁的问题 1.事务 原子性:要么成功,要么失败 一致性:前后数据保持一致状态 隔离性:多个事务并行,相互不影响 持久性:事务提交之后,对数据的影响是永久性的,即使故障也可以保持. 2.并 ...

  5. laravel 数据库之DB类

    // 取回数据表的第一条数据 DB::table('table')->where('key', 'value')->first(); DB::table('table')->firs ...

  6. maven项目添加jar包

    使用集成工具创建一个maven项目,如果需要添加开发包,只需去maven仓库找到对应的包,将配置信息加入pom.xml文件即可.这样,我们就再也不用到处寻找和下载jar包了. 用例:开发工具 STS ...

  7. 简述java多态

    一.多态性: 1.java实现多态的前提:继承.覆写: 2.覆写调用的前提:看new是哪个类的对象,而后看方法是否被子类覆写,若覆写则调用覆写的方法,若没覆写则调用父类的方法: 二.多态性组成: 1方 ...

  8. 最全总结 | 聊聊 Python 办公自动化之 PPT(中)

    1. 前言 上一篇文章简单地介绍了 PPT 的文档结构,并使用 python-pptx 这个依赖库完成对 PPT 文档最基本的操作 最全总结 | 聊聊 Python 办公自动化之 PPT(上) 作为 ...

  9. TurtleBot3使用课程-第四节(北京智能佳)

    目录 1.机器学习 2 1.1 机器学习一 2 1.1.1 目标 2 1.1.2 操作环境 2 1.1.3 设置 2 1.1.4运行(它需要超过几秒取决于PC) 3 1.1.5运行屏幕 3 1.1.6 ...

  10. Logstash-input-jdbc同步mysql数据到ES - sql_last_value

    问题:使用 Logstash-input-jdbc同步mysql到ES, 当中使用了 sql_last_value ,如何重新同步? 答: logstash把上一次同步的最后一条记录id写到 c:\用 ...