Nerry实现服务器端指定客户端发送消息。

目录

pom依赖

Server端

main方法初始化netty

服务端端主动给客户端发送消息


pom依赖

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

Server端

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;

import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;

public class NettyServer{

    /** 记录所有客户端连接, key=用户id, value=ChannelHandlerContext */
    public static ConcurrentHashMap<String, ChannelHandlerContext> userIdMap = new ConcurrentHashMap();

    /** 记录所有客户端连接, key=客户端地址和端口, value=ChannelHandlerContext */
    public static ConcurrentHashMap<String, ChannelHandlerContext> remoteAddressMap = new ConcurrentHashMap();

    /**
     * netty启动端口号
     */
    public static int port = 8081;

    public void initServer() {
        /**
         *  客户端创建两个线程池组分别为 boss线程组和工作线程组
         */
        // boss线程池,用于接受客户端连接的请求 (并没有处理请求)
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // work线程池,用于处理客户端连接的读写操作,三个线程
        NioEventLoopGroup workGroup = new NioEventLoopGroup(3);

        // 用于创建我们的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();
            System.out.println("关闭服务器");
        } 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 {
        // msg消息协议格式:id/消息内容
        String[] split = msg.split("/");
        NettyServer.userIdMap.put(split[0], channelHandlerContext);

        System.out.println("接收客户端消息:" + msg);
        // 响应客户端内容:
        channelHandlerContext.writeAndFlush("00");

        InetSocketAddress ipSocket = (InetSocketAddress)channelHandlerContext.channel().remoteAddress();
        System.out.println("获取客户端ip:" + ipSocket.getAddress().toString());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // remoteAddress = /192.168.52.1:59807
        String remoteAddress = ctx.channel().remoteAddress().toString();
        System.out.println("有客户端连接... 客户端地址" + remoteAddress);
        NettyServer.remoteAddressMap.put(remoteAddress, ctx);
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String remoteAddress = ctx.channel().remoteAddress().toString();
        System.out.println("有客户端断开连接... " + remoteAddress);
        NettyServer.remoteAddressMap.remove(remoteAddress);
        super.channelInactive(ctx);
    }
}

main方法初始化netty

@SpringBootApplication
@MapperScan(basePackages = "com.kc.monitor.mapper")
public class MonitorApplication {

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

		// 初始化Netty服务器
		NettyServer nettyServer = new NettyServer();
		nettyServer.initServer();
	}

}

服务端端主动给客户端发送消息

发送前提是以有客户端连接了服务器端。

注意:修改address客户端地址,也可以直接从remoteAddressMap中读取客户端地址。

    /**
     * 两种发送模式:
     * 1.根据远程客户端ip端口发送消息
     * 2.根据用户id发送消息
     *
     * 目前版本使用方式1比较完善,已经实现客户端断开连接从map中删除该连接,第2种方式还没实现断开连接删除;按照需求可以两种方式结合一下。
     */
    @GetMapping("/test")
    public void add(String msg){
        System.out.println(NettyServer.remoteAddressMap.size());
        System.out.println(NettyServer.userIdMap.size());

        // 1.根据客户端ip地址发送,发送消息到“/192.168.52.1:59807”客户端
        String address = "/192.168.52.1:59807";
        if (NettyServer.remoteAddressMap.containsKey(address)){
            NettyServer.remoteAddressMap.get(address).writeAndFlush(msg);
        }

        // 2.给用户id为1的客户端发送消息
        String userId = "1";
        if (NettyServer.userIdMap.containsKey(userId)){
            NettyServer.userIdMap.get(userId).writeAndFlush(msg);
        }

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

祁_z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值