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()发送数据和接收数据的顺序的理解。