LoginRequest.LoginCommand
的生成見http://www.jianshu.com/p/a36f31aa55de
服務端
public class ProtobufServer {
public void bind(int port) throws Exception{
//配置服務端的NIO執行緒組
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());
socketChannel.pipeline().addLast(new ProtobufDecoder(LoginRequest.LoginCommand.getDefaultInstance()));
socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
socketChannel.pipeline().addLast(new ProtobufEncoder());
socketChannel.pipeline().addLast(new ProtobufServerHandler());
}
});
//繫結埠,同步等待成功
ChannelFuture f= b.bind(port).sync();
//等待伺服器監聽埠關閉
f.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
}finally {
//優雅退出,釋放執行緒池資源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args!=null&&args.length>0){
try{
port = Integer.valueOf(args[0]);
}catch (NumberFormatException e){
}
}
new ProtobufServer().bind(port);
}
}
複製程式碼
服務端處理器
public class ProtobufServerHandler extends ChannelHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
LoginRequest.LoginCommand command = (LoginRequest.LoginCommand) msg;
if("zml".equals(command.getUsername())){
System.out.println("伺服器接收到的訊息:"+command.toString());
ctx.writeAndFlush(resp(command.getUsername()));
}
}
private LoginRequest.LoginCommand resp(String username) {
LoginRequest.LoginCommand.Builder builder = LoginRequest.LoginCommand.newBuilder();
builder.setUsername("hello zml");
return builder.build();
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel registed----");
super.channelRegistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel active----");
super.channelActive(ctx);
}
}
複製程式碼
客戶端
public class ProtobufClient {
public void connect(int port,String host)throws Exception{
//配置客戶端NIO執行緒組
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());
socketChannel.pipeline().addLast(new ProtobufDecoder(LoginRequest.LoginCommand.getDefaultInstance()));
socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
socketChannel.pipeline().addLast(new ProtobufEncoder());
socketChannel.pipeline().addLast(new ProtobufClientHandler());
}
});
//發起非同步連線操作
ChannelFuture f= b.connect(host,port).sync();
//等待非同步連線操作
f.channel().closeFuture().sync();
}catch (Exception e){
e.printStackTrace();
} finally{
group.shutdownGracefully();
System.out.println("優雅關閉");
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args!=null&&args.length>0){
try{
port = Integer.valueOf(args[0]);
}catch (NumberFormatException e){
e.printStackTrace();
port = 8081;
}
}
new ProtobufClient().connect(port,"127.0.0.1");
System.out.println("啟動完成");
}
}
複製程式碼
客戶端處理器
public class ProtobufClientHandler extends ChannelHandlerAdapter {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("收到服務端的返回:"+msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("第一次連線");
for(int i = 0;i<10;i++){
ctx.writeAndFlush(subReq(i));
}
ctx.flush();
}
private LoginRequest.LoginCommand subReq(int i) {
LoginRequest.LoginCommand.Builder builder = LoginRequest.LoginCommand.newBuilder();
builder.setUsername("zml");
return builder.build();
}
}
複製程式碼
ProtobufDecoder僅僅負責解碼,它不支援讀半包。因此,在ProtobufDecoder前面,一點要有能夠讀半包的解碼器,有一下三種方式可以選擇。
- 使用Netty提供的ProtobufVarint32FrameDecoder,它可以處理半包訊息。
- 繼承Netty提供的通用半包解碼器LengthFieldBasedFrameDecoder。
- 繼承ByteToMessageDecoder類,自己處理半包訊息。
netty extend用法 tcp編碼
BaseCommand.HitCommand.Builder builder = BaseCommand.HitCommand.newBuilder();
builder.setDamage(10);
builder.setEnemyId(2);
builder.setId(1);
BaseCommand.HitCommand build = builder.build();
BaseCommand.ServerCommand.Builder builder1 = BaseCommand.ServerCommand.newBuilder();
builder1.setCommandType(BaseCommand.CommandType.Hit);
builder1.setExtension(BaseCommand.HitCommand.hitCommand,build);
channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer(builder1.build().toByteArray()),new InetSocketAddress("127.0.0.1",port)));
複製程式碼
解碼
ByteBuf data = datagramPacket.content();
byte[] b = new byte[data.readableBytes()];
data.readBytes(b);
ExtensionRegistry registry = ExtensionRegistry.newInstance();
BaseCommand.registerAllExtensions(registry);
BaseCommand.ServerCommand serverCommand = BaseCommand.ServerCommand.parseFrom(b,registry);
System.out.println("commandType:"+serverCommand.getCommandType());
BaseCommand.HitCommand extension = serverCommand.getExtension(BaseCommand.HitCommand.hitCommand);
System.out.println("damage:"+extension.getDamage());
System.out.println("enemyId:"+extension.getEnemyId());
System.out.println("id:"+extension.getId());
複製程式碼