TCP-IP學習筆記六:Netty使用--簡單通信編程2

TCP/IP學習筆記六:Netty使用–簡單通信編程2

標籤(空格分隔): Netty 網絡編程


Netty進行對象類型數據的傳遞實例。

編程步驟與簡單類型數據傳遞相同。

服務器端

package com.netty.demo2.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Netty NIO 服務器端
 * @author MOTUI
 *
 */
public class NettyNIOServer {

    public static void main(String[] args) throws InterruptedException {

        //1.創建NIOServerSocketChannel的服務引導
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //2.創建線程池 boss(請求轉發) worker(IO事件處理)
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        //3.綁定線程
        serverBootstrap.group(boss, worker);
        //4.設置ServerSocket服務類
        serverBootstrap.channel(NioServerSocketChannel.class);
        //5.綁定IO處理事件
        serverBootstrap.childHandler(new ServerChannelInitializer());
        //6.綁定服務端口
        System.out.println("服務器監聽8989端口");
        ChannelFuture future = serverBootstrap.bind(8989).sync();
        //等待服務器被關閉
        future.channel().closeFuture().sync();

        //釋放線程資源
        worker.shutdownGracefully();
        boss.shutdownGracefully();  
    }   
}

註冊IO事件處理類

    ch.pipeline().addLast(new ObjectCodec());
    這行代碼是注入ObjectCodec()對接收數據的編解碼操作
package com.netty.demo2.server;

import com.netty.demo2.ObjectCodec;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;

public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
    /**
     * 註冊IO事件處理類
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast(new ObjectCodec());
        ch.pipeline().addLast(new ServerRequestResponseHander());
    }
}

IO事件處理類

package com.netty.demo2.server;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * 事件處理類
 * @author MOTUI
 *
 */
public class ServerRequestResponseHander extends ChannelHandlerAdapter {
    /**
     * 異常調用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        System.out.println("發生異常了···異常信息:"+cause.getMessage());

    }

    /**
     * 讀數據調用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("客戶端發請求了···服務器接收的數據:"+msg);
        ctx.writeAndFlush(msg);//寫回響應
    }
}

客戶端

package com.netty.demo2.client;

import java.net.InetSocketAddress;

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

/**
 * Netty NIO 客戶端
 * @author MOTUI
 *
 */
public class NettyNIOClient {

    public static void main(String[] args) throws InterruptedException {

        //1.創建NIOSocketChannel的服務引導
        Bootstrap bootstrap = new Bootstrap();
        //2.創建線程池 worker(IO事件處理)
        NioEventLoopGroup boss = new NioEventLoopGroup();
        //3.關聯線程池
        bootstrap.group(boss);
        //4.設置NioSocketChannel
        bootstrap.channel(NioSocketChannel.class);
        //5.綁定IO處理事件
        bootstrap.handler(new ClientChannelInitializer ());
        //6.鏈接服務器
        ChannelFuture future = bootstrap.connect(new InetSocketAddress("192.168.0.117", 8989));

        //等待關閉連接
        future.channel().closeFuture().sync();

        //釋放資源
        boss.shutdownGracefully();
    }   
}

客戶端註冊IO事件處理類

    ch.pipeline().addLast(new ObjectCodec());
    這行代碼是注入ObjectCodec()對接收數據的編解碼操作
package com.netty.demo2.client;

import com.netty.demo2.ObjectCodec;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;

public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
    /**
     * 註冊IO事件處理類
     */
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ch.pipeline().addLast(new ObjectCodec());
        ch.pipeline().addLast(new ClientRequestResponseHander());
    }
}

客戶端IO事件處理類

package com.netty.demo2.client;

import com.netty.demo2.User;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

/**
 * 事件處理類
 * @author MOTUI
 *
 */
public class ClientRequestResponseHander extends ChannelHandlerAdapter {
    /**
     * 異常調用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        System.out.println("發生異常了···異常信息:"+cause.getMessage());

    }

    /**
     * 發送請求
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        ctx.writeAndFlush(new User(1, "guozh"));
    }


    /**
     * 讀數據調用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {

        System.out.println("客戶端接收的響應:"+msg);
        //關閉鏈接
        ctx.close();

    }
}

實體類(因爲實體進行網絡傳輸,需要實現序列化接口)

package com.netty.demo2;

import java.io.Serializable;

/**
 * 測試實體
 * @author MOTUI
 *
 */
@SuppressWarnings("serial")
public class User implements Serializable {

    private Integer id;
    private String name;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public User() {
        super();
        // TODO Auto-generated constructor stub
    }
    public User(Integer id, String name) {
        super();
        this.id = id;
        this.name = name;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + "]";
    }
}

自定義編碼解碼類

package com.netty.demo2;

import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

/**
 * ByteBuf 和 Object 的轉碼與解碼
 * @author MOTUI
 *
 */
public class ObjectCodec extends MessageToMessageCodec<ByteBuf, Object> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg,
            List<Object> out) throws Exception {
        System.out.println("編碼····");
        //Object 轉成 byteBuf
        byte[] values = ObjectSerializerUtils.serializer(msg);
        ByteBuf byteBuf = Unpooled.buffer(values.length);
        byteBuf.writeBytes(values);
        //輸出
        out.add(byteBuf);

    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg,
            List<Object> out) throws Exception {
        System.out.println("解碼····");
        byte[] values = new byte[msg.readableBytes()];
        msg.readBytes(values);
        Object obj = ObjectSerializerUtils.deSerializer(values);
        out.add(obj);
    }
}

編碼解碼工具類

package com.netty.demo2;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectSerializerUtils {

    /**
     * 將Object轉碼爲byte[]
     * @param obj   對象
     * @return  byte[]
     */
    public static byte[] serializer(Object obj){
        if (obj == null) return null;
        ObjectOutputStream oos = null;

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.flush();
            return baos.toByteArray();

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if (oos != null) {
                try {
                    //關閉流
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 將byte[] 轉碼爲 Object
     * @param bytes byte[]
     * @return  Object對象
     */
    public static Object deSerializer(byte[] bytes){
        if (bytes == null || bytes.length == 0) return null;
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);

            return ois.readObject();

        } catch (IOException e) {
            e.printStackTrace();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

執行結果:

服務器端:
    服務器監聽8989端口
    解碼····
    客戶端發請求了···服務器接收的數據:User [id=1, name=guozh]
    編碼····
客戶端:
    編碼····
    解碼····
    客戶端接收的響應:User [id=1, name=guozh]

總結:

對於註冊IO事件處理,會對發送和接收的數據進行再次處理獲得處理結果發送和展示。主要的問題在於對數據的處理和對執行順序的理解,即:註冊IO事件處理類中的ch.pipeline().addLast()發送數據和接收數據的順序的理解。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章