效果图:服务端发送消息,客户端进行接收

Netty客户端和服务端发送接收消息_bootstrap

 

说明:我们这个netty需要用到jar包,请大家自行下载,添加

jar下载地址:http://netty.io/downloads.html
netty官方地址: http://netty.io/
netty4.x 官方地址:http://netty.io/wiki/user-guide-for-4.x.html
Netty 实现聊天功能:https://waylau.com/netty-chat/

1.服务端主方法main:

package comtest.example.admin.netty.server;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import comtest.example.admin.netty.client.NettyClientHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * Created by wrs on 2019/6/3,10:35
 * projectName: Testz
 * packageName: comtest.example.admin.netty.server
 */
public class NettyServer {
    private int port = 20803;
    //维护设备在线的表
    private Map<String, Integer> clientMap = new HashMap<>();

    public synchronized void setClient(String name) {
        this.clientMap.put(name, 1);
    }

    public synchronized void removeClient(String name) {
        this.clientMap.remove(name);
    }

    //判断连接处里面是否有东西
    public synchronized boolean getClientMapSize() {
        return this.clientMap.size() > 0;
    }

    //维护设备连接的map 用于推送消息
    private Map<String, Channel> channelMap = new HashMap<>();

    public synchronized void setChannel(String name, Channel channel) {
        this.channelMap.put(name, channel);
    }

    public synchronized Map<String, Channel> getChannelMap() {
        return this.channelMap;
    }

    //发送消息给下游设备
    public boolean writeMsg(String msg) {
        boolean errorFlag = false;
        Map<String, Channel> channelMap = getChannelMap();
        if (channelMap.size() == 0) {
            return true;
        }
        Set<String> keySet = clientMap.keySet();
        for (String key : keySet) {
            try {
                Channel channel = channelMap.get(key);
                if (!channel.isActive()) {
                    errorFlag = true;
                    continue;
                }
                channel.writeAndFlush(msg + System.getProperty("line.separator"));
            } catch (Exception e) {
                errorFlag = true;
            }
        }
        return errorFlag;
    }

    public void bind() {
        System.out.println("service start successful");
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        //特殊分隔符
                        pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,
                                Unpooled.copiedBuffer(System.getProperty("line.separator").getBytes())));
                        pipeline.addLast("decoder", new StringDecoder());
                        pipeline.addLast("encoder", new StringEncoder());
                        pipeline.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                        pipeline.addLast("handler", new NettyServerHandler(NettyServer.this));
                    }
                });
        try {
            ChannelFuture f = bootstrap.bind(port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        final NettyServer nettyServer = new NettyServer();
        new Thread() {
            @Override
            public void run() {
                nettyServer.bind();
            }
        }.start();
        Scanner scanner = new Scanner(System.in);
        String msg = "";
        while (!(msg = scanner.nextLine()).equals("exit")) {
            System.out.println(nettyServer.writeMsg(msg));
        }
    }

}

2.服务端类

package comtest.example.admin.netty.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * Created by wrs on 2019/6/3,11:13
 * projectName: Testz
 * packageName: comtest.example.admin.netty.server
 */
public class NettyServerHandler extends SimpleChannelInboundHandler{
    private int counter=0;
    private NettyServer nettyServer;

    public NettyServerHandler(NettyServer nettyServer){
        this.nettyServer = nettyServer;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        System.out.println("client say"+o.toString());
        //重置心跳次数
        counter = 0;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        String clientName = ctx.channel().remoteAddress().toString();
        System.out.println("RemoteAddress:"+clientName+"active!");
        nettyServer.setClient(clientName);
        nettyServer.setChannel(clientName,ctx.channel());
        super.channelActive(ctx);
        counter = 0;
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent)evt;
            if (event.state().equals(IdleState.READER_IDLE)){
                //空闲4s后触发
                if (counter>=10){
                    ctx.channel().close().sync();
                    String clientName = ctx.channel().remoteAddress().toString();
                    System.out.println(""+clientName+"offline");
                    nettyServer.removeClient(clientName);
                    //判断是否有在线的
                    if (nettyServer.getClientMapSize()){
                        return;
                    }
                }else{
                    counter++;
                    System.out.println("loss"+counter+"count HB");
                }
            }
        }
    }
}

3.客户端main方法:

package comtest.example.admin.netty.client;


import java.util.concurrent.TimeUnit;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * Created by wrs on 2019/6/3,10:04
 * projectName: Testz
 * packageName: comtest.example.admin.netty.client
 */
public class NettyClient {
    private String host;
    private int port;
    private Channel channel;
    private Bootstrap b = null;

    public NettyClient(String host, int port) {
        this.host = host;
        this.port = port;
        init();
    }

    private void init() {
        b = new Bootstrap();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        b.group(workerGroup).option(ChannelOption.SO_KEEPALIVE, true)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,
                                Unpooled.copiedBuffer(System.getProperty("line.separator").getBytes())));
                        //字符串编码解码
                        pipeline.addLast("decoder", new StringDecoder());
                        pipeline.addLast("encoder", new StringEncoder());
                        //心跳检测
                        pipeline.addLast(new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS));
                        //客户端的逻辑
                        pipeline.addLast("handler", new NettyClientHandler(NettyClient.this));

                    }
                });
    }

    public void start() {
        ChannelFuture f = b.connect(host, port);
        //断线重连
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if (!channelFuture.isSuccess()) {
                    final EventLoop loop = channelFuture.channel().eventLoop();
                    loop.schedule(new Runnable() {
                        @Override
                        public void run() {
                            System.out.println("not connect service");
                            start();
                        }
                    }, 1L, TimeUnit.SECONDS);
                } else {
                    channel = channelFuture.channel();
                    System.out.println("connected");
                }
            }
        });
    }

    public Channel getChannel() {
        return channel;
    }

    public static void main(String[] args) {
        NettyClient nettyClient = new NettyClient("127.0.0.1", 20803);
        nettyClient.start();
    }
}

4.客户端类:

package comtest.example.admin.netty.client;

import java.util.concurrent.TimeUnit;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

/**
 * Created by wrs on 2019/6/3,10:03
 * projectName: Testz
 * packageName: comtest.example.admin.netty.client
 */
public class NettyClientHandler extends SimpleChannelInboundHandler{
    private NettyClient nettyClient;
    private String tenantId;
    private int attempts = 0;

    public NettyClientHandler(NettyClient nettyClient){
        this.nettyClient = nettyClient;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        System.out.println("service send message"+o.toString());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("output connected!");
        attempts = 0;
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("offline");
        //使用过程中断线重连
        final EventLoop eventLoop = ctx.channel().eventLoop();
        if (attempts<12){
            attempts++;
        }
        int timeout = 2<<attempts;
        eventLoop.schedule(new Runnable() {
            @Override
            public void run() {
                nettyClient.start();
            }
        },timeout, TimeUnit.SECONDS);
        ctx.fireChannelInactive();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent)evt;
            if (event.state().equals(IdleState.READER_IDLE)){
                System.out.println("READER_IDLE");
            }else if (event.state().equals(IdleState.WRITER_IDLE)){
                //发送心跳,保持长连接
                String s = "NettyClient"+System.getProperty("line.separator");
                ctx.channel().writeAndFlush(s);  //发送心跳成功
            }else if (event.state().equals(IdleState.ALL_IDLE)){
                System.out.println("ALL_IDLE");
            }
        }
        super.userEventTriggered(ctx,evt);
    }
}

到这里就写完了,这个netty很多的东西都是固定写法,如果涉及到用户逻辑,请自行处理,这个例子,我们先运行server的main方法,启动服务,然后再运行client的main方法,启动客户端,然后再server的控制台输入字符串,可以通过tcp长连接的方式,发送到客户端