故事
所有的事情都由一个失误引起。
一般前端请求服务端的某个服务,也就是执行某个方法,该方法都会返回一个字符串,用来页面转发。
如果不需要页面转发,可以return null;
但是我不小心return ""; return了一个空字符串。
这样的话,页面会跳转到自身,就比如你本身是index.html,结果还是跳转到index.html。
我需要服务端返回一个base64编码过的图片字符串,是一串很长的字符,大概50000+。
之前没有发现return "";的默认转发错误问题,但是报错了,报错如下。
也就是页面转发失败了,它说的是response已经被提交过了,而我拿到response的PrintWriter只是在里面写入了值,
并没有flush(),close()等操作。
它怎么就说提交过了呢?之前没注意这个错误,但是我把这个写入过程注释掉,报错消失了,
但是客户端拿到的返回值是整个indel.html的页面代码
识别结果内本身是接收返回结果的,但是我注释掉了,发现还是返回了页面代码,起初以为是Response的body不写,默认会返回页面源码。最后发现是retun "";应该写成 return null;
那么return “”“;
也就是
request.getRequestDispatcher("").forward(req,resp);
这个操作到底会发生什么?
应该是页面转发,通过在resp中塞入index.html的方式,让前端显示这个页面,所以这个转发发生,前端就拿到了indel.html的源代码。
而在转发之前,拿到response的PrintWriter,写入数据,只要不触发flush()方法,都会触发这个最后的转发操作。
而我在PrintWriter中塞入一个50000+的长字符串,提示了response已经提交过了,不能转发。
而塞入一个类似“abc”的短字符串,能够触发转发操作。
这个问题说明什么问题?
也就是我们在Response的PringWriter中塞入一个长字符串的时候,会迫使response提前提交,也就是后来知道的,它会触发flush()函数,把缓冲区内的数据输出。
当时我是不知道这个缓冲区的概念的,是比较抠脚的那种。
但是我拿到的信息是,往response中塞入长字符串会迫使response提前提交,而通过getRequestDispatcher()的方式来转发页面的方式不允许在转发之前提交response。
接下来的问题是:这个字符串要多长,才会让response提前提交?
经过在3-50000之间进行若干次2分法,创建对应长度的字符串的方法进行测试,最后得到答案是16384。
(这个时候有的朋友可能会说,这个博主脑子有问题吧!没有错,朋友,我的脑子确实是有点问题的。)
在当时的环境下,我只知道,字符串长度大于等于16384,都会迫使response提前提交,把转发操作无效化。
而16384到底是个什么东西?
16384是2的14次方,凡是和2的多少次方搞上关系的都是有故事的,所以16384绝对是个有故事的数字。
没有错,你看完这篇文章,会记住这个神奇的数字,16384!
通过搜索“16384+PrintWriter”的方式,没有查到什么有用的东西,但是渐渐了解到这个东西和缓冲区有关系。
接着我搜索“16384+缓冲区”,拿到的可能是最后答案的答案。
这个东西是Tcp收发缓冲区的默认大小,
通过在服务器上打开文件查看tcp发送缓冲区的默认值,键入
cat /proc/sys/net/ipv4/tcp_wmem
得到结果如下
根据可靠的小道消息,这个东西是tcp发送缓冲区的默认大小
也就是经过java的封装,我们的PrintWriter的发送缓冲区就是16384?而这个缓冲区对应这tcp的发送缓冲区....
也就是塞入这个大小了,PrintWriter就执行一波flush(),把缓冲区提交,这样response已经提交过了,也就不能转发了。
不知道我的故事说的好不好,我现在现出真身了!
我是个傻吊!没有错,你没有看错。
这个故事是关于宇宙起源的16384问题,困扰许许多多的科学家很多年,大家都在问PrintWriter的缓冲区大小是多大呢?但是鲜有人能够把这个答案回答出来。没有错,这个装逼的机会交给你,是16384!当然这个答案还不是很站得住脚,当时候你装逼失败,被人打一顿,不要告诉别人你是从我这里看到的,好吗?