netty transfer java bean object

     In the last blog ( netty entry to implement a simple echo program ), we know how to use netty to send a simple message, but it is far from enough. In this blog, we will use netty to send a message of a java bean object, but to send a message of object type, the java object must be serialized. There are many serialization frameworks in java, here we use protostuff To serialize, if you don't know protostuff, you can take a look at this blog ( protostuff serialization ) to understand the simple usage.

need:

    When the client connects to the server, it sends 100 Person objects to the server.

    After the server receives the message, it can be printed on the console.

    Protocol for message sending: 4 bytes in length (the length is the length of the following object, excluding its own 4 bytes), followed by the actual data to be sent

Implementation ideas:

   1. Server side:

         1. The server first uses LengthFieldBasedFrameDecoder to decode and obtains a complete ByteBuf message

         2. Then write the ProtostuffDecoder decoder to convert the message decoded in the previous step into a Person object

         3. Write the ProtoStuffServerHandler class to output the Person object decoded in the previous step

   2. Client

        1. Write the ProtostuffClientHandler class to send the Person object to the server

        2. Write a ProtostuffEncoder to convert the Person object into a byte array

        3. Use netty's LengthFieldPrepender to add 4 bytes of message length to the previous byte array

The processing of the half-package is mainly processed by the LengthFieldBasedFrameDecoder submitted by netty

Notice:

         new LengthFieldPrepender(4) ==>  will add 4 bytes before the sent data to indicate the length of the message

         new LengthFieldBasedFrameDecoder(10240, 0, 4, 0, 4)  ==> 10240 means that if the length of the bytes read this time is larger than this, it means that someone else forged a socket attack, and an exception will be thrown, the first 4 means read Take four bytes to indicate the length of this message, and a 4 behind it indicates that four bytes are discarded, and then the service data is read.

Implementation steps:

1. Introduce maven dependencies

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.huan.netty</groupId>
	<artifactId>netty-study</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>netty-study</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>4.1.6.Final</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.16.18</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.25</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.1.7</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>1.1.7</version>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-access</artifactId>
			<version>1.1.7</version>
		</dependency>
		<dependency>
			<groupId>io.protostuff</groupId>
			<artifactId>protostuff-api</artifactId>
		</dependency>
		<dependency>
			<groupId>io.protostuff</groupId>
			<artifactId>protostuff-core</artifactId>
		</dependency>
		<dependency>
			<groupId>io.protostuff</groupId>
			<artifactId>protostuff-runtime</artifactId>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>io.protostuff</groupId>
				<artifactId>protostuff-bom</artifactId>
				<version>1.4.4</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
</project>

 Second, write the entity class Person ( the client will send this object, and the server will receive this object )

 

 

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Person {
	private int id;
	private String name;
}

 3. Write ProtostuffDecoder to convert the data in ByteBuf into Person object

 

 

public class ProtostuffDecoder extends ByteToMessageDecoder {
	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
		Schema<Person> schema = RuntimeSchema.getSchema(Person.class);
		Person person = schema.newMessage();
		byte[] array = new byte[in.readableBytes()];
		in.readBytes(array);
		ProtobufIOUtil.mergeFrom(array, person, schema);
		out.add(person);
	}
}

 Fourth, write ProtoStuffServerHandler to output the received data to the console

 

 

@ Slf4j
public class ProtoStuffServerHandler extends ChannelInboundHandlerAdapter {
	private int counter = 0;

	/**
	 * Called when data is received
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		Person person = (Person) msg;
		log.info("This is the [{}]th time to get the person object [{}] sent by the client.", ++counter, person);
	}

	/** When an exception occurs, this method is called */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		log.error("error:", cause);
		ctx.close();
	}
}

 5. Write the netty server to start the netty service

 

 

@ Slf4j
public class NettyServer {
	public static void main(String[] args) throws Exception {
		EventLoopGroup boss = new NioEventLoopGroup(1);
		EventLoopGroup worker = new NioEventLoopGroup();
		ServerBootstrap bootstrap = new ServerBootstrap();
		bootstrap.group(boss, worker)//
				.channel(NioServerSocketChannel.class)// corresponds to the ServerSocketChannel class
				.option(ChannelOption.SO_BACKLOG, 128)//
				.handler(new LoggingHandler(LogLevel.TRACE))//
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(10240, 0, 4, 0, 4));
						ch.pipeline().addLast(new ProtostuffDecoder());
						ch.pipeline().addLast(new ProtoStuffServerHandler());
					}
				});
		ChannelFuture future = bootstrap.bind(9090).sync();
		log.info("server start in port:[{}]", 9090);
		future.channel().closeFuture().sync();
		boss.shutdownGracefully();
		worker.shutdownGracefully();
	}
}

 Here you need to pay attention to the order of the decoders:

 

    Must be LengthFieldBasedFrameDecoder first, then ProtostuffDecoder, then ProtoStuffServerHandler

6. Write the handler processor of the client to send the Person object to the server

 

@ Slf4j
public class ProtostuffClientHandler extends ChannelInboundHandlerAdapter {
	/**
	 * This method is called after the client and server TCP links are successfully established
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		Person person;
		for (int i = 0; i < 100; i++) {
			person = new Person();
			person.setId(i);
			person.setName("张三" + i);
			ctx.writeAndFlush(person);
		}
	}

	/**
	 * Called when an exception occurs
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		log.error("client error:", cause);
		ctx.close();
	}
}

 7. Write the ProtostuffEncoder encoder to encode the Person object into a byte array

 

 

public class ProtostuffEncoder extends MessageToByteEncoder<Person> {

	@Override
	protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception {
		LinkedBuffer buffer = LinkedBuffer.allocate(1024);
		Schema<Person> schema = RuntimeSchema.getSchema(Person.class);
		byte[] array = ProtobufIOUtil.toByteArray(msg, schema, buffer);
		out.writeBytes(array);
	}

}

 Eight, write the client

 

 

@ Slf4j
public class NettyClient {

	public static void main(String[] args) throws InterruptedException {
		EventLoopGroup group = new NioEventLoopGroup();
		Bootstrap bootstrap = new Bootstrap();
		bootstrap.group(group)//
				.channel(NioSocketChannel.class)//
				.option(ChannelOption.TCP_NODELAY, true)//
				.handler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline().addLast(new LengthFieldPrepender(4));
						ch.pipeline().addLast(new ProtostuffEncoder());
						ch.pipeline().addLast(new ProtostuffClientHandler());
					}
				});
		ChannelFuture future = bootstrap.connect("127.0.0.1", 9090).sync();
		log.info("client connect server.");
		future.channel().closeFuture().sync();
		group.shutdownGracefully();
	}
}

 Note: Here you also need to pay attention to the order in which the encoders in the initChannel method are added

          ProtostuffEncoder -> Convert Person object to byte array

          LengthFieldPrepender->Add 4 bytes of length before the byte array of the previous step

Nine, start the server and client for testing

 

 

 

  

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326493823&siteId=291194637