处理粘包和半包问题的socket分包Java实现

流子 服务器负责人  2008-07-22 11:16:57
加精
只知道原理,代码实现还不知道怎么实现?请高手指点,谢谢!高分馈赠!
...全文
8169 点赞 收藏 66
写回复
66 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
xiaogezq0 2012-08-02
解决了我很大的问题!代码注释能不能相信些呢,谢谢了!这个问题我弄了一周多了!头疼啊!
回复
klcwt 2010-09-28
解决方法:
1.定义自己的协议
2.
byte[] byteArray = new byte[len];
int actualReadNum = inputStream.read(byteArray, off, len);

if (actualReadNum == len) {
return byteArray;
}

int readNum;
while (actualReadNum < len) {
readNum = inputStream.read(byteArray, actualReadNum, len
- actualReadNum);
if (readNum == -1) {
throw new IOException("读取失败!");
}
actualReadNum = actualReadNum + readNum;
}
return byteArray;
}
回复
RommelZhan 2010-04-29

  
  分包算法
  针对三种不同的粘包现象,分包算法分别采取了相应的解决办法。其基本思路是首先将待处理的接收数据流(长度设为m)强行转换成预定的结构数据形式,并从中取出结构数据长度字段,即图5中的n,而后根据n计算得到第一包数据长度。
  1)若n<m,则表明数据流包含多包数据,从其头部截取n个字节存入临时缓冲区,剩余部分数据依此继续循环处理,直至结束。
  2)若n=m,则表明数据流内容恰好是一完整结构数据,直接将其存入临时缓冲区即可。
  3)若n>m,则表明数据流内容尚不够构成一完整结构数据,需留待与下一包数据合并后再行处理。
  对分包算法具体内容及软件实现有兴趣者,可与作者联系。
回复
RommelZhan 2010-04-29
粘包后,分包的具体实现如下

// 定义消息接受缓冲区
byte[] buf = new byte[4096];
// 真正用来放数据的缓冲区,要有两个包长度的大小
byte[] tmp = new byte[4096 * 2];
// tmp缓冲区中实际数据长度
int len = 0;
// 本次解析中使用了的缓冲数据长度
int usedlen = 0;
// 本次接收包的真实长度
int recvLen = 0;
try {
// 等待接受消息
while((recvLen = skt.getInputStream().read(buf)) > 0) {

// 解析获取到的消息
ClientFileInfoPack pack = new ClientFileInfoPack();
TcpDataHeader headerRecv = (TcpDataHeader)pack.getHeader(buf);
// 如果包中有上次解析剩下的余留数据,就先处理
if(len > 0) {
// 如果长度小于通讯时候定义的头长度(我们的是26个byte)
if(len<26) {
// 不够头长度,就读现在的包
System.arraycopy(buf, 0, tmp, len, recvLen);
len += recvLen;
// 再判断是不是够一个头的长度,够了就分析这个头里面描述数据体长度的字段
if(len>=26){
headerRecv = (TcpDataHeader)pack.getHeader(tmp);
if((recvLen + len) < headerRecv.length()) {
continue;
}
}
} else if (len >= 26) {// 如果余留的数据就超过头长度
// 解析这个头, 然后读数据看看有分半包和粘包的情况
headerRecv = (TcpDataHeader)pack.getHeader(tmp);
if ((recvLen + len) < headerRecv.length()) {
System.arraycopy(buf, 0, tmp, len, recvLen);
len += recvLen;
continue;
} else {
System.arraycopy(buf, 0, tmp, len, recvLen);
len += recvLen;
}
}
} else {
// 没有余留数据,那么就直接解析先在接收到的包
System.arraycopy(buf, 0, tmp, len, recvLen);
len += recvLen;
if(len>=26) {
headerRecv = (TcpDataHeader)pack.getHeader(tmp);
if(len < headerRecv.length()) {
continue;
}
} else {
continue;
}
}
// 实际本包长度
usedlen = headerRecv.length();
//////////////////////////////////////////////////////////////////////////////////////

// 彻底解析消息
MSG msg = pack.parse(tmp);
// 先判断有没通讯异常,我们规定30000以上的是网络通讯异常
if(msg.getErrorCode() > 29999) {
ClientFileInfo cfi = new ClientFileInfo();
cfi.setErrorCode(msg.getErrorCode());
infos.add(cfi);
return infos;
}
// 把剩余的多余的移到缓冲区头部,因为这必然是下一个包的开始
for(int i=usedlen; i<len; i++) {
tmp[i - usedlen] = tmp[i];
}
//////////////////////////////////////////////////////////////////////////////////////
// 因为解析了一个包,所以要减去这个包的长度
len -= usedlen;
//////////////////////////////////////////////////////////////////////////////////////
回复
everlasting188 2010-04-12
讨论的的还不错!
回复
jianren719 2009-12-28
学习了 mark....
回复
coastcdl 2009-02-24
mark...
回复
冰思雨 2008-08-04
路过,没有什么意见。
回复
南南北北 2008-08-03
学习.
回复
流子 2008-08-03
更新:采用了缓冲输入/输出流来包装输出流,再采用数据输入/输出输出流进行包装,加快传输的速度。最近把包装类换成了DataInputStream和DataOutputStream,替换了以前的BufferedReader和PrintStream,字符传送改成了字节传送方式,并发现有个方法可以取代我写的递归读recursionRead()的方法,readFully,参看了一下API说明:
void
readFully(byte[] buf)
读取字节,同时阻塞直至读取所有字节。

void
readFully(byte[] buf, int off, int len)
读取字节,同时阻塞直至读取所有字节。
包装如下:

in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
out = new DataOutputStream(socket.getOutputStream());


满足我们以前的需求!preferme ,kokobox 以及大家觉得合适吗?
回复
kocr 2008-08-01
[Quote=引用 49 楼 yizia 的回复:]
引用 47 楼 jiangguilong2000 的回复:

1.你的回复表示了你连帖子的意思都没看懂,看看其他人回复的吧,特别是preferme ,kokobox ,TinyJimmy==回复的,他们的回复的内容就是帖子所需要的答案。
2.你这样的回复根本没有任何意义,就好像我们现在问向量机,正交矩阵的问题,然后你跳出来说这都是大学里学的东西,还好意思来这里问。…


同意你的看法.

我当时只是一惊,怎么学校课程里讲的东西都出来了.
[/Quote]
这位仁兄说话直白了点,哈哈

本人可能见识浅薄,没听说粘包和半包的问题,
只知道首先定义好socket的网络通讯协议是不会有这种问题的,具体内容楼主可以参照下各通信运营商自己开发的通讯协议,如CMPP,SMGP,SGIP等等。
回复
sitych 2008-08-01
学习,ls说的是在TCP上层又封装过的协议吧
回复
sunyuqian 2008-07-29
关注
回复
flashboy 2008-07-29
1.如果是TCP流传输,就定义包文头与包文体结构,然后通过包文头中的报文体长度来进行截取.(报文体为变长,报文头为定长)
2.如果是UDP,内置支持消息边界,不用考虑粘包,只需要考虑可靠性问题.

通常为减少TCP粘包以及提高TCP效率,每个包建议不超过1.3K,以避免MAC层进行分祯
回复
lovewds2002 2008-07-29
长见识了.
回复
wrxdos 2008-07-29
mark
回复
椅子 2008-07-28
本专科的课程里面有啊....

这种问题,说白了就是开发前忽视了通信协议.....

居然还置顶了....
回复
chsfly 2008-07-28
[Quote=引用 30 楼 TinyJimmy 的回复:]
socket在通信处理中数据包一分为二或二合为一的状况很多,而且不好模拟测试. 处理方法一般有二:

1.定长法. 每次发送数据都定长. 多余的数据作为下一个包的起始部分保留. 收不够长度就存储起来, 知道够数据为止
2.起始符法. 每个数据都有特定的起始符号内的特定数据, 如果起始符范围外有数据,可以确认是程序逻辑有问题或有人搞破坏了.

上面的方法开工前, 都要制定你的应用协议. 原理清楚了, 协议搞完整了, 程序就好写了.
[/Quote]
回复
椅子 2008-07-28
[Quote=引用 47 楼 jiangguilong2000 的回复:]

1.你的回复表示了你连帖子的意思都没看懂,看看其他人回复的吧,特别是preferme ,kokobox ,TinyJimmy==回复的,他们的回复的内容就是帖子所需要的答案。
2.你这样的回复根本没有任何意义,就好像我们现在问向量机,正交矩阵的问题,然后你跳出来说这都是大学里学的东西,还好意思来这里问。…
[/Quote]

同意你的看法.

我当时只是一惊,怎么学校课程里讲的东西都出来了.

回复
shunzi__1984 2008-07-28
mark
回复
加载更多回复
相关推荐
发帖
Java SE
创建于2007-09-28

6.1w+

社区成员

Java 2 Standard Edition
申请成为版主
帖子事件
创建了帖子
2008-07-22 11:16
社区公告
暂无公告