- 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
- 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
java基于TCP的socket數据包拆分方法
java基于TCP的socket数据包拆分方法
好了,现在轻松许多。话说看到falcom官方的《空轨》动画时间表,又看到崩坏的人设,我表示真的非常不能接受。当然了这个咱也管不着。
好了话归正题,前不久写的socket程序,服务器是java的,客户端是flex。一开始就想过所谓的拆分数据包的问题,因为数据包结构是自己定义的,也简单的写了几行数据包的验证。关键是测试中完全没有发生什么情况,但是发布到外网之后却出现一些非常奇怪的问题,典型的就是通信过一定时间之后,数据包验证那块就会出错然后就抛弃了数据包,这就是所谓的“丢包”吧,但是我的是TCP的socket,所谓因为网络问题导致的数据包没有发送与接收成功这种问题应该是不可能出现的。
于是看了几篇文正,发现这种现象被称作“粘包”,我觉得还是挺贴切的。经过一定时间的思考、和测试,大概了解了其中的原理,按照现在的此时情况来看,应该是没什么问题。于是在此总结一下,如果哪天我发现一些新问题或更好的方法,还是会来继续补充这篇文章的。当然各位路过的前辈觉得其中存在错误什么的也请指出。
首先在将程序之前,还是先说一下TCP的通信。TCP和UDP的最大区别就是TCP维护了连接状态,而这个状态我们可以理解为一个畅通的流通道,即stream,当然流的传输内容归根结底还是byte。于是将流的通信进行假设,假设存在一条引水管道,从远方输水过来,我们在这边等待水的到来,并使用容器接收流出来的水。
此时存在一下几种情况:
假设这个输水管道在操作过程中不会断掉。
先进先出,先流进管道的水一定是先到达。
某一状态,(在输水管没有断掉是)流量无法保证,甚至某段时间没有水。
我们的容器(缓冲区)大小固定,即每次接收的水量存在最大值,超多将无法接收。
在以上情况作为前提,再回归编码。TCP的socket可以通信的前提是连接没有断开,连接断开事件可以从两种情况进行判断。流断开,这read()为-1,SocketException或者IoException。分包的前提是socket可以正常通信,不论网络延时多么严重,这些TCP协议会去处理。我们仅仅关心通信既可。现在最优情况,即实验室环境,或者是内网,服务器与客户端延时不会大于一毫秒,此时只要我们接收输水的容器够大,基本就可以完成正常通信。
但是互联网情况就非常复杂,数据包要经过无数网络软硬件设备,延时不可避免,但是TCP协议会像输水管道那样,保障数据包的顺序和保证不会丢失。所以这时我们可以控制的只有接水的容器。查看一些简单的TCP通信的知识,网络数据在传输的时候存在缓存现象,简单的说,就是连续发送N个数据包。他们可能被缓存起来一起被发送。这种情况就是粘包,当然对于接收端来说,我们不能保证 每次都能正好的完整的接收数据包,更多时候是x.5个数据包。
再次回到输水的模型,我们的容器等待水的到来,现在存在超时时间即每次等待水来有一个最大时间,超过这个时间,即使没有接到一滴水我们也要处理这个水桶。所以我们得到水桶的水理论上是大于等于0,小于等于水桶的容量。我想这样说应该可以很清楚的表达清楚了吧。现在开始从代码角度来说。
现在我们有一个byte[] buffer = new byte[MAX_LEN],即数据包读取缓冲区,int len = connection.read(buffer)。read方法使用buffer读数据流,len为实际读出的数据长度,此长度大于等于0,小于等于MAX_LEN。等于0自然不去处理,等于-1认为连接断开,当然read方法会抛出异常,即当读取数据过程中,连接出错。
现在我们获得一个buffer,即缓冲区。里面存在len长度的可用数据。我们要做的就是根据自己的协议结构将这个buffer转化为遵循我们自己的协议的packet。进而交由后面的业务逻辑代码处理。
此时我们定义自己的通信协议一个byte的包头,用于数据吧合法性验证,两byte数据包长(一般用4byte,即一个int),剩下内容为可变长度的数据包体。现在我们拿到buffer,这时候就有分包(粘包),和组包(数据包没有接收完整)两种情况。感觉似乎比较头疼,但是实际上获得packet我们紧紧需要知道的是数据包的真实长度,即2byte的内容,转为short后假设为PACKET_LEN。然后我们只要拆分和等待PACKET_LEN个长度的byte即可,那才是我们班真正需要的东西。当然,这个过程我曾经陷入过误区,然后经人指点后才发现我关注了很多没用的东西,结果增加了代码的复杂度。之后就上代码了,现在我的结构是服务器使用nio,然后nio框架将buffer封装为java.nio.ByteBuffer。其底层实现还是固定长度的byte[],它做的仅仅是封装了一些byte操作的快捷方法而已。既然它封装了,我们
文档评论(0)