学习netty之前,我们要知道netty是什么,Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,主要提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
你可以把netty当作一个建立通信的工具,凡是牵扯到网络通信的,你都可以使用它,与netty一样的还有mina。
protocolbuffer是一个google的一种数据交换的格式,它独立于语言,独立于平台,google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。可以把protobuf当作一个封装数据的载体。
这里附上protobuf的语法地址
http://blog.csdn.net/sylar_d/article/details/51325987
其实真的有用到的就是域了
我们在定义Message的时候protobuf提供了3种可选域:
required: message中必须至少包含一个required域,并且在序列化之前必须被赋值。
optional: message中需要包含0个以上optional域。
repeated:这个域用来保存一些要重复设置的变量,这些变量可以设置0次到多次。并且顺序保存。(用于设置数组)。
我们可以把一个message看成java的一个对象,其中required和option可以看成常量属性,required不能为空,option可以为空,而repeated可以看成一个链表。
我们使用一个简单的例子吧
首先创建protobuf文件 User.proto
package test;
option java_package = "com.zengame.proto.user";
option java_outer_classname = "UserProto";
message Req_UpdateSecurityPassword{
required string securityPassword = 1;
optional string oldSecurityPassword = 2[default = ""];//老密码
}
message Ans_RecommendServer{
required int32 recommendSvrID = 1;//推荐serverID 玩家可能在其他服有信息
}
message Req_User{
required string username = 1;
}
这是proto文件,之后使用java的编译器生成代码,window下的指定如下(注意:必须要在同一目录下):
protoc.exe -I=./ --java_out=./proto User.proto
pause
我们在使用proto时会将其序列化和反序列化,所以我创建了一个RPCMessage作为传递消息的对象,代码如下:RPCMessage.java
//记得加上这个标签,依旧实现Serializable接口
@Message
public class RPCMessage implements Serializable{
private static final long serialVersionUID = 1L;
private int cmd; //用于存储协议号
private byte[] data;//用于存储protobuf的字节数组
private int seq; //消息队列位置
private byte type;//消息类型
private transient Object obj;//这个暂时没点用,所以加上transient让他不需要序列化
public int getCmd() {
return cmd;
}
public void setCmd(int cmd) {
this.cmd = cmd;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
public byte getType() {
return type;
}
public void setType(byte type) {
this.type = type;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
@Override
public String toString() {
return "RPCMessage [cmd=" + cmd + ", seq=" + seq + ", type=" + type + "]";
}
}
服务器代码 NettyServer.java
ServerBootstrap bootStrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootStrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = Channels.pipeline();
ObjectDecoder objectDecoder = new ObjectDecoder(1024 * 1024,
ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader()));
//pipeline.addLast("decoder", new ProtobufDecoder(UserProto.Ans_RecommendServer.getDefaultInstance()));
pipeline.addLast("decoder", objectDecoder); // 传输对象需要使用对象解码
pipeline.addLast("encoder", new ObjectEncoder()); //传输对象需要使用对象加码
//pipeline.addLast("decoder", new ProtobufDecoder(UserProto.Req_UpdateSecurityPassword.getDefaultInstance()));
//pipeline.addLast("encoder",new ProtobufEncoder());
pipeline.addLast("handler", new ObjectServerHandler()); //事件处理器 需要自己重写
return pipeline;
}
});
bootStrap.bind(new InetSocketAddress(8000));
服务器处理类 ObjectServerHandler.java
public class ObjectServerHandler extends SimpleChannelHandler{
//客户端连接服务器时,会执行
@Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
//将channel存储起来
PlayerManager.addSession(100175, ctx.getChannel());
}
//服务器收到消息时会执行
@Override
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) throws Exception {
RPCMessage message = (RPCMessage)e.getMessage();
switch (message.getCmd()) {
case 10001:
Req_UpdateSecurityPassword ans = Req_UpdateSecurityPassword.parseFrom(message.getData());
System.out.println(message);
System.out.println("SecurityPassword = "+ans.getSecurityPassword()+",OldSecurityPassword = "+ans.getOldSecurityPassword());
Ans_RecommendServer req = Ans_RecommendServer.newBuilder()
.setRecommendSvrID(10001)
.build();
PlayerManager.sendMessage(message.getCmd(),req.toByteArray(), 100175);
break;
case 10002:
Req_User rsp = Req_User.parseFrom(message.getData());
System.out.println(message);
System.out.println("username=" + rsp.getUsername());
req = Ans_RecommendServer.newBuilder()
.setRecommendSvrID(10002)
.build();
PlayerManager.sendMessage(message.getCmd(),req.toByteArray(), 100175);
break;
default:
break;
}
}
}
客户端代码 NettyClient.java
ClientBootstrap bootStarp = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootStarp.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline = Channels.pipeline();
ObjectDecoder objectDecoder = new ObjectDecoder(1024 * 1024,
ClassResolvers.weakCachingConcurrentResolver(this
.getClass().getClassLoader()));
//pipeline.addLast("decoder", new ProtobufDecoder(UserProto.Ans_RecommendServer.getDefaultInstance()));
pipeline.addLast("decoder", objectDecoder);
pipeline.addLast("encoder",new ObjectEncoder());
pipeline.addLast("handler", new ObjectClientHandler()); //客户端处理类
return pipeline;
}
});
bootStarp.connect(new InetSocketAddress("127.0.0.1",8000));
客户端处理类 ObjectClientHandler.java
public class ObjectClientHandler extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
RPCMessage message = (RPCMessage) e.getMessage();
switch (message.getCmd()) {
case 10001:
Ans_RecommendServer ans = Ans_RecommendServer.parseFrom(message.getData());
System.out.println(message);
System.out.println("recommendSvrID = " + ans.getRecommendSvrID());
break;
case 10002:
ans = Ans_RecommendServer.parseFrom(message.getData());
System.out.println(message);
System.out.println("recommendSvrID = " + ans.getRecommendSvrID());
break;
default:
break;
}
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
PlayerManager.addSession(100175, ctx.getChannel());
}
}
客户端发送消息代码
int op = 0;
Scanner in = new Scanner(System.in);
StringBuffer sb = new StringBuffer();
boolean flag = true;
do{
System.out.println("请输入操作: 1 发送消息 2 断开连接");
op = in.nextInt();
switch (op) {
case 1:
System.out.print("请输入发送消息的内容:");
sb.append(in.next()+" "+in.next());
String[] value = sb.toString().split(" ");
Req_UpdateSecurityPassword req = Req_UpdateSecurityPassword.newBuilder()
.setSecurityPassword(value[0])
.setOldSecurityPassword(value[1])
.build();
PlayerManager.sendMessage(10001,req.toByteArray(), 100175);
break;
case 2:
System.out.print("请输入发送消息的内容:");
sb.setLength(0);
sb.append(in.next());
Req_User username = Req_User.newBuilder()
.setUsername(sb.toString())
.build();
PlayerManager.sendMessage(10002,username.toByteArray(), 100175);
break;
default:
PlayerManager.close(100175);
System.out.println("连接关闭!");
flag = !flag;
break;
}
}while(flag);
System.out.println("系统关闭");
这里我还多做了一步操作,存储channel,文件名为PlayerManager.java
public class PlayerManager {
private static Map sessionMap = new ConcurrentHashMap();
public static void addSession(int playerId ,Channel channel){
if(sessionMap.containsKey(playerId)){
if(sessionMap.get(playerId) == channel){
return ;
}
}
sessionMap.put(playerId, channel);
}
public static void removeSession(int playerId){
if(sessionMap.containsKey(playerId)){
sessionMap.remove(playerId);
}
}
public static Channel getSession(int playerId){
if(!sessionMap.containsKey(playerId)){
return null;
}
return sessionMap.get(playerId);
}
public static void sendMessage(int cmd, byte[] data, int playerId){
if(sessionMap.containsKey(playerId)){
RPCMessage message = new RPCMessage();
message.setCmd(cmd);
message.setSeq(-1);
message.setType((byte) -1);
message.setData(data);
sessionMap.get(playerId).write(message);
System.out.println("发送消息成功.");
}
}
public static void close(int playerId){
if(sessionMap.containsKey(playerId)){
sessionMap.get(playerId).close();
}
}
}
运行结果
好吧,整个大概的使用应该会了,有了这些,我感觉就可以实现一个简单的聊天室系统了,哈哈
在这里我也要说声抱歉了,因为我对netty以及NIO了解的也不是很深,不过我可以推荐大家这篇文章,是讲解NIO,个人觉得讲解的非常好!!
https://zhuanlan.zhihu.com/p/23488863
总结:个人觉得自己对于netty只是会初步使用,里面的底层实现什么的我还不是很了解,学习之路永不停息。