閱讀本文建議從第一篇開始往後看
本系列文章
一、Encoder的作用
將發送的數據進行編碼成需要的數據格式,然後發送出去
二、Decoder的作用
將收到的數據根據數據協議進行解碼,然後處理
三、Handler的作用
將解碼好的數據進行處理
四、下面們通過一個簡單的一個示例進行收發數據
- 定義一個傳輸的數據包格式
包頭 | 命令字 | 數據長度 | 數據區 | 包尾 |
---|---|---|---|---|
0x2A | 一個字節 | 一個字節 | 數據字符串 | 0x2A |
- 根據上面定義的數據包,便可以生成對應的數據實體類;如下:
- 數據實體類
public class PkgDataBean {
//命令字
private byte cmd;
//數據長度
private byte dataLength;
//數據
private String data;
//省略get/set函數
}
- 客戶端發送數據就可以直接寫入
PkgDataBean
對象,然後在ClientEncoder
中編碼 - 發送數據
//獲取與服務端的連接通道
Channel channel = NettyClient.getChannel();
PkgDataBean bean = new PkgDataBean();
bean.setCmd((byte) 0x01);
bean.setData(etContent.getText().toString());
bean.setDataLength((byte) bean.getData().getBytes().length);
//寫入數據
channel.writeAndFlush(bean);
- 對數據進行編碼,將對象轉成字節數組。可以注意到這裏我們將泛型直接使用的是定義的實體類
public class ClientEncoder extends MessageToByteEncoder<PkgDataBean> {
private static final String TAG = "ClientEncoder";
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, PkgDataBean data, ByteBuf byteBuf) throws Exception {
//根據數據包協議,生成byte數組
byte[] bytes = {0x2A, data.getCmd(), data.getDataLength()};
byte[] dataBytes = data.getData().getBytes();
//將所有數據合併成一個byte數組
byte[] all = ByteUtil.byteMergerAll(bytes, dataBytes, new byte[]{0x2A});
//發送數據
byteBuf.writeBytes(all);
}
}
- 程序執行的效果如下:
五、在客戶端發送數據我們使用了Encoder
進行編碼,那麼服務端要接收這些數據就需要在Decoder
中進行解碼,這樣就能夠拿到正確的數據了
- 服務端的Decoder實現,將數據解碼生成
PkgDataBean
實體
public class ServerDecoder extends ByteToMessageDecoder {
private static final String TAG = "ServerDecoder";
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
int length = byteBuf.readableBytes();
//收到的數據包
byte[] data = byteBuf.readBytes(length).array();
//判斷數據包是不是一個正確的數據包
if (data[0] == 0x2A && data[0] == data[data.length - 1]) {
PkgDataBean bean = new PkgDataBean();
bean.setCmd(data[1]);
bean.setDataLength(data[2]);
byte[] bytes = Arrays.copyOfRange(data, 3, 3 + bean.getDataLength());
bean.setData(new String(bytes));
Log.d(TAG, "收到了客戶端發送的數據:" + bean.toString());
}
}
}
- 運行的效果
六、上面我們說到的都是客戶端發送數據,服務端接收數據;在實際開發中肯定都是雙方都是有數據發送和接收的,所以雙方都會有Encoder
、Decoder
進行編解碼,用法都是一樣的這裏就不累贅闡述了
七、在解碼器中我們將數據解析出來了,那麼就需要使用到業務中了;也就是在Handler
中處理,所以Handler可以說是我們的數據處理中心、包括重連、心跳、客戶端上下線等等…(這些後面的文章會講到)
-
先介紹一下重點的幾個方法
- channelRead0()——>當收到數據的回調
- channelActive()——>有客戶端連接過來的回調
- channelInactive()——>有客戶端斷開了連接的回調
- userEventTriggered()——>空閒事件的回調
- exceptionCaught()——>發生異常的回調
-
Handler中的數據從哪傳過來的呢?這個當然是從
Decoder
中來的了,如下:
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
int length = byteBuf.readableBytes();
//收到的數據包
byte[] data = byteBuf.readBytes(length).array();
//判斷數據包是不是一個正確的數據包
if (data[0] == 0x2A && data[0] == data[data.length - 1]) {
PkgDataBean bean = new PkgDataBean();
bean.setCmd(data[1]);
bean.setDataLength(data[2]);
byte[] bytes = Arrays.copyOfRange(data, 3, 3 + bean.getDataLength());
bean.setData(new String(bytes));
Log.d(TAG, "收到了客戶端發送的數據:" + bean.toString());
//將數據傳遞給下一個Handler,也就是在NettyServer給ChannelPipeline添加的處理器
list.add(bean);
}
}
- 將數據解析完畢後使用list.add(),將數據傳遞至Handler
Handler
的代碼如下:
public class ServerHandler extends SimpleChannelInboundHandler<PkgDataBean> {
private static final String TAG = "ServerHandler";
/**
* 當收到數據的回調
*
* @param channelHandlerContext 封裝的連接對像
* @param bean
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, PkgDataBean bean) throws Exception {
Log.d(TAG, "收到了解碼器處理過的數據:" + bean.toString());
}
}
- 運行的效果
- 這裏也同樣接受一個泛型需要注意的是這裏指定的泛型必須與Decoder中list.add()的類型一致,否則會出錯!
八、下一篇文章將着重對斷線重連、心跳處理進行講解說明
Demo將會在本系列文章中最後一篇給出