在前面的文章我们说到过,可以采用长度+数据的格式定义包结构;但是如果长度定义的特别大,那么缓冲区必然会存储不下,而引起宕机或其他问题。这便是一种socket攻击。
除此之外,如果定义的长度和实际长度不匹配,比如实际长度为5,定义的长度为6,那么读取的时候可能会把下一个数据包的一部分读取到,造成以后的数据不能正常解析;这也是一种socket攻击。
可以通过以下几个方面处理socket攻击:
1.定义字节的最大长度,如果字节流大于Max,跳过所有字节流
// 防止Socket攻击
if (buffer.readableBytes() > 2048) {
buffer.skipBytes(buffer.readableBytes());
return null;
}
2.但是当跳过所有字节流后,可用包头定位到下一个正确的数据包开始的地方,所以标准包结构还应该有个起始的包头标志。跳过最大长度后,循环读取数据包一个字节,判断是否是包头;如果不是则跳过继续读,直到读到包头,跳出循环,然后才可以继续解析;但是这个过程可能会在读的过程中长度小于最小包结构长度了,这时候要直接返回null,等待下次数据到来后再解析;
具体代码如下:
package com.cn.codc;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import com.cn.constant.ConstantValue;
import com.cn.model.Response;
/**
* response解码器
* <pre>
* 数据包格式
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* | 包头 | 模块号 | 命令号 | 状态码 | 长度 | 数据 |
* +——----——+——-----——+——----——+——----——+——-----——+——-----——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
*
-
*
*/
public class ResponseDecoder extends FrameDecoder{
/**
* 数据包基本长度
*/
public static int BASE_LENTH = 4 + 2 + 2 + 4;
@Override
protected Object decode(ChannelHandlerContext arg0, Channel arg1, ChannelBuffer buffer) throws Exception {
//可读长度必须大于基本长度
if(buffer.readableBytes() >= BASE_LENTH){
//防止字节流攻击
if(buffer.readableBytes()>2048){
//清除缓存中的数据
buffer.skipBytes(buffer.readableBytes());
}
//记录包头开始的index
int beginReader ;
while(true){
beginReader = buffer.readerIndex();
//标记当前索引
buffer.markReaderIndex();
//判断包头是否是当前的包头
//因为清除了数据之后,因为可能会出现分包截断的现象,下次进来的时候,
//可能不是开头,所以不能知道哪个是长度,哪个是数据,所以需要包头,
//只有当时读到包头的时候才继续往下走。
if(buffer.readInt() == ConstantValue.FLAG){
break;
}
//未读到包头超过了一个字节
buffer.resetReaderIndex();
//因为可能读一个int之后,略过了包头,因为可能包头在第一个字节处
//所以这里选择继续往下读一个字节
buffer.readByte();
//可能出现极端的情况,长度又变得不满足
if(buffer.readableBytes()<BASE_LENGTH){
return null
}
}
//模块号
short module = buffer.readShort();
//命令号
short cmd = buffer.readShort();
//状态码
int stateCode = buffer.readInt();
//长度
int length = buffer.readInt();
if(buffer.readableBytes() < length){
//还原读指针
buffer.readerIndex(beginReader);
return null;
}
byte[] data = new byte[length];
buffer.readBytes(data);
Response response = new Response();
response.setModule(module);
response.setCmd(cmd);
response.setStateCode(stateCode);
response.setData(data);
//继续往下传递
return response;
}
//数据包不完整,需要等待后面的包来
return null;
}
}