2.1 准备工作创建工程
创建一个工程包含服务端和客户端两个Module
修改上篇文章提到过的BaseMsg.proto文件中,定义的包名
运行gen.bat程序,生成对应的类
把生成好的类,拷贝到对应的包名下
- 工程中,添加protobuf和netty的jar包
2.2服务端代码思路
通过ServerBootstrap,快速构建服务端代码
服务端消息编解码方式采用protobuf
2.3消息编解码的 配置(服务端和客户端通用)
2.4 NettyServerBootstrap,负责启动服务端程序
package com.netty.test;
import com.netty.test.pojo.BaseMsgOuterClass;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
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.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
public class NettyServerBootstrap {
public EventLoopGroup boss;
public EventLoopGroup worker;
private void bind(int port) {
//配置服务器端的NIO线程组
boss = new NioEventLoopGroup();
worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.option(ChannelOption.*SO_BACKLOG*, 1024 * 100);
bootstrap.option(ChannelOption.*TCP_NODELAY*, true);
bootstrap.childOption(ChannelOption.*SO_KEEPALIVE*, true);
//初始化channel,添加解码器,防止TCP拆包粘包问题
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline p = socketChannel.pipeline();
//1.消息解码
p.addLast(new ProtobufVarint32FrameDecoder());
//需要解码的目标类
p.addLast( new ProtobufDecoder(BaseMsgOuterClass.BaseMsg.*getDefaultInstance*()));
//2.消息的编码
p.addLast(new ProtobufVarint32LengthFieldPrepender());
p.addLast(new ProtobufEncoder());
p.addLast(serverHandler);
}
});
try {
ChannelFuture f = bootstrap.bind(port).sync();
if (f.isSuccess()) {
System.*out*.println("server start---------------");
} else {
System.*out*.println("server start-------failure--------");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private NettyServerHandler serverHandler;
public void start(int port) throws InterruptedException {
serverHandler = new NettyServerHandler( );
bind(port);
}
}
```
###2.5 NettyServerHandler,负责接收客户端传来的消息
```java
package com.netty.test;
import android.content.Context;
import com.netty.test.pojo.BaseMsgOuterClass;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
*/**
* Created by pengfei on 2017/5/11.
*/
*//加上此注解,每次新的客户端连接进来的时候不用重新再new一个handler了,
@ChannelHandler.Sharable
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
public Context mContext;
public NettyServerHandler() {
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof BaseMsgOuterClass.BaseMsg) {
BaseMsgOuterClass.BaseMsg baseMsg = (BaseMsgOuterClass.BaseMsg) msg;
String msgType = baseMsg.getMsgType();
System.*out*.print("消息的类型:"+msgType);
//BaseMsg.proto的消息内容为bytes类型的,它是二进制的。
//这样我们可以根据需要,把消息内容解码为文本消息,或文件消息
String msgContent = baseMsg.getMsg().toStringUtf8();
System.*out*.print("消息的内容:"+msgContent);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
cause.printStackTrace();
ctx.close();
}
}
最后,有兴趣的可以关注下方的公众号,会定期分享一些知识,以及工作中所遇到问题的解决方案。扫描二维码就可以添加关注: