在netty入门学习(1)中我们曾经提到NIO为了提高效率,默认情况下可以服务端一次write造成客户端多次read,或者服务商多个write但客户端一次全读取过来。模拟演示如下:
改正前:
服务端:
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
public class NioServer {
public static void main(String args[]){
//Server服务启动器
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));
//设置处理客户端消息和各种消息事件的类(Handler)
bootstrap.setPipelineFactory(new ChannelPipelineFactory(){
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(/*new LengthFieldPrepender(2),
new LengthFieldBasedFrameDecoder(64*1024,0,2,0,2),*/
new BusinessHandler());
}
});
//绑定8000端口供客户端访问
bootstrap.bind(new InetSocketAddress(8000));
}
private static class BusinessHandler extends SimpleChannelHandler{
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
ChannelBuffer buffer = (ChannelBuffer)e.getMessage();
System.out.println("Receive:"+buffer.toString(Charset.defaultCharset()));
String msg = buffer.toString(Charset.defaultCharset()) + "has been processed!";
ChannelBuffer buffer2 = ChannelBuffers.buffer(msg.length());
buffer2.writeBytes(msg.getBytes());
e.getChannel().write(buffer2);
}
}
}
客户端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
public class NioClient {
public static void main(String args[]){
// Client服务启动器
ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// 设置一个处理服务端消息和各种消息事件的类(Handler)
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(/*new LengthFieldPrepender(2),new LengthFieldBasedFrameDecoder(64*1024,0,2,0,2),*/new RequestHandler());
}
});
// 连接到本地的8000端口的服务端
ChannelFuture future = bootstrap.connect(new InetSocketAddress(
"127.0.0.1", 8000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(future.isSuccess()){
StringBuilder builder = new StringBuilder();
builder.append("Request001");
for(int i=0;i<100;i++){
builder.append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
}
ChannelBuffer buffer = ChannelBuffers.buffer(builder.toString().length());
buffer.writeBytes(builder.toString().getBytes());
future.getChannel().write(buffer);
}
}
private static class RequestHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
ChannelBuffer buffer = (ChannelBuffer)e.getMessage();
System.out.println(buffer.toString(Charset.defaultCharset()));
}
}
}
根据结果可知晓,一次write对应多次read,为了解决这个问题netty提供了两个重要的handler:LengthFieldPrepender和LengthFieldBasedFrameDecoder
在业务处理 handler之前加上这两个handler就可以解决上述的问题了:代码如下
服务端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
public class NioServer {
public static void main(String args[]){
//Server服务启动器
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool()));
//设置处理客户端消息和各种消息事件的类(Handler)
bootstrap.setPipelineFactory(new ChannelPipelineFactory(){
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new LengthFieldPrepender(2),
new LengthFieldBasedFrameDecoder(64*1024,0,2,0,2),
new BusinessHandler());
}
});
//绑定8000端口供客户端访问
bootstrap.bind(new InetSocketAddress(8000));
}
private static class BusinessHandler extends SimpleChannelHandler{
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
ChannelBuffer buffer = (ChannelBuffer)e.getMessage();
System.out.println("Receive:"+buffer.toString(Charset.defaultCharset()));
String msg = buffer.toString(Charset.defaultCharset()) + "has been processed!";
ChannelBuffer buffer2 = ChannelBuffers.buffer(msg.length());
buffer2.writeBytes(msg.getBytes());
e.getChannel().write(buffer2);
}
}
}
客户端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.LengthFieldPrepender;
public class NioClient {
public static void main(String args[]){
// Client服务启动器
ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// 设置一个处理服务端消息和各种消息事件的类(Handler)
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new LengthFieldPrepender(2),new LengthFieldBasedFrameDecoder(64*1024,0,2,0,2),new RequestHandler());
}
});
// 连接到本地的8000端口的服务端
ChannelFuture future = bootstrap.connect(new InetSocketAddress(
"127.0.0.1", 8000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(future.isSuccess()){
StringBuilder builder = new StringBuilder();
builder.append("Request001");
for(int i=0;i<100;i++){
builder.append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
}
ChannelBuffer buffer = ChannelBuffers.buffer(builder.toString().length());
buffer.writeBytes(builder.toString().getBytes());
future.getChannel().write(buffer);
}
}
private static class RequestHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
ChannelBuffer buffer = (ChannelBuffer)e.getMessage();
System.out.println(buffer.toString(Charset.defaultCharset()));
}
}
}