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()發送數據和接收數據的順序的理解。