版权声明:转载请注明出处 https://blog.csdn.net/u014402533/article/details/84675768
1.NettyServerStart
public class NettyServerStart {
@Autowired
public NettyServerStart(final NettyServer nettyServer) {
System.out.println("------------Spring自动加载 ---------");
System.out.println("------------启动Netty服务 ---------");
//绑定端口
nettyServer.setServerPort(6667);
ExecutorService executorService= Executors.newCachedThreadPool();
//执行nettyServer
executorService.execute(nettyServer);
}
}
2.NettyServerInitialize
public class NettyServerInitialize extends ChannelInitializer<SocketChannel> {
@Autowired
private NettyServerHandler nettyServerHandler;
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//pipeline.addLast("decoder", new LengthFieldBasedFrameDecoder(8192,12,2,0,0));
// pipeline.addLast("decoder", new CCYLengthFieldDecoder(8192,12,2,0,0));
// pipeline.addLast("decoder",new CNPCDecoder());
pipeline.addLast("handler", nettyServerHandler);
System.out.println("SimpleChatClient:"+ch.remoteAddress() +"连接上");
}
}
3.NettyServerHandler
重点要说一下这个channelRead0方法里面我遇到的问题。由于某些原因还未来得及系统学习Netty,但是又急需把数据收集起来存入数据库。情况是这样的:有一个1w多个字节的数据需要我来解析出真实数据,但是每次用网络测试助手往端口发送数据,收到的数据都会出现不完全的情况。刚开始,我也不知道啥状况,于是对于那些不完整的数据采取的是不处理。后来才知道,TCP是不会出现丢包的,当数据长度过长的时候,路由器转发的时候会对数据进行拆分,分多少次发,channelRead0这个方法就会执行多少次,也就是说当我们觉得收到的数据不完全的时候,不是数据丢失,只是channelRead0方法还在执行中,数据没有收发完。解决办法:自己建立一个动态数组,用于每次执行channelRead0方法存储数据,然后校验首位末尾,校验通过之后,解析完毕,立刻存入数据库,然后清空动态数组。下图可以看到,随着channelRead0不断执行,动态数组越来越长。
public class NettyServerHandler extends SimpleChannelInboundHandler<Object> {
@Autowired
private CollectedDataService collectedDataService;
private Map<String, DynamicArray<Byte>> dynamicArrayMap = new HashMap<>();
private int defaultDynamicCapacity = 1024 * 1024;
/**
* A thread-safe Set Using ChannelGroup, you can categorize Channels into a meaningful group.
* A closed Channel is automatically removed from the collection,
*/
public static ChannelGroup channels =
new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // (2)
Channel incoming = ctx.channel();
// Broadcast a message to multiple Channels
channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
channels.add(ctx.channel());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // (3)
Channel incoming = ctx.channel();
channels.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
}
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // (4)
ByteBuf in = (ByteBuf) msg;
InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
String ip = insocket.getAddress().getHostAddress();
if(!dynamicArrayMap.containsKey(ip)){
dynamicArrayMap.put(ip,new DynamicArray<Byte>(defaultDynamicCapacity));
}
DynamicArray<Byte> dynamicArray = dynamicArrayMap.get(ip);
while (in.isReadable()) {
byte recByte = in.readByte();
dynamicArray.addLast(recByte);
}
System.out.println("------------------------------------");
System.out.println("dynamicArray长度:"+dynamicArray.getSize());
byte [] dataByte = new byte[dynamicArray.getSize()];
//从Byte[]获得char[]
for (int i = 0;i<dynamicArray.getSize();i++) {
byte b = dynamicArray.get(i);
dataByte[i] = b;
}
char[] byteToChars = getChars(dataByte);
Integer validSuccessIndex = null;
//todo 校验成功
if(byteToChars[0]=='0' && byteToChars[1] == '1'
&& byteToChars[byteToChars.length-2]== '0'
&& byteToChars[byteToChars.length-1] == '3'
) {
validSuccessIndex = byteToChars.length;
System.out.println("成功位:"+ validSuccessIndex);
System.out.println("成功位:"+ validSuccessIndex);
collectedDataService.addCnpcTankData(String.valueOf(byteToChars));
}
if(byteToChars[0]=='1' && byteToChars[1] == 'B'
&& byteToChars[byteToChars.length-2]== '0'
&& byteToChars[byteToChars.length-1] == 'D'
) {
validSuccessIndex = byteToChars.length;
System.out.println("成功位:"+ validSuccessIndex);
System.out.println("成功位:"+ validSuccessIndex);
collectedDataService.addCnpcGunData(String.valueOf(byteToChars));
System.out.println("清空dynamicArray之前:"+dynamicArray.getSize());
}
if(validSuccessIndex != null){
// Byte[] validBuffer = dynamicArray.leftShift(validSuccessIndex);
dynamicArray.leftShift(validSuccessIndex);
System.out.println("清空dynamicArray之后:"+dynamicArray.getSize());
//清空byteToChar
}
System.out.println("1st:"+byteToChars[0]+" 2nd:"+byteToChars[1]+
" 3rd:"+byteToChars[byteToChars.length-2]+" 4th:"+byteToChars[byteToChars.length-1]);
for (char byteTochar : byteToChars
) {
System.out.print(byteTochar);
}
// System.out.println("--------------------");
// System.out.println("dataByte.lenth:"+dataByte.length);
//ValidSuccessIndex不为空则校验成功
}
byte[] toPrimitives(Byte[] oBytes)
{
byte[] bytes = new byte[oBytes.length];
for(int i = 0; i < oBytes.length; i++) {
bytes[i] = oBytes[i];
}
return bytes;
}
char[] getChars(byte[] bytes) {
Charset cs = Charset.forName("UTF-8");
ByteBuffer bb = ByteBuffer.allocate(bytes.length);
bb.put(bytes);
bb.flip();
CharBuffer cb = cs.decode(bb);
return cb.array();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { // (5)
Channel incoming = ctx.channel();
System.out.println("Client:" + incoming.remoteAddress() + "上线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { // (6)
Channel incoming = ctx.channel();
System.out.println("Client:" + incoming.remoteAddress() + "掉线");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Channel incoming = ctx.channel();
System.out.println("Client:" + incoming.remoteAddress() + "异常");
// 当出现异常就关闭连接
//cause.printStackTrace();
ctx.close();
}
}
4.NettyServer
package com.cnpc.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* Created by caihanbin on 2017/4/29.
*/
@Component
public class NettyServer implements Runnable {
@Autowired
private NettyServerInitialize nettyServerInitialize;
private int port;
//设置端口
public void setServerPort(int port) {
this.port = port;
}
public NettyServer(){}
public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(nettyServerInitialize)
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// 绑定端口,开始接收进来的连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务器 socket 关闭 。
// 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}