客户端和服务器端在传递消息的过程中,必然需要约定消息,否则双方是没有办法理解相互之间传递的信息。
redis的协议
redis协议遵循的规则是 位数-命令,位数-命令,位数-命令....
例子:
package com.test.netty.c7;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
@Slf4j
public class RedisClient {
public static void main(String[] args) {
NioEventLoopGroup group = new NioEventLoopGroup();
//按照redis的协议
final byte[] LINE = {'\r', '\n'};
try {
ChannelFuture cOnnect= new Bootstrap().group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer
写完之后,在redis端查询"name":
HTTP协议
HTTP协议相对比较复杂,Netty已经共了服务器端编码和解码的工具类HttpServerCodec来处理HTTP请求。
// HttpServerCodec 中既有请求的解码器 HttpRequestDecoder 又有响应的编码器 HttpResponseEncoder
// Codec(CodeCombine) 一般代表该类既作为 编码器 又作为 解码器
public final class HttpServerCodec extends CombinedChannelDuplexHandler
服务器端代码:
package com.test.netty.c7;
import com.test.nio.c3.block.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
@Slf4j
public class HttpServer {
public static void main(String[] args) {
NioEventLoopGroup group = new NioEventLoopGroup();
new ServerBootstrap()
.group(group)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer
Hello,MG!
".getBytes(StandardCharsets.UTF_8);
response.headers().setInt(CONTENT_LENGTH, bytes.length);
//设置响应体
response.content().writeBytes(bytes);
//写会响应
ctx.writeAndFlush(response);
}
});
}
}).bind(8080);
}
}
浏览器访问:
自定义协议的要素
实例:
package com.test.netty.c8;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;
@Slf4j
public class MessageCodec extends ByteToMessageCodec
测试类:
package com.test.netty.c8;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TestCodec {
public static void main(String[] args) throws Exception{
EmbeddedChannel channel = new EmbeddedChannel(
new LengthFieldBasedFrameDecoder(1024, 12, 4, 0, 0),
new LoggingHandler(LogLevel.DEBUG),
new MessageCodec()
);
LoginRequestMessage loginRequestMessage = new LoginRequestMessage("liming", "2292123a");
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
new MessageCodec().encode(null, loginRequestMessage, byteBuf);
channel.writeInbound(byteBuf);
}
}
运行结果:
为了提高handler的使用率,netty使用了注解@Sharable注解对handler进行标识,其实就是一个handler是否是线程安全的(也就是无状态的),如果是线程安全的就可以使用@Sharable进行标识,表明这个handler可以共用,否则每次都需要new 一个新的handler。
之前写的 MessageCodec handler理论上市线程安全的,但是它的父类是线程不安全的,所以使用
@ChannelHandler.Sharable
public class MessageSharableCodec extends MessageToMessageCodec
MessageToMessageCodec 这个父类即可。