文章目录
前言
Netty服务端项目模块级代码
这一篇主要是用于使用 Socket 通信的端对端的项目,本篇采取Java代码编写服务端,通过自定义编解码器
实现数据约定及安全性校对,如有BUG请广大网友批评指针。
下面展示项目的包结构:
- 最外层的类为Netty初始化及基础的通道处理;
- 其中有三个大目录,business\codec\protocol,分别是业务包、编解码器、私定协议;
- request包及response包为请求消息体及响应消息体的封装;
将依赖引入后,将进行正式开始流程说明。
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.68.Final</version>
</dependency>
本文是采取与SpringBoot的结合方式进行编码。
一、初始化启动
首先将服务端进行进程启动。
NettyServerInitialize.java
package org.springblade.netty;
import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.CompletableFuture;
/**
* @author 李家民
*/
@Component
public class NettyServerInitialize {
private static Logger log = LoggerFactory.getLogger(NettyServerInitialize.class);
/** 进程组 */
private static EventLoopGroup bossGroup;
private static EventLoopGroup workerGroup;
private static ServerBootstrap serverBootstrap;
/** 启动监听 */
private static ChannelFuture channelFuture;
/** 端口 */
private static Integer port = 10222;
static {
// Server初始化
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup(2);
serverBootstrap =
new ServerBootstrap().group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new NettyServerChannel());
}
@PostConstruct
public void starter() {
CompletableFuture.runAsync(new Runnable() {
@Override
public void run() {
try {
channelFuture = serverBootstrap.bind(port)
.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("监听端口 " + port + " 成功");
} else {
log.error("监听端口 " + port + " 失败");
}
}
}).channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
});
}
}
然后我们需要通道处理器进行流水线处理。
NettyServerChannel.java
package org.springblade.netty;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import org.springblade.netty.codec.BusinessMsgDecoder;
import org.springblade.netty.codec.BusinessMsgEncoder;
/**
* 通道初始化器
*/
public class NettyServerChannel extends ChannelInitializer<SocketChannel> {
public NettyServerChannel() {
super();
}
/**
* 在将ChannelHandler添加到实际上下文并准备好处理事件后调用 如果覆盖此方法 请确保您调用 super
* @param ctx 通道处理器上下文
* @throws Exception
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
}
/**
* Handle the {@link Throwable} by logging and closing the {@link Channel}. Sub-classes may override this.
* @param ctx
* @param cause
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
/**
* This method will be called once the {@link Channel} was registered. After the method returns this instance
* will be removed from the {@link ChannelPipeline} of the {@link Channel}.
* @param ch the {@link Channel} which was registered.
* @throws Exception is thrown if an error occurs. In that case it will be handled by
* {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
* the {@link Channel}.
*/
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 编解码器 自定义
pipeline.addLast(new BusinessMsgEncoder());
pipeline.addLast(new BusinessMsgDecoder());
pipeline.addLast(new NettyServerInboundHandler());
}
}
二、消息入站
从上面的代码可以看到,我们在ChannelPipeline
中加入了三个处理器,分别是
- 编码器;
- 解码器;
- 入站处理器;
我们按照数据的流入到转出纵观看向这幅代码,数据进站前,流入解码器。
BusinessMsgDecoder.java
package org.springblade.netty.codec;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.netty.protocol.MsgProtocol;
import org.springblade.netty.protocol.request.ServerRequest;
import org.springblade.netty.protocol.response.SendResponse;
import java.util.Arrays;
import java.util.List;
/**
* 自定义业务解码器
* @author lijiamin
*/
public class BusinessMsgDecoder extends ByteToMessageDecoder {
private static final Logger logger = LoggerFactory.getLogger(BusinessMsgDecoder.class);
/**
* 消息解码量化交易
* flag(1 byte)+length(4 byte,后边内容的长度)+protocol code(4 byte)+content
* length的长度包括 :消息号 + 内容
* @param channelHandlerContext
* @param byteBuf
* @param list
* @throws Exception
*/