一段数据在网络中的流动过程
@(计算机网络)
应用层:包括的协议有HTTP(web文档请求和传送)、SMTP(电子邮件报文的传输)和FTP(两个端系统之间的文件传送)。
运输层:包括的协议有 TCP和UDP。
网络层:包括的协议有IP协议,和路由选择协议。
链路层:简述一下,把链路层的分组称为帧,链路层和痛惜链路相关,有很多协议,如以太网、wifi和电缆接入网的DOCSIS协议。
物理层:物理层传输的是比特流,但也有其相关的物理层协议,如双绞线、电缆、光纤等等。
下载一个文件的流程
注意:
3次握手会发送没有请求体(即实体主体数据)的报文。
TCP 3次握手后建立tcp链接,才开始第一个带数据的报文(真正的包含请求体的报文)的传输。
实体主体即我们程序中的请求体,响应体与此雷同。
注意:
报文(应用层,即程序中你声明的请求对象,如Request对象,其包含RequestBody对象) 报文段(运输层)、数据报(网络层)、数据帧(链路层)、比特位(物理层)。
响应过程
- 响应端系统根据请求报文,使用http处理工具封装响应报文(包括ip地址,头,响应体数据等信息)。
- 一个大的响应体报文通过套接字到达运输层时会被分割成多个运输层报文段。
- 运输层的每个报文段发送到网络层后被分割成多个网络层数据报。
- 网络层数据报发送到链路层后被分割多个帧。
- 链路层的帧到达物理层会被打成比特位01(就是用高低电平的方式传输),每次能发送到物理层多少帧取决于网络的实际情况。
- 物理层将比特位一点一点的流向请求端系统,丢比特位的话数据将不完整。
- 一部分的比特位(如响应的报文总大小为2m,最前面流的是20kb)到达请求端系统的物理层。
- 物理层将这部分比特流传送到链路层。
- 链路层将这20kb的比特流封装成数据帧(如果不能封装成一帧的话,链路层可能会陷入等待状态直到能封装成一个完整的帧),链路层将封装的帧传送到网络层。
- 网络层将数据帧封装成数据报(假设多个帧可以组成一个数据包,否则可能会陷入等待阻塞的状态),网络层将数据包传送到运输层。
- 运输层将网络层数据报封装成报文段(假设多个数据报可以封装成完整的报文段,否则可能陷入阻塞等待),运输层将已到达的报文段通过套接字传送到应用层。
- 应用层http处理代码在应答成功时应答的第一部分(就是三次握手后包含code码的这个应答的部分)会形成一个响应报文对象(如okhttp中的Response),此对象一直存在,其中的response.body—>ResponseBody中的InputStream代表接收数据的管道,此时还没有在管道传数据,后面的应答中才会传输数据,报文段会以byte的方式到达管道中,可以从此管道中读取数据,如下图。
string:
请求体对象中的数据可以以string类型的方式返回,但返回string类型的方法会阻塞,因为它需要等待流通道(InputStream)将一个一个的分组接收并缓存到内存中,当所有分组接收完毕时,将缓存的分组的byte数据转换成string类型的数据返回,此时,请求体数据完整的接收了。bytes:
同1,只不过最后返回的是完整请求体的字节数组类型的数据。InputStream:
拿到body中的InputStream管道数据流对象。InputStream.read(byte[] bytes):
此方法将阻塞(如网络卡,数据断断续续的到达请求端系统,只要不是不到最后一个报文段,InputStream中如果没有数据的话read方法将阻塞等待直到可用数据的到达,然后将数据放到bytes中返回),阻塞时间过长可能会抛出异常;- +++++++ read方法负责从InputStream管道中读取数据,运输层的一个个报文段会通过套接字到达响应报文对象中响应体的InputStream中,会短暂缓存在InputStream管道中,像水流一样,read方法中的bytes变量像控制水阀,控制每次从管道中读取多少个字节,read方法每次从InputStream管道中读取一定量的数据并存放到bytes数组中,InputStream中暂时没有数据时请参看4。read方法返回-1时标识所有的报文段读取完成(最后一个报文段有一个标志位),整个文件已经完整的从响应端系统到达了请求端系统。
BufferSource:
okhttp中的流通道对象,同InputStream一样的东西,只不过重新封装了,其他的http网络框架应该也一样,都是用于接收字节流数据的。
public void onResponse(Call call, Response response) throws IOException {
if (response.code() == 200) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Response originalResponse = response;
long contentLength = (int) originalResponse.body().contentLength();
InputStream is = originalResponse.body().byteStream();//。响应体中的流通管道
byte[] bytes = new byte[512];
int total = 0;
int len;
//读完返回len
while ((len = is.read(bytes)) != -1) {
total += len;
// 计算已下载的百分比
int finalTotal = total;
int finalLen = len;
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("shimyProgress", "*" + "--len:" + finalLen + "--total:" + finalTotal + "--content:" + contentLength
+ "--百分比:" + (finalTotal * 100) / contentLength + "%");
}
});
}
} catch (IOException e) {
Log.i("shimyProgress", "抛异常了");
}
}
}).start();
} else {
Log.i("shimyProgress", "code:" + response.code());
}
}
无论是请求过程还是响应过程都像是从一个坑的水向另一个坑用水管转移,网络及程序处理会影响其流速度。