Java Springboot + netty + socket服务端

第一步:引入maven netty-all jar包

<!--netty-socketio-->
<dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.1.36.Final</version>
</dependency>

第二步: 自定义解码器


import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;

/**
 * 自定义解码器
 */
@Slf4j
public class DecoderHandler extends ByteToMessageDecoder {

    private static Map<ChannelHandlerContext, String> msgBufMap = new ConcurrentHashMap<>();

    private static Map<String,ChannelHandlerContext> channelMap = new ConcurrentHashMap<>();

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        byte[] data = new byte[in.readableBytes()];
        in.readBytes(data);
        log.info("接收到的消息:【{}】",data);
    }
 }   

继承 ChannelInboundHandlerAdapter 实现连接相关方法

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@ChannelHandler.Sharable
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    PooledByteBufAllocator pool = PooledByteBufAllocator.DEFAULT;

    private static Map<String,String> antIdMap = new ConcurrentHashMap<>();

    /**
     * 在与客户端的连接已经建立之后将被调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("netty客户端与服务端连接开始...客户端地址【{}】",ctx.channel().remoteAddress());
    }

    /**
     * 当从客户端接收到一个消息时被调用
     * msg 就是硬件传送过来的数据信息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("当从客户端接收到一个消息时被调用...{}",msg.toString());
    }

    /**
     * 客户端与服务端断开连接时调用
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("netty客户端与服务端连接关闭...客户端地址【{}】",ctx.channel().remoteAddress());
    }

    /**
     * 在处理过程中引发异常时被调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
        System.out.println("异常信息:rn " + cause.getMessage());
    }
    /**
     * 服务端接收客户端发送过来的数据结束之后调用
     * @param ctx
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
    	ctx.writeAndFlush("我收到消息了!");
        ctx.flush();
    	System.out.println("服务端接收客户端发送过来的数据结束之后调用 ");
    }
 }   

@ChannelHandler.Sharable 作用与多个客户端连接时使用 否则当第二个连接时会抛异常

第三步:创建配置类 配置socket端口

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 读取YML中的服务配置
 *
 */
@Configuration
@ConfigurationProperties(prefix = ServerProperties.PREFIX)
@Data
public class ServerProperties {

    public static final String PREFIX = "netty.server";

    /**
     * 服务器端口
     */
    private Integer port;

}

yml配置:

netty:
  server:
    port: 4001

第四步:创建启动server

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;

/**
 * 启动 Server
 */
@Component
@Slf4j
public class NettySocketServer {

    @Autowired
    private NettyServerHandler nettyServerHandler;

    @Autowired
    private ServerProperties serverProperties;

    public static ServerSocketChannel serverSocketChannel;

    /**
     * 初始化
     */
    public void start() throws Exception {
        log.info("初始化 NettySocketServer  ...");
        // 连接处理group
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 事件处理group
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();//1.创建ServerBootStrap实例
        // 绑定处理group
        bootstrap.group(bossGroup, workerGroup)//2.设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel
                .channel(NioServerSocketChannel.class)//3.设置并绑定服务端的channel
                // 保持连接数
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 有数据立即发送
                .option(ChannelOption.TCP_NODELAY, true)
                // 保持连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                // 处理新连接
                .childHandler(new ChannelInitializer<SocketChannel>() {//设置了客户端连接socket属性。
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        // 增加任务处理
                        ChannelPipeline p = sc.pipeline();
                        p.addLast(new DecoderHandler(), // 自定义解码器
                                //默认的编码器
                                new StringEncoder(Charset.forName("utf-8")),
                                new StringDecoder(Charset.forName("utf-8")),
                                // 自定义的处理器
                                // new ServerHandler()
                                nettyServerHandler);
                    }
                });

        // 绑定端口,同步等待成功
        ChannelFuture future;
        try {
            log.info("netty服务器在[{}]端口启动监听",serverProperties.getPort());
            future = bootstrap.bind(serverProperties.getPort()).sync();//真正让netty跑起来的重点
            if (future.isSuccess()) {
                serverSocketChannel = (ServerSocketChannel) future.channel();
                log.info("netty服务开启成功");
            } else {
                log.info("netty服务开启失败");
            }
            // 等待服务监听端口关闭,就是由于这里会将线程阻塞,导致无法发送信息,所以我这里开了线程
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 优雅地退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

第五步:配置启动类

import com.cloud.nettysocket.service.NettySocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;

/**
 * @author zhangbl
 * @version 1.0.0
 * @Description 消息通讯模块
 * @Date : 2022/5/25 14:17
 **/
@SpringCloudApplication
public class NettySocketApplication implements CommandLineRunner {

	@Autowired
	private NettySocketServer nettySocketServer;

	public static void main(String[] args) {
		SpringApplication.run(NettySocketApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		nettySocketServer.start();
	}
}

启动项目 使用网络调试工具连接 127.0.0.1 4001 端口 注意!!! 这个端口不是项目端口 时配置的socket端口

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
# 基于Nettysocket server ------ ## 介绍 使用Netty分别实现了三个Socket server和一个socket client: > * server1:9099 主要用来跟硬件传感器通信 > * server2:8888/websocket 作为websocket服务端跟网页通信 > * server2:8889/websocket 跟storm服务做数据通信,同时也作为websocket服务端跟网页通信 > * client 作为模拟客户端,跟server1建立连接后,不断给服务端发送假数据 整个项目启动后,主要做了下面几件事: - [ ] 创建socket server和socket client,并建立连接 - [ ] 执行定时任务,每5秒socket server往所有连接的socket client发送请求数据命令 - [ ] socket client接受到请求数据的命令后,从mysql中读取假数据,伪造成真实设备传输的数据格式,并发送给socket server - [ ] socket server接收到返回的数据后,分别写入到hbase数据库和kafka队列中 - [ ] 最后调用websocket server,往所有跟它建立的客户端发送接收到的数据 ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值