Netty搭建-spring版

在Springboot中搭建Netty服务

实现socket通讯

线程版请看另一片文章

1.把Netty服务通过@Component注入到spring容器中

@PostConstruct和@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。

被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。

@PostConstruct在构造函数之后执行,init()方法之前执行。

@PreDestroy在destroy()方法执行之后执行。

url:127.0.0.1,port:7000,自己配

import java.net.InetSocketAddress;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; 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 javax.annotation.PostConstruct; import javax.annotation.PreDestroy; /** * 初始化 * * @author zq */ @Component public class NettyServer { private static final Logger logger = Logger.getLogger(NettyServer.class); @Value("${netty.url}") private String url;//IP地址  @Value("${netty.port}") private int port;//端口号  @Autowired ServerChannelInitializer serverChannelInitializer;//通道初始化 /** * NioEventLoop并不是一个纯粹的I/O线程,它除了负责I/O的读写之外 创建了两个NioEventLoopGroup, * 它们实际是两个独立的Reactor线程池。 一个用于接收客户端的TCP连接, * 另一个用于处理I/O相关的读写操作,或者执行系统Task、定时任务Task等。 */ //负责接收客户端连接线程 private final EventLoopGroup bossGroup = new NioEventLoopGroup(); //负责处理客户端i/o事件、task任务、监听任务组 private final EventLoopGroup workerGroup = new NioEventLoopGroup(); private Channel channel; @PostConstruct public void start() { try { InetSocketAddress address = new InetSocketAddress(url, port); //启动 NIO 服务的辅助启动类 ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup, workerGroup); //配置 Channel bootstrap.channel(NioServerSocketChannel.class); bootstrap.localAddress(address); bootstrap.childHandler(serverChannelInitializer); //BACKLOG用于构造服务端套接字ServerSocket对象, // 设置队列大小,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度 bootstrap.option(ChannelOption.SO_BACKLOG, 1024); // 是否启用心跳保活机制,两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文 bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); // 绑定端口,开始接收进来的连接 ChannelFuture future = bootstrap.bind(address).sync(); System.out.println("Netty服务监听端口: " + address.getPort()); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭主线程组  bossGroup.shutdownGracefully(); // 关闭工作线程组  workerGroup.shutdownGracefully(); } } /** * 停止服务 */ @PreDestroy public void destroy() { logger.info("Shutdown Netty Server..."); if (channel != null) { channel.close(); } bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); logger.info("Shutdown Netty Server Success!"); } }

2.通道初始化

channel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));// 解码格式
channel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));// 编码格式
如果是字符串消息可用这两种编码解码格式,
这里我是和硬件设备通讯,所以要手动解码。
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 初始化 * * @author zq */ @Component public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> { @Autowired ServerHandler serverHandler; @Override protected void initChannel(SocketChannel channel) throws Exception { // 加密 //channel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));// 解码格式 //channel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));// 编码格式 channel.pipeline().addLast(serverHandler); } }

3.服务处理

处理客户端传来的数据(byte数组)

import java.util.Map;

import com.bls.blsapplicationsocket.common.dict.ConstXml;
import io.netty.channel.ChannelHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor; import org.apache.log4j.Logger; import com.bls.blsapplicationsocket.common.utils.ParseDataUtil; import com.bls.blsapplicationsocket.common.utils.ByteConver; import com.bls.blsapplicationsocket.common.utils.RequestUtil; import com.bls.blsapplicationsocket.common.utils.ResponseUtil; import com.bls.blsapplicationsocket.common.utils.XmlUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * netty服务端处理器 * * @author zq **/ @Component @ChannelHandler.Sharable public class ServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger(ServerHandler.class); @Autowired ParseDataUtil parseDataUtil; /** * 所有的活动用户 */ private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + "----->连接触发----->" + ctx.channel().id()); super.channelActive(ctx); group.add(ctx.channel()); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().remoteAddress() + "----->断开连接----->" + ctx.channel().id()); super.channelInactive(ctx); group.remove(ctx.channel()); } /** * 客户端发消息会触发;每一个传入的消息都要调用 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("收到通道IP为" + ctx.channel().remoteAddress() + "发来的消息"); ByteBuf msgByteBuf = (ByteBuf) msg; byte[] byteArray = new byte[msgByteBuf.readableBytes()]; msgByteBuf.readBytes(byteArray);
     //处理byte数组数据
} /** * 发生异常触发 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }

4.这里附上解析byte数组

前10位是包头+序列号+内容长度

后4位是包尾

从第10位,到倒数第4位,是需要的数据内容

  /**
     * 解析客户端发送回来的信息
     * 
     * @param msg
     * @return
     */
    public static String message(Object msg) {
        try {
            // 解析xml 获取xml长度
            byte[] size = new byte[4];
            System.arraycopy(msg, 10, size, 0, 4);
            int len = ByteConver.Bytes2Int_LE(size);

            // 创建符合xml长度的数组
            byte[] xml = new byte[len]; // 返回解析后的xml System.arraycopy(msg, 14, xml, 0, len); return new String(xml, "utf-8"); } catch (Exception e) { e.printStackTrace(); } return null; }

 

猜你喜欢

转载自www.cnblogs.com/zq33312757/p/11837462.html