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()发送数据和接收数据的顺序的理解。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章