1. 程式人生 > >netty學習二:基於socket通訊的小demo

netty學習二:基於socket通訊的小demo

概述

上次寫了一篇netty學習一:用netty構造http服務的小demo
簡單介紹如何使用netty編寫http程式,而基於socket程式設計才是netty的強項。

服務端程式碼

package socket.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import
io.netty.channel.socket.nio.NioServerSocketChannel; public class SocketServer { public static void main(String[] args) throws InterruptedException { // 接收連線,但是不處理 EventLoopGroup parentGroup = new NioEventLoopGroup(); // 真正處理連線的group EventLoopGroup childGroup = new NioEventLoopGroup(); try
{ //載入Initializer ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(parentGroup, childGroup) .channel(NioServerSocketChannel.class) //這裡的childHandler是服務於childGroup的,如果直接使用 //handler方法新增處理器,則是服務於parentGroup的
.childHandler(new SocketServerInitializer()); //繫結監聽埠 ChannelFuture channelFuture = serverBootstrap.bind(8899).sync(); channelFuture.channel().closeFuture().sync(); } finally { parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } } } package socket.server; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; public class SocketServerInitializer extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //1、新增解碼器,用於解釋二進位制內容 pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4)); //2、編碼器,用於計算訊息的長度,並把訊息長度以二進位制的形式追加到訊息的前面 pipeline.addLast(new LengthFieldPrepender(4)); //3、socket程式設計中需要對字串進行編碼解碼 pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8)); pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8)); //4、新增自定義處理器 pipeline.addLast(new SocketServerHandler()); } } package socket.server; import java.util.UUID; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class SocketServerHandler extends SimpleChannelInboundHandler<String>{ @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("請求來自:"+ctx.channel().remoteAddress() + ",內容:" + msg); ctx.channel().writeAndFlush("from server:"+UUID.randomUUID()); } }

客戶端程式碼

package socket.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;

public class SocketClient {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {
            //載入Initializer
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup)
                           .channel(NioSocketChannel.class)
                           .handler(new SocketClientInitializer());

            //連線服務端
            ChannelFuture channelFuture = bootstrap.connect("localhost", 8899).sync();
            channelFuture.channel().closeFuture().sync();
        }
        finally {
            eventLoopGroup.shutdownGracefully();
        }

    }
}

package socket.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class SocketClientInitializer extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //1、新增解碼器,用於解釋二進位制內容
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));

        //2、編碼器,用於計算訊息的長度,並把訊息長度以二進位制的形式追加到訊息的前面
        pipeline.addLast(new LengthFieldPrepender(4));

        //3、socket程式設計中需要對字串進行編碼解碼
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));

        //4、新增自定義處理器
        pipeline.addLast(new SocketClientHandler());
    }
}

package socket.client;

import java.time.LocalDateTime;

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

public class SocketClientHandler extends SimpleChannelInboundHandler<String>{
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("伺服器端地址:"+ctx.channel().remoteAddress());
        System.out.println("client receive:"+msg);
        ctx.channel().writeAndFlush("from client:"+LocalDateTime.now());
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush("你好,服務端");
    }
}

當客戶端於服務端之間的channel建立後,客戶端程式可以在channelActive方法中給服務端傳送訊息,
服務端接收到訊息後,觸發SocketServerHandler類的channelRead0方法,給客戶端傳送訊息,
客戶端接收到訊息後,觸發了SocketClientHandler類的channelRead0的方法,給服務端傳送訊息。
無限發下去。

csdn code 路徑

這個專案的原始碼放置在csdn code上,歡迎訪問。