Netty自定义通信协议主要是在解码器与编码器,其他的变动不大。netty入门实例:https://blog.csdn.net/zc_ad/article/details/83824911,此处将测试的demo共享出来,现在对netty没办法到用语言组织的程度,只能先将demo贡献出来,到时候学的更加深入了,会将添加上讲解。
此demo的形成的效果是:当客户端连接服务端时,会向服务端发送自定义协议消息,服务端收到客户端自定义协议消息后,会给客户端发送另一条自定义协议消息。
协议格式:
* 数据包格式
+——----——+——-----——+——----——+
|协议开始标志| 长度 | 数据 |
+——----——+——-----——+——----——+
1.协议开始标志head_data,为int类型的数据,16进制表示为0X76
2.传输数据的长度contentLength,int类型
3.要传输的数据
固定值,报头:
/**
* Created by XiChuan on 2018-11-07.
*/
public class Constant {
public static final int HEAD = 0x76;
}
自定义协议:
/**
* Created by XiChuan on 2018-11-07.
*/
import java.util.Arrays;
/**
*
* 自己定义的协议
* 数据包格式
* +——----——+——-----——+——----——+
* |协议开始标志| 长度 | 数据 |
* +——----——+——-----——+——----——+
* 1.协议开始标志head_data,为int类型的数据,16进制表示为0X76
* 2.传输数据的长度contentLength,int类型
* 3.要传输的数据
*
*/
public class MessageProtocol {
/**
* 消息的开头的信息标志
*/
private int head_data = Constant.HEAD;
/**
* 消息的长度
*/
private int contentLength;
/**
* 消息的内容
*/
private byte[] content;
/**
* 用于初始化,SmartCarProtocol
*
* @param contentLength
* 协议里面,消息数据的长度
* @param content
* 协议里面,消息的数据
*/
public MessageProtocol(int contentLength, byte[] content) {
this.contentLength = contentLength;
this.content = content;
}
public int getHead_data() {
return head_data;
}
public int getContentLength() {
return contentLength;
}
public void setContentLength(int contentLength) {
this.contentLength = contentLength;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
@Override
public String toString() {
return "MessageProtocol [head_data=" + head_data + ", contentLength="
+ contentLength + ", content=" + Arrays.toString(content) + "]";
}
}
自定义编码器:
import ch.qos.logback.core.encoder.ByteArrayUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* Created by XiChuan on 2018-11-07.
*/
/**
*
* 自己定义的协议
* 数据包格式
* +——----——+——-----——+——----——+
* |协议开始标志| 长度 | 数据 |
* +——----——+——-----——+——----——+
* 1.协议开始标志head_data,为int类型的数据,16进制表示为0X76
* 2.传输数据的长度contentLength,int类型
* 3.要传输的数据
*
*/
public class MessageProtocolEncoder extends MessageToByteEncoder {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, MessageProtocol msg, ByteBuf byteBuf) throws Exception {
// 写入消息SmartCar的具体内容
// 1.写入消息的开头的信息标志(int类型)
byteBuf.writeInt(msg.getHead_data());
// 2.写入消息的长度(int 类型)
byteBuf.writeInt(msg.getContentLength());
// 3.写入消息的内容(byte[]类型)
byteBuf.writeBytes(msg.getContent());
}
}
自定义解码器:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* Created by XiChuan on 2018-11-06.
*/
/**
*
* 自己定义的协议
* 数据包格式
* +——----——+——-----——+——----——+
* |协议开始标志| 长度 | 数据 |
* +——----——+——-----——+——----——+
* 1.协议开始标志head_data,为int类型的数据,16进制表示为0X76
* 2.传输数据的长度contentLength,int类型
* 3.要传输的数据,长度不应该超过2048,防止socket流的攻击
*
*/
public class MessageProtocolDecoder extends ByteToMessageDecoder {
/**
*
* 协议开始的标准head_data,int类型,占据4个字节.
* 表示数据的长度contentLength,int类型,占据4个字节.
*
*/
public final int BASE_LENGTH = 4 + 4;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
List
创建服务端:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
/**
* Created by XiChuan on 2018-11-05.
*/
public class Server {
private int port;
public Server(int port){this.port = port;}
public void run()throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup(); //用来接收进来的连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); //用来处理已经被接收的连接
try {
ServerBootstrap bootstrap = new ServerBootstrap(); //启动NIO服务的辅助启动类
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class) //服务端
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//心跳机制 参数:1.读空闲超时时间 2.写空闲超时时间 3.所有类型的空闲超时时间(读、写) 4.时间单位
//在Handler需要实现userEventTriggered方法,在出现超时事件时会被触发
socketChannel.pipeline().addLast("idleStateHandler", new IdleStateHandler(500, 0, 0,TimeUnit.SECONDS));
//设置解码器
socketChannel.pipeline().addLast("decoder", new MessageProtocolDecoder());//new ByteArrayDecoder());//new FixedLengthFrameDecoder(4));
//设置自定义ChannelHandler
socketChannel.pipeline().addLast("channelHandler", new ServerHandler());
//设置编码器
socketChannel.pipeline().addLast("encoder",new MessageProtocolEncoder());//new ByteArrayEncoder());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture cf = bootstrap.bind(port).sync(); //绑定端口,开始接收进来的连接
cf.channel().closeFuture().sync(); //等待服务器socket关闭
}catch (Exception e){
e.printStackTrace();
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args)throws Exception{
new Server(8081).run();
}
}
创建服务端处理类:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by XiChuan on 2018-11-05.
*/
public class ServerHandler extends ChannelInboundHandlerAdapter {
private AtomicInteger channelCount = new AtomicInteger(0); //通道数量
/**
* 读数据
* @param ctx
* @param msg
* @throws Exception
*/
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("read channel=" + ctx.channel() + ", total channel=" + channelCount);
try {
MessageProtocol message = (MessageProtocol)msg;
System.out.println("receive client message:"+message.toString());
String str = "Hi I am Server ...";
MessageProtocol protocol = new MessageProtocol(str.getBytes().length, str.getBytes());
// 当服务端完成写操作后,关闭与客户端的连接
ctx.channel().writeAndFlush(protocol);
//没有定义将接收的数据改为什么类型此时就会用ByteBuf,在自定义协议的时候并没有用
if (msg instanceof ByteBuf){
//将ascii码的二进制转换为对应的字符
System.out.println("Buffer ");
ByteBuf byteBuf = (ByteBuf)msg;
System.out.println(byteBuf.toString(CharsetUtil.UTF_8));
//转换为16进制字符串
/*byte[] bytes = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(bytes);
String byteStr = ByteBufUtil.hexDump(bytes);
System.out.println(byteStr);*/
//转换为16进制字符串
byte[] byte2s = ByteBufUtil.getBytes(byteBuf);
System.out.println("hex string:"+ ByteBufUtil.hexDump(byte2s));
}
} finally {
// 抛弃收到的数据
ReferenceCountUtil.release(msg);
}
}
/**
* 心跳检测的超时时会触发
* @param ctx
* @param evt
* @throws Exception
*/
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.READER_IDLE) {
System.out.println("trigger channel =" + ctx.channel());
ctx.close(); //如果超时,关闭这个通道
}
} else if (evt instanceof SslHandshakeCompletionEvent) {
System.out.println("ssl handshake done");
//super.userEventTriggered(ctx,evt);
}
}
/**
* 当通道活动时
* @param ctx
* @throws Exception
*/
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channelCount.incrementAndGet();//当有新通道连接进来时,将通道数+1
System.out.println("active channel=" + ctx.channel() + ", total channel=" + channelCount + ", id=" + ctx.channel().id().asShortText());
}
/**
* 当通道不活动时
* @param ctx
* @throws Exception
*/
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//当通道关闭时,将通道数-1
ctx.close();
channelCount.decrementAndGet();
System.out.println("inactive channel,channel=" + ctx.channel() +", id=" + ctx.channel().id().asShortText());
}
/**
* 异常获取
* @param ctx
* @param cause
* @throws Exception
*/
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("exception channel=" + ctx.channel() + " cause=" + cause); //如果不活跃关闭此通道
ctx.close();
}
}
创建客户端:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayDecoder;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
/**
* Created by XiChuan on 2018-11-05.
*/
public class Client {
private String host;
private int port;
public Client(String host,int port){
this.host = host;
this.port = port;
}
public void run() throws Exception {
// 配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
// 客户端辅助启动类 对客户端配置
Bootstrap b = new Bootstrap();
b.group(group)//
.channel(NioSocketChannel.class)//
.option(ChannelOption.TCP_NODELAY, true)//
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast("decoder",new MessageProtocolDecoder());//new ByteArrayDecoder());
socketChannel.pipeline().addLast("encoder",new MessageProtocolEncoder());//new ByteArrayEncoder());
socketChannel.pipeline().addLast("channelHandler",new ClientHandler()); // 处理网络IO
}
});
// 异步链接服务器 同步等待链接成功
ChannelFuture f = b.connect(host, port).sync();
// 等待链接关闭
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
System.out.println("client release resource...");
}
}
public static void main(String[] args) throws Exception {
new Client("127.0.0.1",8081).run();
}
}
创建客户端处理类:
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* Created by XiChuan on 2018-11-05.
*/
public class ClientHandler extends ChannelInboundHandlerAdapter {
// 客户端与服务端,连接成功的的处理
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("active channel:" + ctx.channel());
// 发送自定义Message协议的消息
// 要发送的信息
String str = "I am client ...";
// 获得要发送信息的字节数组
byte[] content = str.getBytes();
// 要发送信息的长度
int contentLength = content.length;
MessageProtocol protocol = new MessageProtocol(contentLength, content);
System.out.println("send message:"+protocol.toString());
Channel channel = ctx.channel();
channel.writeAndFlush(protocol);
}
// 只是读数据,没有写数据的话
// 需要自己手动的释放的消息
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("read channel:" + ctx.channel());
try {
MessageProtocol messageProtocol = (MessageProtocol) msg;
System.out.println("receive server message:" + messageProtocol.toString());
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.close();
}
}
效果如下:
服务端:
客户端:
参考链接:
https://blog.csdn.net/zbw18297786698/article/details/53691915