昨天看了篇写的很棒的关于socket缓冲区的讲解。

socket套接字及缓冲区详解@lx青萍之末

android socket send缓冲区 socket数据缓存和处理_输入缓冲区

引自其博客。 

上图反应的是客户端和服务端数据传输的模式图,主要说明的是,客户端 socket 通过write()方法写出去后,并不是直接在网络中传输数据,而是将数据写入缓冲区然后内核协栈将缓冲区的数据通过TCP协议发送到对端主机的输入缓冲区注意这个过程不受程序控制,因为将数据写入输出缓冲区时,函数已经返回)。数据从接收端的缓冲区输入程序缓冲区,这个过程由类似read的方法完成。

我主要想说的是,按接收端来说(发送端配套使用),用缓冲流 以及 本身InputStream is = socket.getInputStream 作为输入的流 和 数据流DataInputStream的区别。

为什么缓冲流和本身的流 is 一块说呢,首先我们由上面的图解知道,本身的流在抵达程序之前是有输入缓冲区的,我们通过类似 read 的方法读取数据,此时相当于一根管子套在输入缓冲区上,等数据从发送端的输出缓冲区完全抵达接收端的输入缓冲区,一次性读取出来,数据还从输出缓冲区到输入缓冲区路上的过程,read 方法只能阻塞,暂时“ 等它 ”。因此我们知道本身的输入流 虽是InputStream的子类,没有明确的指明是Buffered的类,却是具有缓冲性质的。通过转换流和BufferedReader 的处理,我们可以调用readLine()方法,很方便的读取一行数据出来(本身流也是可以读取全部的数据,只是比较复杂)。所以说缓冲流和本身的流都可以按照上图解释。

然而,问题就是,请各位带入一个场景,我在客户端输入字符,在服务端要输出到控制台上,单次还是很容易实现的,但是我的诉求是一直输入字符,一直输出到控制台上,期间没有客户端的关闭。这就困扰了我好久,因为你输入了一行字符,按下回车,没有及时的输出到控制台上,直到客户端while循环判断条件不符合,退出循环,关闭客户端close之后,才将堆叠的字符输出到控制台上。具体什么样子呢,比如我第一行输入Hello接着按下回车,服务端控制台一看没有输出,再输出 Server按下回车,控制台仍然没有输出,然后输入空行退出循环,程序结束。这时控制台上赫然的一个HelloServer根本不是想象中的Hello然后下一行Server。

其实按照上面的图解很好说的通,你输入了字符都传输到了输出缓冲区,把你的整个数据拼凑起来打个包全部发送到接收端。这样好理解啊,省了电费不是,效率还上来了不是?你每输入一行字符,按下回车都是一个个小数据包,一个个小数据包发送到目标机器,必然没有打成一个大包发过去来的高效。所以会造成Hello回车Server回车 拼接为HelloServer而不是Hello下一行Server了。

用了 DataInputStream 之后,显然这不是一个缓冲流。它传输的时候模式图就不一样了,用他的writeUTF(String str)写出去之后接收端再readUTF(String str)并不会出现HelloServer这样的情形,而是Hello下一行Server,完全符合了我的要求。

然后底下是我的猜想,用了数据流DataInputStream之后,你每次发送的一段字符都是一个数据小包,发送过去,接收端再读取,显示到控制台上。直到退出循环,程序结束。数据到到达输出缓冲区之后,会直接变成一个数据小包发送到目标机器,目标机器再读取,输出缓冲区的数据不会再等所有数据全部输入完成,打成一个大包,传输到目标机器。输入缓冲区的数据也不会等所有的数据都到这里,才会被读取了,应该是有一个数据小包读取一个数据小包,因为下一个字符串,我还没有输入,更没有到目标机器的输入缓冲区这。这样的话,模式图里的输入、输出缓冲区也不能叫做缓冲区了,只能叫输入、输出区了,不具备缓冲的性质。这样的话,便能解释了。

大一学生,由于Java刚入的门,学到web才发现前面的基础很不牢固,因此重新拾起来再学。还万望贤者予以斧正!

                                                                                                                         2021.8.13