netty學習之一:第一個netty程序

最近在學習netty,看了幾天的博客以後,打算本身寫一個練手的程序。java

這個程序很簡單:客戶端發送一個ping,服務端會相應地回覆一個pong,當監測到服務端失去鏈接後,即斷開。bootstrap

整個代碼分爲client與server兩部分,結構以下:服務器

引入netty包:socket

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

client端:ide

PingClient.javaoop

package org.attempt.netty4.demo001.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class PingClient {

    /** 服務器IP地址 */
    private String host = "127.0.0.1";

    /** 服務器端口 */
    private int port = 8000;

    private EventLoopGroup group = null;
    private Bootstrap b = null;
    private Channel channel = null;

    public PingClient() throws Exception {
        group = new NioEventLoopGroup();
        b = new Bootstrap();
        b.group(group)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline()
                                //字符串解碼和編碼
                                .addLast(new StringDecoder())
                                .addLast(new StringEncoder())
                                //客戶端的邏輯
                                .addLast(new PingClientHandler());
                    }
                });
    }

    public Channel getChannel() throws Exception {
        if(null == channel || !channel.isActive()) {
            channel = b.connect(host, port).sync().channel();
        }
        return channel;
    }

    public static void main(String[] args) throws Exception {
        PingClient client = null;
        try {
            client = new PingClient();
            Channel channel = client.getChannel();

            while(true) {
                //輸出channel的狀態,對應於close()
                System.out.println(channel.isOpen());
                //判斷鏈接狀態
                if(channel.isActive()) {
                    channel.writeAndFlush("ping");
                } else {
                    System.out.println("失去鏈接,關閉客戶端");
                    channel.close();
                    break;
                }
                Thread.sleep(5000);
            }
        } finally {
            if(null != client) {
                client.stop();
            }
        }
    }

    public void stop() {
        if(null != group) {
            //優雅退出,釋放線程池資源
            group.shutdownGracefully();
        }
    }

}

PingClientHandler.java學習

package org.attempt.netty4.demo001.client;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class PingClientHandler extends SimpleChannelInboundHandler {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("receive from server: " + msg.toString());
    }

}

server端: PongServer.java編碼

package org.attempt.netty4.demo001.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class PongServer {

    public static void main(String[] args) throws Exception {
        int port = 8000;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
                //採用默認值
            }
        }
        new PongServer().bind(port);
    }

    public void bind(int port) throws Exception {
        //配置服務端的NIO線程組
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline()
                            //字符串解碼和編碼
                            .addLast(new StringDecoder())
                            .addLast(new StringEncoder())
                            //服務器的邏輯
                            .addLast(new PongServerHandler());
                        }
                    });

            //綁定端口,同步等待成功
            ChannelFuture f = b.bind(port).sync();

            //等待服務器監聽端口關閉
            f.channel().closeFuture().sync();
        } finally {
            //優雅退出,釋放線程池資源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

PongServerHandler.java.net

package org.attempt.netty4.demo001.server;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * @author admin
 * @date 2018-09-06 22:48
 */
public class PongServerHandler extends SimpleChannelInboundHandler {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("receive from client: " + msg.toString());
        //返回客戶端消息
        if(msg.toString().equals("ping")) {
            ctx.writeAndFlush("pong");
        } else {
            ctx.writeAndFlush("UNKONWN");
        }
    }

}

運行,先啓動server端,再啓動client端,結果以下:線程

  • server端:
receive from client: ping
receive from client: ping
receive from client: ping
receive from client: ping
receive from client: ping

Process finished with exit code -1
  • client端:
true
receive from server: pong
true
receive from server: pong
true
receive from server: pong
true
receive from server: pong
true
receive from server: pong
false
失去鏈接,關閉客戶端

代碼中的一些類及用法,後面再說。

相關文章
相關標籤/搜索