18 服务器接收数据过程
书接上文。接下来分析服务器接收数据的过程,接收方法如下:
Connection类
120行,设置本方法运行的超时时间,若本方法在超时时间内未完成,将通知上层应用超时了。
121行,从channel读取数据到inbuf中。inbuf成员变量,初始化为
private ByteBuffer inbuf = ByteBufferStream.allocate(1024);
122行,若返回值n<0,说明读到流结束标记,此后便断开连接,
否则n表示本次读取了多少字节的数据到inbuf中。
初始时dataLength为-1, headerLength=4,条件
用来确定要接收的数据长度是多少。
客户端发送数据的格式为 :
长度(4字节,最高二进制位为1,即读出后为负值) + id(4字节) + 内容
若inbuf.postion() >= headerLength成立,说明已读取了至少4个字节,而此时可以确定长度已接收了,代码
130行,读取数据长度dataLength, 若 dataLength
=headerLength,说明已接收到id字段,
150行,从索引4读取id。
至此,通过接收到的数据已确定了长度,id,即确定了head部分,但内容部分还没有确定。
由于没有读完内容部分,接下来会继续从channel中读取数据并解析。但有个问题,由于
inbuf容量初始化为1024,若数据长度dataLength超出了1024,Inbuf装不下,怎么办?此时,能想到的就是重新分配一段空间,然后往新的空间读取数据。
135行,判断 头部+数据部分长度是否超出了inbuf的容量,是的话,重新分配一段内存,然后
将老内存内容拷贝到新内存上,同时释放老内存,最后inbuf指向新分配的内存。
inbuf空间可能不够的问题解决了,接下来就是将剩余的内容读入了。
怎样判定内容部分读完了?由于数据长度dataLength确定了,若
inbuf.position() >= headerLength + dataLength表示已读完。 inbuf存的是从channel接收的实际数据,inbuf.position()可以表示已读取实际数据的长度,而这个实际数据包括了头和内容。即实际读到的数据长度已经超过了 headerLength +dataLength,就表示内容已读完。
152行,判断数据是否读完,需要在dataLength>=0的前提下。
数据读完了,也就是已读到了一个完整的数据包,此时,就需要把这个数据包独立出来。
154行,分配一个长度为dataLength的内存data。
156行,设置inbuf的当前位置为 headerLength, 因为此位置开始存的才是实际数据。
159行,将inbuf中从position位置开始到limit间的数据拷贝到 data中,即将实际的内容数据读取到data中,而此时data中已存储了一个完整的数据包。
163行,将data通知到上层逻辑,由上层逻辑去处理。
接下来还原headerLength为4,dataLength为-1,id为null,即将变量还原成初态,为接收下个数据包作准备。
以上看起来没有问题,但似乎遗漏了什么。
一个完整包data是从inbuf中解析出来了,但Inbuf中只会包含一个完整的包吗?有可能inbuf中包含了一个完整的包,并且包含下一个包的部分数据。此时inbuf数据处理要做好,即解析出data包的时候,inbuf原有的剩余数据还不能扔掉,否则会造成数据丢失。
157行,记录下了inbuf的原有长度。
160行,将inbuf的数据拷贝到data后,重新恢复了limit,
161行,调了compact(),即将 inbuf从当前postion到limit间的数据拷贝到了inbuf的开始位置,即拷贝到索引0处开始的位置。
当将inbuf数据拷贝到data后,inbuf的当前位置刚好就是inbuf剩余数据的第一个字节处。
此时从inbuf当前位置,即剩余数据的第一个字节处拷贝到inbuf的开始位置,就保证了剩余数据不会丢失,从而为解析下一个包作了准备。
到此,一个完整包的接收过程已介绍完毕。
接下来,需要看看服务器如何去处理这个包。
留言与评论(共有 0 条评论) |