Netty应用:一个简单的C/S通信模型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40178464/article/details/90137076

利用Netty实现一个简单的通信模型,看程序:
1.服务器端:
Server类:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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 Server {
    public void bind(int port) {
        /**
         * NioEventLoopGroup 是用来处理I/O操作的多线程事件处理器
         * Netty 提供了许多不同的 EventLoopGroup的实现来处理不同的传输
         * 本例中实现服务端的应用,因此会有2个NioEventLoopGroup 会被使用
         * 一个是main,也叫做boss,用来接收进来的连接
         * 一个是work,用来处理已经接受的连接
         * 一旦boss接收到连接,就会把信息注册到worker上
         * 如何知道多少个线程已经被使用,如何映射到已经创建的channel上都需要依赖于EventLoopGroup的实现
         * 并且可以通过构造函数来配置他们的关系
         */
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            /**
             * ServerBootstrap 是一个启动NIO的辅助启动类
             * 可以在这个服务中直接使用channel
             */
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
                    //这里指定使用NioServerSocketChannel类来举例说明一个新的channel如何接受进来的连接
                    .channel(NioServerSocketChannel.class)
//                    .localAddress(port)
                    /**
                     * 这里的事件处理类经常被用来处理一个最近结束的channel
                     * 匿名内部类 继承自ChannelInitializer 是一个特殊的处理类,他的目的是帮助使用者配置一个新的channel
                     */
                    .childHandler( new ChannelInitializer<SocketChannel>() {
                        //添加serverHandler到channel的channelPipeline
                        //通过ServerHandler给每一个新来的Channel初始化
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("handler", new ServerHandler());
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                        }
                    })
                    //设置TCP协议的请求等待队列
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            //绑定端口,调用sync()同步阻塞方法等待完成
            ChannelFuture sync = bootstrap.bind(port).sync();
            System.out.println("服务端监听端口:"+port +" 启动成功");

            //使用sync方法进行阻塞,等待服务端链路关闭之后main函数退出
            sync.channel().closeFuture().sync();
        } catch (Exception e) {

        } finally {
            //释放线程池资源
            workGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
            System.out.println("server 关闭了");
        }
    }
}

ServerHandler类:该类主要负责具体的业务实现

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.SocketAddress;

public class ServerHandler extends SimpleChannelInboundHandler<String> {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel channel = ctx.channel();
        // 通过channel获取到对应的客户端ip地址
        SocketAddress remoteAddress = channel.remoteAddress();
        // 获取通道中的信息
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);

        String s1 = new String(bytes, "UTF-8");
        System.out.println(remoteAddress+":"+s1);
        channel.writeAndFlush("[server recv]"+s1);
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"上线了");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress()+"下线了");
    }
}


2.客户端

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.net.InetSocketAddress;
import java.util.Scanner;

public class Client {
    private String host;
    private int port;

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

    public void start(){
    	// 与服务器端类似
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            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 {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("handler", new ClientHandler());
                            pipeline.addLast("decoder", new StringDecoder());
                            pipeline.addLast("encoder", new StringEncoder());
                        }
                    });
            Channel channel = bootstrap.connect().sync().channel();
            System.out.println("客户端连接服务器成功");
            Scanner scanner = new Scanner(System.in);
            scanner.useDelimiter("\n");

            while (scanner.hasNext()) {
                String msg = scanner.nextLine();
                if ("exit".equals(msg)) {
                    System.out.println("我下线了");
                    channel.close();
                    return;
                }
                if (" ".equals(msg)) {
                    continue;
                }
                channel.writeAndFlush(msg);
            }
        } catch (Exception e) {
            e.getStackTrace();
        } finally {
            group.shutdownGracefully();
            System.out.println("客户端关闭了");
        }
    }
}

ClientHandler类:主要负责客户端的功能实现

public class ClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);

        String s1 = new String(bytes, "UTF-8");
        System.out.println(s1);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
//        System.out.println("recv:"+msg);
    }
}

注:这里有个问题是有时候channelRead()方法中可以读取到通道中的信息,有时候channelRead0()方法会读取到同道中的信息,个人猜测与JDK版本相关,使用时应先试一试哪个方法可用,两个方法的区别在于channelRead()方法获取到的信息需要通过ByteBuf 转化成String型,而channelRead0()方法获取到的直接是Strring型。



3.服务器与客户端的启动
服务器:

public class ServerTest {
    public static void main(String[] args) {
        Server server = new Server();
        server.bind(9999);
    }
}

客户端:

public class ClientTest {
    public static void main(String[] args) {
        Client client = new Client("127.0.0.1", 9999);
        client.start();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40178464/article/details/90137076
今日推荐