Protobuf快速入门实例

1. 编码和解码的基本介绍

  编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码。codec(编解码器) 的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据。

1. Netty 本身的编码解码的机制

Netty 自身提供了一些 codec(编解码器)

Netty 提供的编码器

StringEncoder,对字符串数据进行编码

ObjectEncoder,对 Java 对象进行编码

Netty 提供的解码器

StringDecoder, 对字符串数据进行解码

ObjectDecoder,对 Java 对象进行解码

Netty 本身自带的 ObjectDecoder 和 ObjectEncoder 可以用来实现 POJO 对象或各种业务对象的编码和解码,底层使用的仍是 Java 序列化技术 , 而Java 序列化技术本身效率就不高,存在如下问题:
(1)无法跨语言
(2)序列化后的体积太大,是二进制编码的 5 倍多。
(3)序列化性能比较低

2. Protobuf

Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。

git地址: https://github.com/protocolbuffers/protobuf

2. Protobuf 简单实用

1. 到git 上下载Protobuf 客户端工具,用于将proto 文件反向生成java类文件

 2. 编写.proto 文件: protobuf 以Message 进行管理

syntax = "proto3"; //版本
option java_outer_classname = "StudentPOJO";//生成的外部类名,同时也是文件名
//protobuf 使用message 管理数据
message Student { //会在 StudentPOJO 外部类生成一个内部类 Student, 他是真正发送的POJO对象
    int32 id = 1; // Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
    string name = 2;
}

3. 用上面第一步下载的客户端反向生成java 类, cmd 命令行执行如下命令

protoc.exe --java_out=. StudentPOJO.proto

4. 生成的类信息如下:

  1 package netty.codec;// Generated by the protocol buffer compiler.  DO NOT EDIT!
  2 // source: StudentPOJO.proto
  3 
  4 public final class StudentPOJO {
  5   private StudentPOJO() {}
  6   public static void registerAllExtensions(
  7       com.google.protobuf.ExtensionRegistryLite registry) {
  8   }
  9 
 10   public static void registerAllExtensions(
 11       com.google.protobuf.ExtensionRegistry registry) {
 12     registerAllExtensions(
 13         (com.google.protobuf.ExtensionRegistryLite) registry);
 14   }
 15   public interface StudentOrBuilder extends
 16       // @@protoc_insertion_point(interface_extends:Student)
 17       com.google.protobuf.MessageOrBuilder {
 18 
 19     /**
 20      * <pre>
 21      * Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
 22      * </pre>
 23      *
 24      * <code>int32 id = 1;</code>
 25      * @return The id.
 26      */
 27     int getId();
 28 
 29     /**
 30      * <code>string name = 2;</code>
 31      * @return The name.
 32      */
 33     String getName();
 34     /**
 35      * <code>string name = 2;</code>
 36      * @return The bytes for name.
 37      */
 38     com.google.protobuf.ByteString
 39         getNameBytes();
 40   }
 41   /**
 42    * <pre>
 43    *protobuf 使用message 管理数据
 44    * </pre>
 45    *
 46    * Protobuf type {@code Student}
 47    */
 48   public static final class Student extends
 49       com.google.protobuf.GeneratedMessageV3 implements
 50       // @@protoc_insertion_point(message_implements:Student)
 51       StudentOrBuilder {
 52   private static final long serialVersionUID = 0L;
 53     // Use Student.newBuilder() to construct.
 54     private Student(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
 55       super(builder);
 56     }
 57     private Student() {
 58       name_ = "";
 59     }
 60 
 61     @Override
 62     @SuppressWarnings({"unused"})
 63     protected Object newInstance(
 64         UnusedPrivateParameter unused) {
 65       return new Student();
 66     }
 67 
 68     @Override
 69     public final com.google.protobuf.UnknownFieldSet
 70     getUnknownFields() {
 71       return this.unknownFields;
 72     }
 73     private Student(
 74         com.google.protobuf.CodedInputStream input,
 75         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
 76         throws com.google.protobuf.InvalidProtocolBufferException {
 77       this();
 78       if (extensionRegistry == null) {
 79         throw new NullPointerException();
 80       }
 81       com.google.protobuf.UnknownFieldSet.Builder unknownFields =
 82           com.google.protobuf.UnknownFieldSet.newBuilder();
 83       try {
 84         boolean done = false;
 85         while (!done) {
 86           int tag = input.readTag();
 87           switch (tag) {
 88             case 0:
 89               done = true;
 90               break;
 91             case 8: {
 92 
 93               id_ = input.readInt32();
 94               break;
 95             }
 96             case 18: {
 97               String s = input.readStringRequireUtf8();
 98 
 99               name_ = s;
100               break;
101             }
102             default: {
103               if (!parseUnknownField(
104                   input, unknownFields, extensionRegistry, tag)) {
105                 done = true;
106               }
107               break;
108             }
109           }
110         }
111       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
112         throw e.setUnfinishedMessage(this);
113       } catch (java.io.IOException e) {
114         throw new com.google.protobuf.InvalidProtocolBufferException(
115             e).setUnfinishedMessage(this);
116       } finally {
117         this.unknownFields = unknownFields.build();
118         makeExtensionsImmutable();
119       }
120     }
121     public static final com.google.protobuf.Descriptors.Descriptor
122         getDescriptor() {
123       return StudentPOJO.internal_static_Student_descriptor;
124     }
125 
126     @Override
127     protected FieldAccessorTable
128         internalGetFieldAccessorTable() {
129       return StudentPOJO.internal_static_Student_fieldAccessorTable
130           .ensureFieldAccessorsInitialized(
131               Student.class, Builder.class);
132     }
133 
134     public static final int ID_FIELD_NUMBER = 1;
135     private int id_;
136     /**
137      * <pre>
138      * Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
139      * </pre>
140      *
141      * <code>int32 id = 1;</code>
142      * @return The id.
143      */
144     @Override
145     public int getId() {
146       return id_;
147     }
148 
149     public static final int NAME_FIELD_NUMBER = 2;
150     private volatile Object name_;
151     /**
152      * <code>string name = 2;</code>
153      * @return The name.
154      */
155     @Override
156     public String getName() {
157       Object ref = name_;
158       if (ref instanceof String) {
159         return (String) ref;
160       } else {
161         com.google.protobuf.ByteString bs = 
162             (com.google.protobuf.ByteString) ref;
163         String s = bs.toStringUtf8();
164         name_ = s;
165         return s;
166       }
167     }
168     /**
169      * <code>string name = 2;</code>
170      * @return The bytes for name.
171      */
172     @Override
173     public com.google.protobuf.ByteString
174         getNameBytes() {
175       Object ref = name_;
176       if (ref instanceof String) {
177         com.google.protobuf.ByteString b = 
178             com.google.protobuf.ByteString.copyFromUtf8(
179                 (String) ref);
180         name_ = b;
181         return b;
182       } else {
183         return (com.google.protobuf.ByteString) ref;
184       }
185     }
186 
187     private byte memoizedIsInitialized = -1;
188     @Override
189     public final boolean isInitialized() {
190       byte isInitialized = memoizedIsInitialized;
191       if (isInitialized == 1) return true;
192       if (isInitialized == 0) return false;
193 
194       memoizedIsInitialized = 1;
195       return true;
196     }
197 
198     @Override
199     public void writeTo(com.google.protobuf.CodedOutputStream output)
200                         throws java.io.IOException {
201       if (id_ != 0) {
202         output.writeInt32(1, id_);
203       }
204       if (!getNameBytes().isEmpty()) {
205         com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_);
206       }
207       unknownFields.writeTo(output);
208     }
209 
210     @Override
211     public int getSerializedSize() {
212       int size = memoizedSize;
213       if (size != -1) return size;
214 
215       size = 0;
216       if (id_ != 0) {
217         size += com.google.protobuf.CodedOutputStream
218           .computeInt32Size(1, id_);
219       }
220       if (!getNameBytes().isEmpty()) {
221         size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_);
222       }
223       size += unknownFields.getSerializedSize();
224       memoizedSize = size;
225       return size;
226     }
227 
228     @Override
229     public boolean equals(final Object obj) {
230       if (obj == this) {
231        return true;
232       }
233       if (!(obj instanceof Student)) {
234         return super.equals(obj);
235       }
236       Student other = (Student) obj;
237 
238       if (getId()
239           != other.getId()) return false;
240       if (!getName()
241           .equals(other.getName())) return false;
242       if (!unknownFields.equals(other.unknownFields)) return false;
243       return true;
244     }
245 
246     @Override
247     public int hashCode() {
248       if (memoizedHashCode != 0) {
249         return memoizedHashCode;
250       }
251       int hash = 41;
252       hash = (19 * hash) + getDescriptor().hashCode();
253       hash = (37 * hash) + ID_FIELD_NUMBER;
254       hash = (53 * hash) + getId();
255       hash = (37 * hash) + NAME_FIELD_NUMBER;
256       hash = (53 * hash) + getName().hashCode();
257       hash = (29 * hash) + unknownFields.hashCode();
258       memoizedHashCode = hash;
259       return hash;
260     }
261 
262     public static Student parseFrom(
263         java.nio.ByteBuffer data)
264         throws com.google.protobuf.InvalidProtocolBufferException {
265       return PARSER.parseFrom(data);
266     }
267     public static Student parseFrom(
268         java.nio.ByteBuffer data,
269         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
270         throws com.google.protobuf.InvalidProtocolBufferException {
271       return PARSER.parseFrom(data, extensionRegistry);
272     }
273     public static Student parseFrom(
274         com.google.protobuf.ByteString data)
275         throws com.google.protobuf.InvalidProtocolBufferException {
276       return PARSER.parseFrom(data);
277     }
278     public static Student parseFrom(
279         com.google.protobuf.ByteString data,
280         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
281         throws com.google.protobuf.InvalidProtocolBufferException {
282       return PARSER.parseFrom(data, extensionRegistry);
283     }
284     public static Student parseFrom(byte[] data)
285         throws com.google.protobuf.InvalidProtocolBufferException {
286       return PARSER.parseFrom(data);
287     }
288     public static Student parseFrom(
289         byte[] data,
290         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
291         throws com.google.protobuf.InvalidProtocolBufferException {
292       return PARSER.parseFrom(data, extensionRegistry);
293     }
294     public static Student parseFrom(java.io.InputStream input)
295         throws java.io.IOException {
296       return com.google.protobuf.GeneratedMessageV3
297           .parseWithIOException(PARSER, input);
298     }
299     public static Student parseFrom(
300         java.io.InputStream input,
301         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
302         throws java.io.IOException {
303       return com.google.protobuf.GeneratedMessageV3
304           .parseWithIOException(PARSER, input, extensionRegistry);
305     }
306     public static Student parseDelimitedFrom(java.io.InputStream input)
307         throws java.io.IOException {
308       return com.google.protobuf.GeneratedMessageV3
309           .parseDelimitedWithIOException(PARSER, input);
310     }
311     public static Student parseDelimitedFrom(
312         java.io.InputStream input,
313         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
314         throws java.io.IOException {
315       return com.google.protobuf.GeneratedMessageV3
316           .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
317     }
318     public static Student parseFrom(
319         com.google.protobuf.CodedInputStream input)
320         throws java.io.IOException {
321       return com.google.protobuf.GeneratedMessageV3
322           .parseWithIOException(PARSER, input);
323     }
324     public static Student parseFrom(
325         com.google.protobuf.CodedInputStream input,
326         com.google.protobuf.ExtensionRegistryLite extensionRegistry)
327         throws java.io.IOException {
328       return com.google.protobuf.GeneratedMessageV3
329           .parseWithIOException(PARSER, input, extensionRegistry);
330     }
331 
332     @Override
333     public Builder newBuilderForType() { return newBuilder(); }
334     public static Builder newBuilder() {
335       return DEFAULT_INSTANCE.toBuilder();
336     }
337     public static Builder newBuilder(Student prototype) {
338       return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
339     }
340     @Override
341     public Builder toBuilder() {
342       return this == DEFAULT_INSTANCE
343           ? new Builder() : new Builder().mergeFrom(this);
344     }
345 
346     @Override
347     protected Builder newBuilderForType(
348         BuilderParent parent) {
349       Builder builder = new Builder(parent);
350       return builder;
351     }
352     /**
353      * <pre>
354      *protobuf 使用message 管理数据
355      * </pre>
356      *
357      * Protobuf type {@code Student}
358      */
359     public static final class Builder extends
360         com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
361         // @@protoc_insertion_point(builder_implements:Student)
362         StudentOrBuilder {
363       public static final com.google.protobuf.Descriptors.Descriptor
364           getDescriptor() {
365         return StudentPOJO.internal_static_Student_descriptor;
366       }
367 
368       @Override
369       protected FieldAccessorTable
370           internalGetFieldAccessorTable() {
371         return StudentPOJO.internal_static_Student_fieldAccessorTable
372             .ensureFieldAccessorsInitialized(
373                 Student.class, Builder.class);
374       }
375 
376       // Construct using StudentPOJO.Student.newBuilder()
377       private Builder() {
378         maybeForceBuilderInitialization();
379       }
380 
381       private Builder(
382           BuilderParent parent) {
383         super(parent);
384         maybeForceBuilderInitialization();
385       }
386       private void maybeForceBuilderInitialization() {
387         if (com.google.protobuf.GeneratedMessageV3
388                 .alwaysUseFieldBuilders) {
389         }
390       }
391       @Override
392       public Builder clear() {
393         super.clear();
394         id_ = 0;
395 
396         name_ = "";
397 
398         return this;
399       }
400 
401       @Override
402       public com.google.protobuf.Descriptors.Descriptor
403           getDescriptorForType() {
404         return StudentPOJO.internal_static_Student_descriptor;
405       }
406 
407       @Override
408       public Student getDefaultInstanceForType() {
409         return Student.getDefaultInstance();
410       }
411 
412       @Override
413       public Student build() {
414         Student result = buildPartial();
415         if (!result.isInitialized()) {
416           throw newUninitializedMessageException(result);
417         }
418         return result;
419       }
420 
421       @Override
422       public Student buildPartial() {
423         Student result = new Student(this);
424         result.id_ = id_;
425         result.name_ = name_;
426         onBuilt();
427         return result;
428       }
429 
430       @Override
431       public Builder clone() {
432         return super.clone();
433       }
434       @Override
435       public Builder setField(
436           com.google.protobuf.Descriptors.FieldDescriptor field,
437           Object value) {
438         return super.setField(field, value);
439       }
440       @Override
441       public Builder clearField(
442           com.google.protobuf.Descriptors.FieldDescriptor field) {
443         return super.clearField(field);
444       }
445       @Override
446       public Builder clearOneof(
447           com.google.protobuf.Descriptors.OneofDescriptor oneof) {
448         return super.clearOneof(oneof);
449       }
450       @Override
451       public Builder setRepeatedField(
452           com.google.protobuf.Descriptors.FieldDescriptor field,
453           int index, Object value) {
454         return super.setRepeatedField(field, index, value);
455       }
456       @Override
457       public Builder addRepeatedField(
458           com.google.protobuf.Descriptors.FieldDescriptor field,
459           Object value) {
460         return super.addRepeatedField(field, value);
461       }
462       @Override
463       public Builder mergeFrom(com.google.protobuf.Message other) {
464         if (other instanceof Student) {
465           return mergeFrom((Student)other);
466         } else {
467           super.mergeFrom(other);
468           return this;
469         }
470       }
471 
472       public Builder mergeFrom(Student other) {
473         if (other == Student.getDefaultInstance()) return this;
474         if (other.getId() != 0) {
475           setId(other.getId());
476         }
477         if (!other.getName().isEmpty()) {
478           name_ = other.name_;
479           onChanged();
480         }
481         this.mergeUnknownFields(other.unknownFields);
482         onChanged();
483         return this;
484       }
485 
486       @Override
487       public final boolean isInitialized() {
488         return true;
489       }
490 
491       @Override
492       public Builder mergeFrom(
493           com.google.protobuf.CodedInputStream input,
494           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
495           throws java.io.IOException {
496         Student parsedMessage = null;
497         try {
498           parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
499         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
500           parsedMessage = (Student) e.getUnfinishedMessage();
501           throw e.unwrapIOException();
502         } finally {
503           if (parsedMessage != null) {
504             mergeFrom(parsedMessage);
505           }
506         }
507         return this;
508       }
509 
510       private int id_ ;
511       /**
512        * <pre>
513        * Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
514        * </pre>
515        *
516        * <code>int32 id = 1;</code>
517        * @return The id.
518        */
519       @Override
520       public int getId() {
521         return id_;
522       }
523       /**
524        * <pre>
525        * Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
526        * </pre>
527        *
528        * <code>int32 id = 1;</code>
529        * @param value The id to set.
530        * @return This builder for chaining.
531        */
532       public Builder setId(int value) {
533         
534         id_ = value;
535         onChanged();
536         return this;
537       }
538       /**
539        * <pre>
540        * Student 类中有 一个属性 名字为 id 类型为int32(protobuf类型) 1表示属性序号,不是值
541        * </pre>
542        *
543        * <code>int32 id = 1;</code>
544        * @return This builder for chaining.
545        */
546       public Builder clearId() {
547         
548         id_ = 0;
549         onChanged();
550         return this;
551       }
552 
553       private Object name_ = "";
554       /**
555        * <code>string name = 2;</code>
556        * @return The name.
557        */
558       public String getName() {
559         Object ref = name_;
560         if (!(ref instanceof String)) {
561           com.google.protobuf.ByteString bs =
562               (com.google.protobuf.ByteString) ref;
563           String s = bs.toStringUtf8();
564           name_ = s;
565           return s;
566         } else {
567           return (String) ref;
568         }
569       }
570       /**
571        * <code>string name = 2;</code>
572        * @return The bytes for name.
573        */
574       public com.google.protobuf.ByteString
575           getNameBytes() {
576         Object ref = name_;
577         if (ref instanceof String) {
578           com.google.protobuf.ByteString b = 
579               com.google.protobuf.ByteString.copyFromUtf8(
580                   (String) ref);
581           name_ = b;
582           return b;
583         } else {
584           return (com.google.protobuf.ByteString) ref;
585         }
586       }
587       /**
588        * <code>string name = 2;</code>
589        * @param value The name to set.
590        * @return This builder for chaining.
591        */
592       public Builder setName(
593           String value) {
594         if (value == null) {
595     throw new NullPointerException();
596   }
597   
598         name_ = value;
599         onChanged();
600         return this;
601       }
602       /**
603        * <code>string name = 2;</code>
604        * @return This builder for chaining.
605        */
606       public Builder clearName() {
607         
608         name_ = getDefaultInstance().getName();
609         onChanged();
610         return this;
611       }
612       /**
613        * <code>string name = 2;</code>
614        * @param value The bytes for name to set.
615        * @return This builder for chaining.
616        */
617       public Builder setNameBytes(
618           com.google.protobuf.ByteString value) {
619         if (value == null) {
620     throw new NullPointerException();
621   }
622   checkByteStringIsUtf8(value);
623         
624         name_ = value;
625         onChanged();
626         return this;
627       }
628       @Override
629       public final Builder setUnknownFields(
630           final com.google.protobuf.UnknownFieldSet unknownFields) {
631         return super.setUnknownFields(unknownFields);
632       }
633 
634       @Override
635       public final Builder mergeUnknownFields(
636           final com.google.protobuf.UnknownFieldSet unknownFields) {
637         return super.mergeUnknownFields(unknownFields);
638       }
639 
640 
641       // @@protoc_insertion_point(builder_scope:Student)
642     }
643 
644     // @@protoc_insertion_point(class_scope:Student)
645     private static final Student DEFAULT_INSTANCE;
646     static {
647       DEFAULT_INSTANCE = new Student();
648     }
649 
650     public static Student getDefaultInstance() {
651       return DEFAULT_INSTANCE;
652     }
653 
654     private static final com.google.protobuf.Parser<Student>
655         PARSER = new com.google.protobuf.AbstractParser<Student>() {
656       @Override
657       public Student parsePartialFrom(
658           com.google.protobuf.CodedInputStream input,
659           com.google.protobuf.ExtensionRegistryLite extensionRegistry)
660           throws com.google.protobuf.InvalidProtocolBufferException {
661         return new Student(input, extensionRegistry);
662       }
663     };
664 
665     public static com.google.protobuf.Parser<Student> parser() {
666       return PARSER;
667     }
668 
669     @Override
670     public com.google.protobuf.Parser<Student> getParserForType() {
671       return PARSER;
672     }
673 
674     @Override
675     public Student getDefaultInstanceForType() {
676       return DEFAULT_INSTANCE;
677     }
678 
679   }
680 
681   private static final com.google.protobuf.Descriptors.Descriptor
682     internal_static_Student_descriptor;
683   private static final 
684     com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
685       internal_static_Student_fieldAccessorTable;
686 
687   public static com.google.protobuf.Descriptors.FileDescriptor
688       getDescriptor() {
689     return descriptor;
690   }
691   private static  com.google.protobuf.Descriptors.FileDescriptor
692       descriptor;
693   static {
694     String[] descriptorData = {
695       "
21StudentPOJO.proto"#
07Student22

02id3001 01" +
696       "(052214
04name3002 01(	B
B13StudentPOJOb06proto3"
697     };
698     descriptor = com.google.protobuf.Descriptors.FileDescriptor
699       .internalBuildGeneratedFileFrom(descriptorData,
700         new com.google.protobuf.Descriptors.FileDescriptor[] {
701         });
702     internal_static_Student_descriptor =
703       getDescriptor().getMessageTypes().get(0);
704     internal_static_Student_fieldAccessorTable = new
705       com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
706         internal_static_Student_descriptor,
707         new String[] { "Id", "Name", });
708   }
709 
710   // @@protoc_insertion_point(outer_class_scope)
711 }
View Code

  可以看到生成一堆信息。

5. 修改之前的类信息,客户端用Prodobuf 编码,服务器端用Protobuf 解码

Serverl

package netty.codec;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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;

public class NettyServer {

    public static void main(String[] args) throws InterruptedException {
        // 1. 创建bossGrou好eworkerGroup
        // bossGroup只负责连接请求,其他交给workerGroup, 两个都是无线循环
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        // 创建服务器端启动对象用于设置参数
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 使用链式编程设置参数
        bootstrap.group(bossGroup, workerGroup)// 设置两个组
                .channel(NioServerSocketChannel.class) // 设置服务器的通道
                .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列得到连接个数
                .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
                .childHandler(new ChannelInitializer<SocketChannel>() { // 设置通道测试对象(匿名对象)
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 加入ProtobufEncoder
                        socketChannel.pipeline().addLast(new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                        socketChannel.pipeline().addLast(new ServerHandler());
                    }
                });

        System.out.println("服务端is ok。。。");

        // 启动服务器并绑定端口。绑定一个端口并且同步,生成一个ChannelFuture对象
        ChannelFuture channelFuture = bootstrap.bind(6666).sync();
        // 对关闭通道进行监控
        channelFuture.channel().closeFuture().sync();
    }
}

ServerHandler

package netty.codec;


import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/**
 * 自定义服务器端处理handler,需要继承netty定义的ChannelInboundHandlerAdapter 类
 */
public class ServerHandler extends SimpleChannelInboundHandler<StudentPOJO.Student> {

    /**
     * 读取事件
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, StudentPOJO.Student msg) throws Exception {
        System.out.println("客户端发送的数据 id=" + msg.getId() + " 名字=" + msg.getName());
    }

    /**
     * 数据读取完毕事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // 将数据写到客户端(write + flush)
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端!", CharsetUtil.UTF_8));
    }

    /**
     * 发生异常事件
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

Client

package netty.codec;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufEncoder;

public class NettyClient {

    public static void main(String[] args) throws InterruptedException {
        // 创建一个事件循环组
        EventLoopGroup eventExecutors = new NioEventLoopGroup();
        try {
            // 创建一个启动Bootstrap(注意是Netty包下的)
            Bootstrap bootstrap = new Bootstrap();
            // 链式设置参数
            bootstrap.group(eventExecutors) // 设置线程组
                    .channel(NioSocketChannel.class) // 设置通道class
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 加入ProtobufEncoder
                            socketChannel.pipeline().addLast(new ProtobufEncoder());
                            socketChannel.pipeline().addLast(new ClientHandler());
                        }
                    });
            System.out.println("客户端is ok...");

            // 启动客户端连接服务器(ChannelFuture 是netty的异步模型)
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();
            // 监听关闭通道
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 关闭
            eventExecutors.shutdownGracefully();
        }
    }
}

ClientHandler

package netty.codec;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * 自定义服务器端处理handler,需要继承netty定义的ChannelInboundHandlerAdapter 类
 */
public class ClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 通道就绪事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ClientHandler ctx: " + ctx);
        ctx.writeAndFlush(StudentPOJO.Student.newBuilder().setId(1).setName("张三").build());
    }

    /**
     * 读取事件
     *
     * @param ctx 上下文对象,含有pipeline管道;通道channel;地址address等信息
     * @param msg 客户端发送的数据(实际类型是ByteBuf - netty 封装的ByteBuffer)
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 强转为netty的ByteBuffer(实际就是包装的ByteBuffer)
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println("服务器会送的消息是:" + byteBuf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器地址:" + ctx.channel().remoteAddress());
    }

    /**
     * 发生异常事件
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

3. 案例二

  实现一个对象包含两个对象。 根据类型传递不同的对象,服务器端根据对应的对象解开。

(1) proto 文件如下

MyData.proto

syntax = "proto3";
option optimize_for = SPEED; // 加快解析
option java_package="netty.codec";   //指定生成到哪个包下
option java_outer_classname="MyDataInfo"; // 外部类名, 文件名

//protobuf 可以使用message 管理其他的message
message MyMessage {

  //定义一个枚举类型
  enum DataType {
    StudentType = 0; //在proto3 要求enum的编号从0开始
    WorkerType = 1;
  }

  //用data_type 来标识传的是哪一个枚举类型
  DataType data_type = 1;

  //表示每次枚举类型最多只能出现其中的一个, 节省空间
  oneof dataBody {
    Student student = 2;
    Worker worker = 3;
  }

}

message Student {
  int32 id = 1;//Student类的属性
  string name = 2; //
}
message Worker {
  string name=1;
  int32 age=2;
}

(2) cmd 用如下命令生成java 类

protoc.exe --java_out=. MyData.proto

(3) ClientHandler 改造:

    /**
     * 通道就绪事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ClientHandler ctx: " + ctx);
        MyDataInfo.MyMessage myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(1).setName("李四").build()).build();
        ctx.writeAndFlush(myMessage);
    }

(4) ServerHandler 改造

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) throws Exception {
        if (msg.getDataType().equals(MyDataInfo.MyMessage.DataType.StudentType)) {
            System.out.println("student name: " + msg.getStudent().getName());
        }
    }
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】
原文地址:https://www.cnblogs.com/qlqwjy/p/14607966.html