2、netty粘包和半包實例代碼

注:源代碼來自享學課堂,學習之後所做筆記,方便回顧,也給大家一個參考

1、Client主函數

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

public class EchoClient {

    private final int port;
    private final String host;

    public EchoClient(int port, String host) {
        this.port = port;
        this.host = host;
    }
    //netty的標準程序基本都是下面這個流程
    public void start() throws InterruptedException {
        /*線程組*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*客戶端啓動必備,服務端使用ServerBootstrap*/
            Bootstrap b = new Bootstrap();
            b.group(group)
                    /*指明使用NIO進行網絡通訊*/
                .channel(NioSocketChannel.class)
                    /*配置遠程服務器的地址,服務器端就是配置LocalAdderss*/
                .remoteAddress(new InetSocketAddress(host,port))
                    //這裏配置我們自定義的handler,netty主要就是寫各種各樣的handler
                .handler(new EchoClientHandler());
            /*連接到遠程節點,阻塞等待直到連接完成*/
            ChannelFuture f = b.connect().sync();
            /*阻塞,直到channel關閉*/
            f.channel().closeFuture().sync();
        } finally {
            //阻塞關閉線程組
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient(8080,"127.0.0.1").start();
    }
}

2、自定義客戶端Handler

在channelActive建立連接之後,循環100次數據,本來應該是server接受100次的,但是看最後的結果只接受了兩次

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

//SimpleChannelInboundHandler幫我們封裝好了很多東西,客戶端和服務端都是使用ByteBuf通信的
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    /*客戶端讀取到服務器發送過來的數據了,在這裏做處理*/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
            throws Exception {
        System.out.println("Client accept:" + msg.toString(CharsetUtil.UTF_8));
    }

    /*客戶端被通知channel活躍以後,做事*/
    //這裏循環發送數據
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (int i = 0; i < 100; i++) {
            ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,Netty ",
                    CharsetUtil.UTF_8));
        }
    }

    //客戶端讀取數據完畢之後做事
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete");
    }

    //發生異常的處理
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

4、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 java.net.InetSocketAddress;

public class EchoServer  {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 8080;
        EchoServer echoServer = new EchoServer(port);
        System.out.println("服務器即將啓動");
        echoServer.start();
        System.out.println("服務器關閉");
    }

    //代碼大致和客戶端相同,不同1\ServerBootstrap,2\childHandler
    public void start() throws InterruptedException {
        /*線程組*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();/*服務端啓動必備*/
            b.group(group)
                    /*指明使用NIO進行網絡通訊*/
            .channel(NioServerSocketChannel.class)
                    //.channel(EpollServerSocketChannel.class)
                    /*指明服務器監聽端口*/
                    .localAddress(new InetSocketAddress(port))
                    /*接收到連接請求,新啓一個socket通信,也就是channel,每個channel
                    * 有自己的事件的handler*/
                    .childOption(ChannelOption.TCP_NODELAY,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            ChannelFuture f = b.bind().sync();/*綁定到端口,阻塞等待直到連接完成*/
            /*阻塞,直到channel關閉*/
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }
}

5、server端的Handler

counter.incrementAndGet()用來計算客戶端發送數據的次數,客戶端循環了100次,但是服務端只接受了
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.concurrent.atomic.AtomicInteger;

/*指明我這個handler可以在多個channel之間共享,意味這個實現必須線程安全的。*/
@ChannelHandler.Sharable//如果不加這個註解,主函數在.childHandler(new EchoServerHandler()),確保每個鏈接都單獨的handler
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    private AtomicInteger counter = new AtomicInteger();

    /*** 服務端讀取到網絡數據後的處理*/
    //計算接受數據次數,按道理客戶端發送了100次,也應該接受100次,結果只有兩次
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        ByteBuf in = (ByteBuf) msg;/*netty實現的緩衝區*/
        System.out.println("Server Accept:" + in.toString(CharsetUtil.UTF_8) + counter.incrementAndGet());
        ctx.write(in);
    }

    /*** 服務端讀取完成網絡數據後的處理*/
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx)
            throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)/*flush掉所有的數據*/
                .addListener(ChannelFutureListener.CLOSE);/*當flush完成後,關閉連接*/
    }

    /*** 發生異常後的處理*/
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

6、運行結果

server

服務器即將啓動
Server Accept:Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hell1
Server Accept:o,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty Hello,Netty 2

這裏只發生了兩次,即粘包

client

Client accetp:Hello,Netty
channelReadComplete
channelReadComplete

Process finished with exit code 0

下一篇介紹netty解決粘包半包的三種基本方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章