Netty实现长连接,客户端随时发送消息给服务端,可在任意代码位置发送消息给服务端

目录

pom依赖

netty服务端代码

netty客户端

PosttingObject封装netty客户端连接信息

测试客户端发送消息到服务器端


1. 可以实现socket长连接,心跳机制每隔N秒客户端给服务器发送一条消息,代表客户端还存活。

2. 可以实现在随意代码位置按照用户id标识,发送消息给服务端。


pom依赖

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

netty服务端代码

package com.kc.monitor.core.utils.netty;
 
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
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;
 
public class NettyServer {
 
    /**
     * netty启动端口号
     */
    private static int port = 8080;
 
    public static void main(String[] args) {
        /**
         *  客户端创建两个线程池组分别为 boss线程组和工作线程组
         */
        // 用于接受客户端连接的请求 (并没有处理请求)
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // 用于处理客户端连接的读写操作
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        // 用于创建我们的ServerBootstrap
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // LineBasedFrameDecoder解决粘包-解包问题,设置我们分割最大长度为1024
                        // 原理就是自动帮我们把带有\n或\r\n的数据进行换行
                        //socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                        socketChannel.pipeline().addLast(new StringEncoder());// String编码器
                        socketChannel.pipeline().addLast(new StringDecoder());// String解码器
                        socketChannel.pipeline().addLast(new ServerHandler());// 管道类-接收发送消息
                    }
                });
        //  绑定我们的端口号码
        try {
            // 绑定端口号,同步等待成功
            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("服务器启动成功:" + port);
            // 等待服务器监听端口
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
 
        } finally {
            // 优雅的关闭连接
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
 
}
 
 
class ServerHandler extends SimpleChannelInboundHandler<String> {
 
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        System.out.println("8080-msg:" + msg);
        // 响应内容:
        channelHandlerContext.writeAndFlush("8080\n"); // String类型加上\n会自动粘包和拆包了
    }
}

netty客户端

package com.kc.monitor.core.utils.netty;
 
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
 
import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;

public class NettyClient {

    /** 存储用户id和netty连接关系 */
    public static ConcurrentHashMap<String, PosttingObject> concurrentHashMap = new ConcurrentHashMap();

    public ClientHandler clientHandler = new ClientHandler();

    public void initNetty(String userId, String host, int port){
        //创建nioEventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group).channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(host, port))
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        System.out.println("正在连接中...");
                        ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new StringEncoder());
                        ch.pipeline().addLast(clientHandler);
                    }
                });

        try {
            // 发起连接
            ChannelFuture sync = bootstrap.connect().sync();
            System.out.println("用户:"+userId + "->服务端连接成功...");

            /* // 发送消息
            sync.channel().writeAndFlush(msg);
            System.out.println("消息发送完成");
            // 关闭连接
            sync.channel().close();*/

            // 异步等待关闭连接channel
            // sync.channel().closeFuture().sync();
            // System.out.println("连接已关闭..");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //group.shutdownGracefully();
            //System.out.println("优雅关闭连接");
        }

        PosttingObject posttingObject = new PosttingObject();
        posttingObject.setNioEventLoopGroup(group);
        posttingObject.setNettyClient(this);
        concurrentHashMap.put(userId, posttingObject);
    }

}
 
 
class ClientHandler extends SimpleChannelInboundHandler<String> {

    public ChannelHandlerContext channelHandlerContext;

    /**
     * 活跃通道可以发送消息
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        channelHandlerContext = ctx;
        //channelHandlerContext.writeAndFlush("11111");
        //channelHandlerContext.channel().close();
    }
 
    /**
     * 读取消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        System.out.println("接收消息:" + msg);
        //channelHandlerContext.channel().close();
    }
}

PosttingObject封装netty客户端连接信息

package com.kc.monitor.core.utils.netty;

import io.netty.channel.nio.NioEventLoopGroup;
import lombok.Data;

/**
 * @title PosttingObject 记录netty连接
 * @description
 * @author qizhentao
 * @date 2021-12-23 11:14
 */
@Data
public class PosttingObject {

    private NettyClient nettyClient;

    private NioEventLoopGroup nioEventLoopGroup;

}

测试客户端发送消息到服务器端

package com.kc.monitor.core.utils.netty;


/**
 * @title NettyClientSendMsg
 * @description
 * @author qizhentao
 * @date 2021-12-23 11:03
 */
public class NettyClientSendMsg {

    private static final String host = "192.168.52.1";
    private static final int port = 8080;

    public static void main(String[] args) {
        // 初始化Netty连接
        String userId = "1";
        new NettyClient().initNetty(userId, host, port);



        // 根据用户id发送消息到客户端
        PosttingObject posttingObject = NettyClient.concurrentHashMap.get(userId);
        try{
            // 发送消息
            NettyClient nettyClient = posttingObject.getNettyClient();
            nettyClient.clientHandler.channelHandlerContext.writeAndFlush("01\n");
            nettyClient.clientHandler.channelHandlerContext.writeAndFlush("02\n");
            nettyClient.clientHandler.channelHandlerContext.writeAndFlush("03\n");
        }finally {
            // 优雅关闭连接
            posttingObject.getNioEventLoopGroup().shutdownGracefully();
        }
    }


}

好的,以下是一个简单的 Spring Boot 整合 Netty 实现 TCP 发送消息的完整服务端代码: ```java @Component public class NettyServer { private final EventLoopGroup bossGroup = new NioEventLoopGroup(); private final EventLoopGroup workerGroup = new NioEventLoopGroup(); @Value("${netty.port}") private int port; @PostConstruct public void start() throws Exception { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new NettyServerHandler()); } }); ChannelFuture future = bootstrap.bind(port).sync(); if (future.isSuccess()) { System.out.println("Netty server started on port " + port); } } @PreDestroy public void stop() throws Exception { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } ``` 其中,NettyServerHandler 是自定义的处理器,用于处理接收到的消息。你需要根据你的业务需求实现自己的 NettyServerHandler。 在上面的代码中,我们在 NettyServer 类上添加了 @Component 注解,将其声明为 Spring Bean。这样,NettyServer 就可以被 Spring 容器扫描到并自动启动。 在启动 NettyServer 之前,需要在 application.yml 中配置 Netty 的端口: ```yaml netty: port: 8080 ``` 启动 NettyServer 后,就可以接收客户端发送消息了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祁_z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值