java 网络 序列化_Java网络通信基础系列-Netty序列化

本文详细介绍了如何在Netty中使用Java原生序列化、MessagePack和JSON进行网络通信。通过示例代码展示了如何在服务器端和客户端配置这些序列化方式,以实现对象在网络中的高效传输。Netty提供了原生的Java序列化支持,同时也支持MessagePack和JSON等轻量级序列化方案,以减小传输体积并提高速度。
摘要由CSDN通过智能技术生成

一.Netty序列化介绍

序列化管理操作:Java原生实现(性能比较差)、JSON(Restful)、MessagePack、Marshalling、AVRO、....

netty本身直接支持有原生的 Java序列化操作,直接配置已有的程序类即可

MessagePack:类似于JSON,但是要比JSON传输的更加小巧同时速度也快。它定义一个自己的压缩算法,例如:boolean只有true和false,但是有了MP就可以通过0和1描述了;

Marshalling:使用JBoss实现的序列化处理操作,是基于传统的Java序列化形式的一种升级。

JSON:是一种标准做法,但是JSON需要清楚的问题是传输体积要大,但是传输的信息明确,本次使用FastJSON操作。

二.实例

1.公共netty类

Member.java

packagecom.bijian.vo;

importjava.io.Serializable;

public class Member implementsSerializable {

privateString mid ;

privateString name ;

privateInteger age ;

privateDouble salary ;

publicMember() {}

publicMember(String mid,String name,Integer age,Double salary) {

this.mid =mid ;

this.name =name ;

this.age =age ;

this.salary =salary ;

}

publicString getMid() {

returnmid;

}

public voidsetMid(String mid) {

this.mid =mid;

}

publicString getName() {

returnname;

}

public voidsetName(String name) {

this.name =name;

}

publicInteger getAge() {

returnage;

}

public voidsetAge(Integer age) {

this.age =age;

}

publicDouble getSalary() {

returnsalary;

}

public voidsetSalary(Double salary) {

this.salary =salary;

}

@Override

publicString toString() {

return "Member{" +

"mid='" + mid + '\'' +

", name='" + name + '\'' +

", age=" + age +

", salary=" + salary +

'}';

}

}

EchoServerHandler.java

packagecom.bijian.http.server.server.handler;

importcom.bijian.vo.Member;

importio.netty.channel.ChannelHandlerContext;

importio.netty.channel.ChannelInboundHandlerAdapter;

importio.netty.util.ReferenceCountUtil;

/**

* 处理Echo的操作方式,其中ChannelInboundHandlerAdapter是针对于数据输入的处理

* Netty是基于NIO的一种开发框架的封装,这里面和AIO是没有任何关系的。

*/

public class EchoServerHandler extendsChannelInboundHandlerAdapter {

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {

try{

System.out.println(msg.getClass() + " **************");

Member member =(Member) msg ;

System.err.println("{服务器}" +member);

member.setName("【ECHO】" +member.getName());

ctx.writeAndFlush(member); // 回应的输出操作

} finally{

ReferenceCountUtil.release(msg) ; // 释放缓存

}

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {

cause.printStackTrace();

ctx.close() ;

}

}

EchoClientHandler.java

packagecom.bijian.http.server.client.handler;

importcom.bijian.vo.Member;

importio.netty.channel.ChannelHandlerContext;

importio.netty.channel.ChannelInboundHandlerAdapter;

importio.netty.util.ReferenceCountUtil;

/**

* 需要进行数据的读取操作,服务器端处理完成的数据信息会进行读取

*/

public class EchoClientHandler extendsChannelInboundHandlerAdapter {

private static final int REPEAT = 500;// 消息重复发送次数

@Override

public void channelActive(ChannelHandlerContext ctx) throwsException {

Member member = new Member(); // 现在直接进行对象的发送

member.setMid("xiaoli");

member.setName("小李老师");

member.setAge(16);

member.setSalary(1.1);

ctx.writeAndFlush(member) ; // 直接进行对象实例发送

}

@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {

// 只要服务器端发送完成信息之后,都会执行此方法进行内容的输出操作

try{

Member member =(Member) msg ;

System.out.println(member); // 输出服务器端的响应内容

} finally{

ReferenceCountUtil.release(msg); // 释放缓存

}

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {

cause.printStackTrace();

ctx.close();

}

}

EchoServerMain.java

packagecom.bijian.http.server.server.main;

importcom.bijian.http.server.server.EchoServer;

public classEchoServerMain {

public static void main(String[] args) throwsException {

newEchoServer().run();

}

}

EchoClientMain.java

packagecom.bijian.http.server.client.main;

importcom.bijian.http.server.client.EchoClient;

public classEchoClientMain {

public static void main(String[] args) throwsException {

newEchoClient().run();

}

}

2.使用Java原生的序列化程序管理实现对象网络传输

项目结构如下:

847f3b9d465e2ecaa797e3b82cb90be4.png

EchoServer.java

packagecom.bijian.http.server.server;

importcom.bijian.http.server.server.handler.EchoServerHandler;

importcom.bijian.info.HostInfo;

importio.netty.bootstrap.ServerBootstrap;

importio.netty.channel.ChannelFuture;

importio.netty.channel.ChannelInitializer;

importio.netty.channel.ChannelOption;

importio.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioServerSocketChannel;

importio.netty.handler.codec.LengthFieldBasedFrameDecoder;

importio.netty.handler.codec.LengthFieldPrepender;

importio.netty.handler.codec.serialization.ClassResolvers;

importio.netty.handler.codec.serialization.ObjectDecoder;

importio.netty.handler.codec.serialization.ObjectEncoder;

/**

* 实现了基础的线程池与网络连接的配置项

*/

public classEchoServer {

public void run() throws Exception { // 进行服务器端的启动处理

// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量

// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)

EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池

EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池

System.out.println("服务器启动成功,监听端口为:" +HostInfo.PORT);

try{

// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel

ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端

// 设置要使用的线程池以及当前的Channel类型

serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);

// 接收到信息之后需要进行处理,于是定义子处理器

serverBootstrap.childHandler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throwsException {

socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled((this.getClass().getClassLoader())))) ;

socketChannel.pipeline().addLast(newObjectEncoder()) ;

socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器

}

});

// 可以直接利用常亮进行TCP协议的相关配置

serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);

serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

// ChannelFuture描述的时异步回调的处理操作

ChannelFuture future =serverBootstrap.bind(HostInfo.PORT).sync();

future.channel().closeFuture().sync();// 等待Socket被关闭

} finally{

workerGroup.shutdownGracefully() ;

bossGroup.shutdownGracefully() ;

}

}

}

EchoClient.java

packagecom.bijian.http.server.client;

importcom.bijian.http.server.client.handler.EchoClientHandler;

importcom.bijian.info.HostInfo;

importio.netty.bootstrap.Bootstrap;

importio.netty.channel.ChannelFuture;

importio.netty.channel.ChannelInitializer;

importio.netty.channel.ChannelOption;

importio.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioSocketChannel;

importio.netty.handler.codec.LengthFieldBasedFrameDecoder;

importio.netty.handler.codec.LengthFieldPrepender;

importio.netty.handler.codec.serialization.ClassResolvers;

importio.netty.handler.codec.serialization.ObjectDecoder;

importio.netty.handler.codec.serialization.ObjectEncoder;

public classEchoClient {

public void run() throwsException {

// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;

// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池

EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池

try{

Bootstrap client = new Bootstrap(); // 创建客户端处理程序

client.group(group).channel(NioSocketChannel.class)

.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据

.handler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throwsException {

socketChannel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled((this.getClass().getClassLoader())))) ;

socketChannel.pipeline().addLast(newObjectEncoder()) ;

socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器

}

});

ChannelFuture channelFuture =client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();

channelFuture.channel().closeFuture().sync(); // 关闭连接

} finally{

group.shutdownGracefully();

}

}

}

先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:

c5c95f4d0105617527a625fdb91a27e9.png

0ff61653426b0080de51e01229ec6347.png

3.MessagePack实现传输序列化

项目结构如下所示:

06062dc9f2e18a7a4a2d2419c14ad174.png

pom.xml中引入msgpack

org.msgpack

msgpack

Member.java增加@Message注解

package com.bijian.vo;

import org.msgpack.annotation.Message;

import java.io.Serializable;

@Message

public class Member implements Serializable {

private String mid ;

private String name ;

private Integer age ;

private Double salary ;

public Member() {}

public Member(String mid,String name,Integer age,Double salary) {

this.mid = mid ;

this.name = name ;

this.age = age ;

this.salary = salary ;

}

public String getMid() {

return mid;

}

public void setMid(String mid) {

this.mid = mid;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

public Double getSalary() {

return salary;

}

public void setSalary(Double salary) {

this.salary = salary;

}

@Override

public String toString() {

return "Member{" +

"mid='" + mid + '\'' +

", name='" + name + '\'' +

", age=" + age +

", salary=" + salary +

'}';

}

}

测试类MessagePackDemoA.java

package com.bijian.msgpack;

import com.bijian.vo.Member;

import org.msgpack.MessagePack;

import org.msgpack.template.Templates;

import java.util.ArrayList;

import java.util.List;

public class MessagePackDemoA {

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

List allMembers = new ArrayList() ;

for(int x = 0 ; x < 10 ; x ++) {

Member member = new Member() ;

member.setMid("MLDN - " + x);

member.setName("Hello - " + x);

member.setAge(10);

member.setSalary(1.1);

allMembers.add(member) ;

}

MessagePack msgPack = new MessagePack() ;

byte data [] = msgPack.write(allMembers) ;

System.out.println(data.length);

{ // 将数据解析回来

List all = msgPack.read(data, Templates.tList(msgPack.lookup(Member.class))) ;

System.out.println(all);

}

}

}

运行测试类结果如下:

96f260c9119b6b0f156f2484348035c8.png

MessagePackDecoder.java

package com.bijian.netty.serious;

import com.bijian.vo.Member;

import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.MessageToMessageDecoder;

import org.msgpack.MessagePack;

import java.util.List;

public class MessagePackDecoder extends MessageToMessageDecoder {

@Override

protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List list) throws Exception {

int len = msg.readableBytes(); // 获取读取的数据长度

byte[] data = new byte[len]; // 准备读取数据的空间

msg.getBytes(msg.readerIndex(), data, 0, len); // 读取数据

MessagePack msgPack = new MessagePack() ;

System.out.println(msgPack.read(data));

// list.add(msgPack.read(data)) ;

list.add(msgPack.read(data,msgPack.lookup(Member.class))) ;

}

}

MessagePackEncoder.java

package com.bijian.netty.serious;

import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.MessageToByteEncoder;

import org.msgpack.MessagePack;

public class MessagePackEncoder extends MessageToByteEncoder {

@Override

protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf byteBuf) throws Exception {

MessagePack msgPack = new MessagePack() ;

byte [] raw = msgPack.write(msg) ; // 进行对象的编码操作

byteBuf.writeBytes(raw) ;

}

}

EchoServerHandler.java

packagecom.bijian.netty.server.handlerimportcom.bijian.vo.Member;importio.netty.channel.ChannelHandlerContext;importio.netty.channel.ChannelInboundHandlerAdapter;importio.netty.util.ReferenceCountUtil;/*** 处理Echo的操作方式,其中ChannelInboundHandlerAdapter是针对于数据输入的处理

* Netty是基于NIO的一种开发框架的封装,这里面和AIO是没有任何关系的。*/

public class EchoServerHandler extendsChannelInboundHandlerAdapter {

@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throwsException {try{

System.out.println(msg.getClass()+ " **************");

Member member=(Member) msg ;

System.err.println("{服务器}" +member);

member.setName("【ECHO】" +member.getName());

ctx.writeAndFlush(member);//回应的输出操作

} finally{

ReferenceCountUtil.release(msg) ;//释放缓存

}

}

@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throwsException {

cause.printStackTrace();

ctx.close() ;

}

}

EchoServer.java

package com.bijian.http.server.server;

import com.bijian.http.server.server.handler.EchoServerHandler;

import com.bijian.info.HostInfo;

import com.bijian.netty.serious.MessagePackDecoder;

import com.bijian.netty.serious.MessagePackEncoder;

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import io.netty.handler.codec.LengthFieldPrepender;

import io.netty.handler.codec.serialization.ClassResolvers;

import io.netty.handler.codec.serialization.ObjectDecoder;

import io.netty.handler.codec.serialization.ObjectEncoder;

/**

* 实现了基础的线程池与网络连接的配置项

*/

public class EchoServer {

public void run() throws Exception { // 进行服务器端的启动处理

// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量

// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)

EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池

EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池

System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);

try {

// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel

ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端

// 设置要使用的线程池以及当前的Channel类型

serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);

// 接收到信息之后需要进行处理,于是定义子处理器

serverBootstrap.childHandler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;

socketChannel.pipeline().addLast(new MessagePackDecoder()) ;

socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ; // 与属性个数保持一致

socketChannel.pipeline().addLast(new MessagePackEncoder()) ;

socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器

}

});

// 可以直接利用常亮进行TCP协议的相关配置

serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);

serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

// ChannelFuture描述的时异步回调的处理操作

ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();

future.channel().closeFuture().sync();// 等待Socket被关闭

} finally {

workerGroup.shutdownGracefully() ;

bossGroup.shutdownGracefully() ;

}

}

}

EchoClient.java

package com.bijian.http.server.client;

import com.bijian.http.server.client.handler.EchoClientHandler;

import com.bijian.info.HostInfo;

import com.bijian.netty.serious.MessagePackDecoder;

import com.bijian.netty.serious.MessagePackEncoder;

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import io.netty.handler.codec.LengthFieldPrepender;

import io.netty.handler.codec.serialization.ClassResolvers;

import io.netty.handler.codec.serialization.ObjectDecoder;

import io.netty.handler.codec.serialization.ObjectEncoder;

public class EchoClient {

public void run() throws Exception {

// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;

// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池

EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池

try {

Bootstrap client = new Bootstrap(); // 创建客户端处理程序

client.group(group).channel(NioSocketChannel.class)

.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据

.handler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;

socketChannel.pipeline().addLast(new MessagePackDecoder()) ;

socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ; // 与属性个数保持一致

socketChannel.pipeline().addLast(new MessagePackEncoder()) ;

socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器

}

});

ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();

channelFuture.channel().closeFuture().sync(); // 关闭连接

} finally {

group.shutdownGracefully();

}

}

}

MessagePack实现传输序列化与使用Java原生的序列化程序管理实现对象网络传输项目的变化如下:

1a7c1be040f37bcfa8b8c2beae6efa2d.png

先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:

f1da3da0ae645e83dad22b833be9e1b8.png

03bdfead9716db99564898d2db62956e.png

4.Marshalling序列化实例

项目结构如下所示:

aeb3123f4a7c0d2ec084c5aa58bf7e19.png

Member.java中的@Message注解去掉

MarshallingCodeFactory.java

packagecom.bijian.netty.serious;

import io.netty.handler.codec.marshalling.*;

importorg.jboss.marshalling.MarshallerFactory;

importorg.jboss.marshalling.Marshalling;

importorg.jboss.marshalling.MarshallingConfiguration;

public classMarshallingCodeFactory {

public staticMarshallingDecoder buildMarshallingDecoder() {

MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial") ; // 获取原始的JDK序列化

MarshallingConfiguration configuration = newMarshallingConfiguration() ;

UnmarshallerProvider provider = newDefaultUnmarshallerProvider(marshallerFactory,configuration) ;

int maxSize = 1024 << 2 ; // 设置单个对象的最大长度

MarshallingDecoder decoder = newMarshallingDecoder(provider,maxSize) ;

returndecoder ;

}

public staticMarshallingEncoder buildMarshallingEncoder() {

MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial") ; // 获取原始的JDK序列化

MarshallingConfiguration configuration = newMarshallingConfiguration() ;

MarshallerProvider provider = newDefaultMarshallerProvider(marshallerFactory,configuration) ;

MarshallingEncoder encoder = newMarshallingEncoder(provider) ;

returnencoder ;

}

}

EchoServer.java

packagecom.bijian.http.server.server;

importcom.bijian.http.server.server.handler.EchoServerHandler;

importcom.bijian.info.HostInfo;

importcom.bijian.netty.serious.MarshallingCodeFactory;

importio.netty.bootstrap.ServerBootstrap;

importio.netty.channel.ChannelFuture;

importio.netty.channel.ChannelInitializer;

importio.netty.channel.ChannelOption;

importio.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioServerSocketChannel;

importio.netty.handler.codec.LengthFieldBasedFrameDecoder;

importio.netty.handler.codec.LengthFieldPrepender;

/**

* 实现了基础的线程池与网络连接的配置项

*/

public classEchoServer {

public void run() throws Exception { // 进行服务器端的启动处理

// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量

// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)

EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池

EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池

System.out.println("服务器启动成功,监听端口为:" +HostInfo.PORT);

try{

// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel

ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端

// 设置要使用的线程池以及当前的Channel类型

serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);

// 接收到信息之后需要进行处理,于是定义子处理器

serverBootstrap.childHandler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throwsException {

socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingEncoder()) ;

socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingDecoder()) ;

socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器

}

});

// 可以直接利用常亮进行TCP协议的相关配置

serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);

serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

// ChannelFuture描述的时异步回调的处理操作

ChannelFuture future =serverBootstrap.bind(HostInfo.PORT).sync();

future.channel().closeFuture().sync();// 等待Socket被关闭

} finally{

workerGroup.shutdownGracefully() ;

bossGroup.shutdownGracefully() ;

}

}

}

EchoClient.java

packagecom.bijian.http.server.client;

importcom.bijian.http.server.client.handler.EchoClientHandler;

importcom.bijian.info.HostInfo;

importcom.bijian.netty.serious.MarshallingCodeFactory;

importio.netty.bootstrap.Bootstrap;

importio.netty.channel.ChannelFuture;

importio.netty.channel.ChannelInitializer;

importio.netty.channel.ChannelOption;

importio.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioSocketChannel;

importio.netty.handler.codec.LengthFieldBasedFrameDecoder;

importio.netty.handler.codec.LengthFieldPrepender;

public classEchoClient {

public void run() throwsException {

// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;

// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池

EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池

try{

Bootstrap client = new Bootstrap(); // 创建客户端处理程序

client.group(group).channel(NioSocketChannel.class)

.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据

.handler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throwsException {

socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingEncoder()) ;

socketChannel.pipeline().addLast(MarshallingCodeFactory.buildMarshallingDecoder()) ;

socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器

}

});

ChannelFuture channelFuture =client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();

channelFuture.channel().closeFuture().sync(); // 关闭连接

} finally{

group.shutdownGracefully();

}

}

}

Marshalling序列化与MessagePack实现传输序列化的变化如下:

a749ff4e22e575d5e2a9cffedb8f310e.png

echo-netty下的pom.xml

40ae8ed45ee470eec2f5bb2faad62849.png

echo-netty下的pom.xml完整内容如下:

echo

cn.mldn

1.0

4.0.0

echo-netty

jar

io.netty

netty-all

cn.mldn

echo-util

junit

junit

test

org.jboss.marshalling

jboss-marshalling

org.jboss.marshalling

jboss-marshalling-serial

工程下的pom.xml

41175edc30453570dce1b280f8b7b39f.png

pom.xml完整内容如下:

4.0.0

cn.mldn

echo

1.0

echo-base

echo-util

echo-netty

netty

http://www.example.com

pom

UTF-8

1.8

3.7.0

4.12

4.1.31.Final

1.0

0.6.12

2.0.6.Final

cn.mldn

echo-util

${echo.version}

io.netty

netty-all

${netty.version}

org.jboss.marshalling

jboss-marshalling

${jboss-marshalling.version}

org.jboss.marshalling

jboss-marshalling-serial

${jboss-marshalling.version}

org.msgpack

msgpack

${msgpack.version}

junit

junit

${junit.version}

test

org.apache.maven.plugins

maven-compiler-plugin

${maven-compiler-plugin.version}

${jdk.version}

${jdk.version}

先启动EchoServerMain.java,再启动EchoClientMain.java,运行效果如下所示:

78c6b53a82c730fea38835da4ab31369.png

23c5c83e549807ef6ffcec27b4e311be.png

5.JSON序列化实例

项目结构如下:

09195801a82d32e9d649e03c62c78c7d.png

JSONDecoder.java

package com.bijian.http.server.serious;

import com.bijian.vo.Member;

import com.alibaba.fastjson.JSON;

import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.MessageToMessageDecoder;

import java.util.List;

public class JSONDecoder extends MessageToMessageDecoder {

@Override

protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf msg, List list) throws Exception {

int len = msg.readableBytes(); // 可以用的数据长度

byte data[] = new byte[len];

msg.getBytes(msg.readerIndex(), data, 0, len);

list.add(JSON.parseObject(new String(data)).toJavaObject(Member.class)) ;

}

}

JSONEncoder.java

package com.bijian.http.server.serious;

import com.alibaba.fastjson.JSONObject;

import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;

import io.netty.handler.codec.MessageToByteEncoder;

public class JSONEncoder extends MessageToByteEncoder {

@Override

protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf out) throws Exception {

byte data [] = JSONObject.toJSONString(msg).getBytes() ;

out.writeBytes(data) ;

}

}

EchoServer.java

package com.bijian.http.server.server;

import com.bijian.http.server.server.handler.EchoServerHandler;

import com.bijian.info.HostInfo;

import com.bijian.http.server.serious.JSONDecoder;

import com.bijian.http.server.serious.JSONEncoder;

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import io.netty.handler.codec.LengthFieldPrepender;

/**

* 实现了基础的线程池与网络连接的配置项

*/

public class EchoServer {

public void run() throws Exception { // 进行服务器端的启动处理

// 线程池是提升服务器性能的重要技术手段,利用定长的线程池可以保证核心线程的有效数量

// 在Netty之中线程池的实现分为两类:主线程池(接收客户端连接)、工作线程池(处理客户端连接)

EventLoopGroup bossGroup = new NioEventLoopGroup(10); // 创建接收线程池

EventLoopGroup workerGroup = new NioEventLoopGroup(20); // 创建工作线程池

System.out.println("服务器启动成功,监听端口为:" + HostInfo.PORT);

try {

// 创建一个服务器端的程序类进行NIO启动,同时可以设置Channel

ServerBootstrap serverBootstrap = new ServerBootstrap(); // 服务器端

// 设置要使用的线程池以及当前的Channel类型

serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);

// 接收到信息之后需要进行处理,于是定义子处理器

serverBootstrap.childHandler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,4)) ;

socketChannel.pipeline().addLast(new JSONDecoder()) ;

socketChannel.pipeline().addLast(new LengthFieldPrepender(4)) ;

socketChannel.pipeline().addLast(new JSONEncoder()) ;

socketChannel.pipeline().addLast(new EchoServerHandler()); // 追加了处理器

}

});

// 可以直接利用常亮进行TCP协议的相关配置

serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);

serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

// ChannelFuture描述的时异步回调的处理操作

ChannelFuture future = serverBootstrap.bind(HostInfo.PORT).sync();

future.channel().closeFuture().sync();// 等待Socket被关闭

} finally {

workerGroup.shutdownGracefully() ;

bossGroup.shutdownGracefully() ;

}

}

}

EchoClient.java

package com.bijian.http.server.client;

import com.bijian.http.server.client.handler.EchoClientHandler;

import com.bijian.info.HostInfo;

import com.bijian.http.server.serious.JSONDecoder;

import com.bijian.http.server.serious.JSONEncoder;

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

import io.netty.handler.codec.LengthFieldPrepender;

public class EchoClient {

public void run() throws Exception {

// 1、如果现在客户端不同,那么也可以不使用多线程模式来处理;

// 在Netty中考虑到代码的统一性,也允许你在客户端设置线程池

EventLoopGroup group = new NioEventLoopGroup(); // 创建一个线程池

try {

Bootstrap client = new Bootstrap(); // 创建客户端处理程序

client.group(group).channel(NioSocketChannel.class)

.option(ChannelOption.TCP_NODELAY, true) // 允许接收大块的返回数据

.handler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536, 0, 4, 0, 4));

socketChannel.pipeline().addLast(new JSONDecoder());

socketChannel.pipeline().addLast(new LengthFieldPrepender(4));

socketChannel.pipeline().addLast(new JSONEncoder());

socketChannel.pipeline().addLast(new EchoClientHandler()); // 追加了处理器

}

});

ChannelFuture channelFuture = client.connect(HostInfo.HOST_NAME, HostInfo.PORT).sync();

channelFuture.channel().closeFuture().sync(); // 关闭连接

} finally {

group.shutdownGracefully();

}

}

}

echo-netty下的pom.xml

034bc8f915278fc5625fb6bce5397411.png

工程下pom.xml内容修改如下:

228db7b13874f7906da76624ac6b002e.png

先启动EchoServerMain.java,再启动EchoClientMain.java,效果如下:

82e36dd0d9fb8795cc0d1024c61a8f27.png

9fc913820c3afefd22c858ce21078589.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值