netty实践(十)-自定义数据包协议

前言

  1. 序列化协议:把数据对象转化成二进制数据,因为网络传输只能传输二进制数据
  2. 自定义数据包协议又是 干什么的呢?
  3. 场景:客户端 发送请求 先发送:give me a coffee 再发送 give me a tea 按理说服务端是先接受到give me a coffee 再接收到give me a tea

    内容

1.两种现象
1.1 粘包现象

服务端收到的请求也许是黏在一起的:give me a coffeegive me a tea所以没有人能够翻译这句话是什么意思?
没办法解析。这种现象叫做粘包现象。

1.2 分包现象

收到:give me
a coffeegive me a tea

出现粘包,分包的根本原因:没有一个稳定数据结构 导致服务器无法区分一个请求到底是传输了哪些数据

2.两种解决思路
2.1 分隔符

1.分隔符: give me a coffee|give me a tea|
缺点:效率低,频繁读取数据(我们要把每个数据一个个读取,查看是否是想要的数据)

2.2 长度+数据

2.长度+数据:16give me a coffee13give me a tea

3.数据包协议
3.1 数据包结构


包头4个字节:int
模块号2个字节:short
命令号2个字节:short

场景:
比如游戏中:分为很多模块,玩家Player模块
发送以下请求:
1.获取玩家数据 2.注册用户 3.购买金币
请求数据不一样

Fuben模块
发送以下两个请求:
1 查看副本
2 攻打副本
以上场景中:Player,Fuben对应模块 发送请求对应命令。

4.代码

我们自定义数据包来封装我们的请求和响应,由于是共有的,所以创建共有的java项目:common

4.1 请求(Request)

1.请求:(Request:由于包头是请求的头部信息,所以我们不包含在request中)


  
  
        
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
        
        
/**
*
* <pre>
* 数据包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包头 | 模块号 | 命令号 | 长度 | 数据 |
* +——----——+——-----——+——----——+——----——+——-----——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
* 数据4字节
*/
public class Request {
/**
* 模块号
*/
private short module;
/**
* 命令号
*/
private short cmd;
/**
* 数据
*/
private byte[] data;
public short getModule() {
return module;
}
public void setModule(short module) {
this.module = module;
}
public short getCmd() {
return cmd;
}
public void setCmd(short cmd) {
this.cmd = cmd;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
/**
* 获取数据长度
*/
public int getDataLength(){
if(this.data==null){
return 0;
}
return this.data.length;
}
}

2.RequestEncoder


  
  
        
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
        
        
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
import com.cn.constants.Constants;
import com.cn.model.Request;
/**
* 请求编码器
* * <pre>
* 数据包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包头 | 模块号 | 命令号 | 长度 | 数据 |
* +——----——+——-----——+——----——+——----——+——-----——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
* @author yxm
*
*/
public class RequestEncoder extends OneToOneEncoder{
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel,
Object msg) throws Exception {
Request request = (Request)msg;
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
//包头
buffer.writeInt(Constants.FLAG);
//模块号
buffer.writeShort(request.getModule());
//命令号
buffer.writeShort(request.getCmd());
// 长度
buffer.writeInt(request.getDataLength());
//数据(数据不为空,才读取到buffer)
if(request.getData()!=null){
buffer.writeBytes(request.getData());
}
return buffer;
}
}

3.RequestDecoder


  
  
        
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
        
        
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.constants.Constants;
import com.cn.model.Request;
/**
* 请求解码器
* <pre>
* 数据包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包头 | 模块号 | 命令号 | 长度 | 数据 |
* +——----——+——-----——+——----——+——----——+——-----——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
*/
public class RequestDecoder extends FrameDecoder{
/**
* 数据基本长度
*/
public static int BASE_LENGTH = 4+2+2+4;//包头+模块号+命令号+长度
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer) throws Exception {
//记录包开头的index
int beginIndex = buffer.readerIndex();
//可读长度必须大于基本长度
if(buffer.readableBytes()>=BASE_LENGTH){
while(true){//循环读取,读到包头flag为止才可以真正读取数据
if(buffer.readInt()==Constants.FLAG){
break;
}
}
//模块号
short module = buffer.readShort();
//命令号
short cmd = buffer.readShort();
//长度
int length = buffer.readInt();
//现在可读长度小于数据长度,说明数据还没有读完
if(buffer.readableBytes()<length){
//已经读取了12字节数据,现在的数据还没有来齐,所以还原读指针,返回空数据
buffer.readerIndex(beginIndex);
return null;
}
//如果数据来全之后(按照长度读取)
byte[] data = new byte[length];
buffer.readBytes(data);
//构建 buffer-->request
Request request = new Request();
request.setModule(module);
request.setCmd(cmd);
request.setData(data);
return request;
}
return null;
}
}
4.2 响应(Response)

1.Response


  
  
        
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        
        
/**
* <pre>
* 数据包格式
* +——----——+——-----——+——----——+——----——+——-----——+
* | 包头 | 模块号 | 命令号 | 长度 | 数据 |
* +——----——+——-----——+——----——+——----——+——-----——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
* 数据4字节
*/
public class Response {
/**
* 状态码
*/
private int statusCode;
/**
* 模块号
*/
private short module;
/**
* 命令号
*/
private short cmd;
/**
* 数据
*/
private byte[] data;
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public short getModule() {
return module;
}
public void setModule(short module) {
this.module = module;
}
public short getCmd() {
return cmd;
}
public void setCmd(short cmd) {
this.cmd = cmd;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
/**
* 获取数据长度
*/
public int getDataLength(){
if(this.data==null){
return 0;
}
return this.data.length;
}
}

2.ResponseEncoder


  
  
        
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
        
        
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
import com.cn.constants.Constants;
import com.cn.model.Response;
/**
* 请求编码器
* * <pre>
* 数据包格式
* +——----+——----+——----——+——----+——--------——+
* | 包头 |模块号 |命令号 |状态码 |长度 | 数据 |
* +——----+——----+——----——+——----+——--------——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
* @author yxm
*
*/
public class ResponseEncoder extends OneToOneEncoder{
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel,
Object msg) throws Exception {
Response response = (Response)msg;
ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
//包头
buffer.writeInt(Constants.FLAG);
//模块号
buffer.writeShort(response.getModule());
//命令号
buffer.writeShort(response.getCmd());
/**
* 状态码
*/
buffer.writeInt(response.getStatusCode());
// 长度
buffer.writeInt(response.getDataLength());
//数据(数据不为空,才读取到buffer)
if(response.getData()!=null){
buffer.writeBytes(response.getData());
}
return buffer;
}
}

3.ResponseDecoder


  
  
        
        
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
        
        
/**
* 请求解码器
* <pre>
* 数据包格式
+——----+——----+——----——+——----+——--------——+
* | 包头 |模块号 |命令号 |状态码 |长度 | 数据 |
* +——----+——----+——----——+——----+——--------——+
* </pre>
* 包头4字节
* 模块号2字节short
* 命令号2字节short
* 长度4字节(描述数据部分字节长度)
*/
public class ResponseDecoder extends FrameDecoder{
/**
* 数据基本长度
*/
public static int BASE_LENGTH = 4+2+2+4;//包头+模块号+命令号+长度
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer) throws Exception {
//记录包开头的index
int beginIndex = buffer.readerIndex();
//可读长度必须大于基本长度
if(buffer.readableBytes()>=BASE_LENGTH){
while(true){//循环读取,读到包头flag为止才可以真正读取数据
if(buffer.readInt()==Constants.FLAG){
break;
}
}
//模块号
short module = buffer.readShort();
//命令号
short cmd = buffer.readShort();
//状态码
int statusCode = buffer.readInt();
//长度
int length = buffer.readInt();
//现在可读长度小于数据长度,说明数据还没有读完
if(buffer.readableBytes()<length){
//已经读取了12字节数据,现在的数据还没有来齐,所以还原读指针,返回空数据
buffer.readerIndex(beginIndex);
return null;
}
//如果数据来全之后(按照长度读取)
byte[] data = new byte[length];
buffer.readBytes(data);
//构建 buffer-->request
Response response = new Response();
response.setModule(module);
response.setCmd(cmd);
response.setStatusCode(statusCode);
response.setData(data);
return response;
}
return null;
}
}
4.3 其他

1.Constants


  
  
        
        
1
2
3
4
5
6
        
        
public class Constants {
/**
* 包头
*/
public static final int FLAG = -32521234;
}

2.StatusCode


  
  
        
        
1
2
3
4
        
        
public interface StatusCode {
public static int SUCCESS=1;//成功
public static int FAIL=0;//失败
}
5.项目测试

修改之前的netty3客户端:
1.encoder:客户端编码是客户端请求的编码器
2.decoder:客户端解码是解码服务端返回的response对象
3.channel.write:是发送request对象

原文:大专栏  netty实践(十)-自定义数据包协议


猜你喜欢

转载自www.cnblogs.com/petewell/p/11584980.html