精华内容
下载资源
问答
  • Unity3D & Java 基于 Protobuf 通信实现,客户端代码
  • Unity3D & Java 基于 Protobuf 通信实现

    千次阅读 2015-05-28 17:27:17
    Unity3D & Java 基于 Protobuf 通信实现最近研究Unity3D,同时需要给游戏制定一套通信协议。因为本人是后端出生,对C#的 Socket相关通信框架不太熟悉,经过几天的学习,终于搞定了。在这里公布出来,大家可以共同...

    Unity3D & Java 基于 Protobuf 通信实现

    最近研究Unity3D,同时需要给游戏制定一套通信协议。因为本人是后端出生,对C#的 Socket相关通信框架不太熟悉,经过几天的学习,终于搞定了。在这里公布出来,大家可以共同学习,少走弯路。
    本文重点:演示怎么解析和发送协议。

    技术选型


    服务端1
    Java7
    netty 4
    客户端2
    C#
    SuperSocket.ClientEngine https://clientengine.codeplex.com/
    它是从SuperSocket中分离出来的,不人性的是竟然没使用教程
    通信协议3
    Protobuf https://github.com/google/protobuf/
    因为Protobuf 官方只支持 Java,C++,Pythone语言,C#需要第三方支持
    protobuf-csharp https://code.google.com/p/protobuf-csharp-port/

    Protobuf 相关的使用,请自行Gooogle,后面的代码会展示相关API, Goole打不开买一个代理一个月20RMB

    开发前准备


    协议解析,无论任何语言协议解析在通信中使用中都是必须的。要成功的解析协议,必须先搞清楚协议是如何制定的。
    Protobuf 是基于 变长消息头(length) + 消息体(body)

    Proto生成

    message Request {
        required string command = 1;
        required string data = 2;
    }
    
    message Response {
        required string command = 1;
        required string data = 2;
    }

    至于怎么生成,我这里就不给出详细方式了,通过Protobuf资料,有详细说明。

    客户端代码


            public static DataEventArgs buffer = new DataEventArgs(); 
    
            public static int count = 0;
    
            public static void Main (string[] args)
            {
    
                buffer.Data = new byte[8192]; //8 KB
                IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8001);
    
                //supersocket clientengine 
                AsyncTcpSession client = new AsyncTcpSession (endPoint);
                client.Connected += OnConnected;
                client.DataReceived += OnDataReceive; //重点解析在这里
    
                //连接服务器
                client.Connect ();
    
                //构造Message,属性Protobuf的人应该都能看懂
                Request.Builder builder = Request.CreateBuilder ();
                builder.SetCommand ("110");
                builder.SetData ("1231231232131");
    
                Request request = builder.BuildPartial ();  
                sendMessage (client, request);
    
                Thread.Sleep (30000);
            }
    
            public static void OnConnected(Object sender, EventArgs e)
            {
                Console.WriteLine ("connect to server finish.");
            }
    
            /**
            * 这里 C# 的实现和Protobuf 官方给的Java实现是一样的
            */
            public static void sendMessage(AsyncTcpSession client, Request request)
            {
                using(MemoryStream stream = new MemoryStream())
                {
                    CodedOutputStream os = CodedOutputStream.CreateInstance(stream);
                    //一定要去看它的代码实现,
                    os.WriteMessageNoTag(request); 
                    /**
                    * WriteMessageNoTag 等价于 WriteVarint32, WriteByte(byte[])
                    * 也就是:变长消息头 + 消息体
                    */
    
                    os.Flush();
    
                    byte[] data = stream.ToArray();
                    client.Send ( new ArraySegment<byte>(data) );
                }
            }
    
            /**
            * 协议解析,把这里搞明白了,就没白看
            */
            public static void OnDataReceive(Object sender, DataEventArgs e) 
            {
                //DataEventArgs 里面有 byte[] Data是从协议层接收上来的字节数组,需要程序端进行缓存
                Console.WriteLine ("buff length: {0}, offset: {1}", e.Length, e.Offset);
                if( e.Length <= 0 )
                {
                    return;
                }
    
                //把收取上来的自己全部缓存到本地 buffer 中
                Array.Copy (e.Data, 0, buffer.Data, buffer.Length, e.Length);
                buffer.Length += e.Length;
    
                CodedInputStream stream = CodedInputStream.CreateInstance (buffer.Data);
                while ( !stream.IsAtEnd ) 
                {
                    //标记读取的Position, 在长度不够时进行数组拷贝,到下一次在进行解析
                    int markReadIndex = (int)stream.Position;
    
                    //Protobuf 变长头, 也就是消息长度
                    int varint32 = (int)stream.ReadRawVarint32();
                    if( varint32 <= (buffer.Length - (int)stream.Position) )
                    {
                        try
                        {
                            byte[] body = stream.ReadRawBytes (varint32);
    
                            Response response = Response.ParseFrom (body);                      
                            Console.WriteLine("Response: " + response.ToString() + ", count: " + (++count));
                            //dispatcher message, 这里就可以用多线程进行协议分发
    
                        }catch(Exception exception)
                        {
                            Console.WriteLine(exception.Message);
                        }
                    } 
                    else 
                    {
                        /**
                        * 本次数据不够长度,缓存进行下一次解析
                        */
                        byte[] dest = new byte[8192];
                        int remainSize = buffer.Length - markReadIndex;
                        Array.Copy(buffer.Data, markReadIndex, dest, 0, remainSize);
    
                        /**
                         * 缓存未处理完的字节 
                         */
                        buffer.Data = dest;
                        buffer.Offset = 0;
                        buffer.Length = remainSize;
    
                        break;
                    }
                }
            }

    后记


    客户端完整代码打包:http://download.csdn.net/detail/zeus_9i/8748899

    展开全文
  • JavaProtoBuf

    2015-09-03 22:12:24
    google 提供了三种语言的实现java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者...

    安装

    protocolbuffer是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了三种语言的实现:javac++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域

    本篇博客主要教大家如何在windows7下安装Java的protocol buffer(具体使用及注意事项将会在下一篇博客当中进行详细介绍) 

    首先,要使用protocol buffer得保证maven安装成功,maven的下载地址:http://maven.apache.org/download.cgi 。

    1.解压完之后请将maven的bin目录配置到你的环境变量当中。

    2.请确保你的JAVA_HOME的变量是指向你的JDK的主目录,如果你的系统变量中没有JAVA_HOME这一项,请点击新建添加。

    3.打开命令行,输入“mvn --version”如果输出正确则表示安装成功

    安装完maven之后就要进行protocol buffer的安装了,下载地址: http://code.google.com/p/protobuf/downloads/list 。下载protobuf-2.4.1.zip 和 protoc-2.4.1-win32.zip 两个包。

    1. 解压完成之后有两种选择,第一:将protoc-2.4.1-win32中的protoc.exe所在的目录配置到环境变量当中,第二:将protoc.exe拷贝到c:\windows\system32目录下,这里推荐第二种做法。

    2. 将proto.exe文件拷贝到解压后的protobuf-2.4.1\src目录中.

    3. 进入protobuf-2.4.1\java 目录  执行mvn package命令编辑该包,系统将会在target目录中生成protobuf-java-2.4.1.jar文件(注意运行时需要联网,首次安装可能需要一定的时间)。

    4. 假设你的数据文件目录在XXX\data目录,把上一步生成的jar拷贝到该目录中即可。

    5. 进入XXX\protobuf-2.4.1\examples目录,可以看到addressbook.proto文件,在命令行中执行 protoc --java_out=. addressbook.proto 命令(特别注意. Addressbook.proto中间的空格,我第一次安装就因为没注意而反复失败),如果生成com文件夹并且最终生成AddressBookProtos类则说明安装成功。

    6. 打开eclipse,选择windows-->preferences-->java-->Installed JREs编辑你默认的java源码包,并将上面所提到的protobuf-java-2.4.1.jar文件添加进去。

    使用

    创建一个.proto文件并在里面定义消息格式把你需要序列化的数据在里面声明类型和名称,并将这个文件命名为addressbook.proto。具体内容如下:

    Proto代码  收藏代码
    1. package tutorial;  
    2.   
    3. option java_package = "com.example.tutorial";  
    4. option java_outer_classname = "AddressBookProtos";  
    5.   
    6. message Person{  
    7.     required string name = 1;  
    8.     required int32  id =2;  
    9.     optional string email = 3;  
    10.       
    11.     enum PhoneType{  
    12.         MOBILE = 0;  
    13.         HOME = 1;  
    14.         WORK = 2;  
    15.     }  
    16.       
    17.     message PhoneNumber{  
    18.         required string number = 1;  
    19.         optional PhoneType type  = 2 [default = HOME];  
    20.     }  
    21.       
    22.     repeated PhoneNumber phone = 4;  
    23. }  
    24.   
    25. message AddressBook{  
    26.     repeated Person person = 1;  
    27. }  

    正如你所看到的,它的语法类似C++或者Java,让我们通过该文件的每一个部分来分析一下吧。

    首先,这个文件以一个package的定义开头,以防在不同工程中的命名冲突,在Java里面,package就是用来当做Java package(除非你有明确的定义一个java package)。 不过,在这里,即使你已经提供了一个java_package,你仍然需要定义一个package以防Protocol Buffer使用在其他没有Java语言的环境中。

    在package的定义之后,你可以看到两个option(选项):java_package以及java_outer_classname。java_package是用来定义java类应该在哪个包里面生成,如果你没有写这一项的话,那么他默认的会以你的package的定义来生成。Java_outer_classname这一项是用来定义在这个文件中的哪一个类需要包含所有的这些类的信息,如果你不定义这一项的话,那么它会以你的文件名来作为刻板,比如说如果你写的是“my_proto.proto”的话,那么它就会默认的使用“MyProto”来作为类名。

    接下来就是你消息的类型定义了。一个message就是一系列类型领域的集合,许多基础的数据类型在这里面都是可用的,包括bool,int32,float,double,以及string等。你同样可以添加更多的结构化的数据在里面,比如上面的Person message就包含了PhoneNumber message,而AddressBook message包含了Person message。你同样在一中message类型里面定义另外一种类型,例如上面所举到的,Person里面所定义的enum(枚举)类型,以区分PhoneNumber的不同类型。

    在每一个元素之后的“=1”,“=2”是用来区分它们的独特的“标签”。标签数字1-15编码所需的字节数比更高的数字所需的字节数要少一个,所以为了使程序达到最佳的状态,你可以使用这些标签进行反复标记(在同一个域中不能重复)。每一个元素在重复的领域都需要重新编码这些“标签”,所以重复的领域应该考虑到更多可能的方案来达到最佳状态。

    每一个域的前面都必须使用下面这些修饰符来修饰:

    ·required(必需的): 这说明这个域的值不能为空,否则这条message将会被当做“不知情的”。如果你尝试的创建一条“不知情的”message,那么系统将会抛出一个RuntimeException(运行时异常)。而分析一条“不知情的”message则会抛出IOException。除此之外,确切的来说一个被required修饰的域其行为则更接近optional修饰的域。 

    ·optional(可选择的): 被这个修饰符修饰的域可以为空。如果一个被optional修饰的域没有设值的话,那么系统将会使用默认值。对于一些基本类型来说,你可以定义你自己的默认值(就像我前面在定义PhoneNumber的PhoneType时一样)。 否则,对于数值类型来说,系统的默认值是0,string的默认值是empty string,bool的默认值是false。对于植入的message来说(比如AddressBook里面的Person),默认值则经常是该消息默认的实例或者标准。调用存取器去获取那些被optional(或者required)修饰的但还没有被初始化的域将会返回它的默认值。

    ·repeated(反复的): 某一个域可能会被使用多次,而那些反复使用的值将会被保留在protocol buffer里面。 你可以把用repeated修饰的域想象成动态数组。

     

    Required是“永久”的。

    当你使用required来修饰域的时候你必须非常的小心。如果某些时候你想要停止发送一个用required修饰的域并将它修改为optional修饰时,之前的readers会把你的message考虑为不完整的并且无意识的丢弃它。事实上,人们已经开始注意到了使用required的所带来的危害,所以我们应该更多的使用optional和repeated。


    使用Protocol buffer的编译器


        现在,你已经有一个.proto文件了,接下来就需要生成class来发送和读取AddressBook消息(包含Person以及PhoneNumber),为了完成这件工作,你需要调用protocol buffer的编译器的proto指令来编译你的.proto文件。语法如下:
                             <span style="white-space:pre">		</span>proto -I=$SRC --java_out=$DIR File
    $SRC表示资源文件夹,$DIR表示目标文件夹,File是你要编译的.proto文件,当然,你也可以定位到这个资源目录中然后只调用proto --java_out=$DIR File即可。(需要注意的是生成的class文件会存在于你所填的目标文件夹下的java_package所指向的目录中,如果你想把目标文件夹指向当前目录,你可以使用“.”来代替,例如对于该例,我们先定位到这个目录下,然后运行:
    <span style="white-space:pre">					</span>proto --java_out=. addressbook.proto)


    使用protocol buffer的API


    首先,让我们看看编译器给我们生成了那些代码。首先你可以发现它的类名与我们定义的java_outer_classname的名字相同,同样里面还包含了你在addressbook.proto里面定义的各种message的类,每一个类都有它自身的Builder用来实例化这个类对象。所有的messagesbuilders都自动生成了存取器,但是messages只有getterbuilders既有getters又有setters。以下就是Person类的一些存取器:

    // required string name = 1;

    public boolean hasName();

    public String getName();

    // required int32 id = 2;

    public boolean hasId();

    public int getId();

    // optional string email = 3;

    public boolean hasEmail();

    public String getEmail();

    // repeated .tutorial.Person.PhoneNumber phone = 4;

    public List<PhoneNumber> getPhoneList();

    public int getPhoneCount();

    public PhoneNumber getPhone(int index);

    同样的Person.Builder 也有这样的存取器:

    // required string name = 1;

    public boolean hasName();

    public java.lang.String getName();

    public Builder setName(String value);

    public Builder clearName();

    // required int32 id = 2;

    public boolean hasId();

    public int getId();

    public Builder setId(int value);

    public Builder clearId();

    // optional string email = 3;

    public boolean hasEmail();

    public String getEmail();

    public Builder setEmail(String value);

    public Builder clearEmail();

    // repeated .tutorial.Person.PhoneNumber phone = 4;

    public List<PhoneNumber> getPhoneList();

    public int getPhoneCount();

    public PhoneNumber getPhone(int index);

    public Builder setPhone(int index, PhoneNumber value);

    public Builder addPhone(PhoneNumber value);

    public Builder addAllPhone(Iterable<PhoneNumber> value);

    public Builder clearPhone();

    正如你所看到的一样,这些都是一些简单的JavaBean-stylegetterssetters,但是用repeated修饰的域有一些特殊的方法,Count方法(用来统计这个消息list(列表)的长度)。通过add 方法可以在这个list中追加一个元素,而addAll 方法可以把一个Container(容器)里面的所有元素都添加在list当中。

    注意到这些方法都是使用的驼峰式命名法,尽管在.proto文件里面我们都是写的小写,这也恰恰展示了protocol buffer的强大之处。

    另外,还有一个需要注意的就是enum(枚举)类型所生成的类,它自动生成了如下代码:

    public static enum PhoneType {
      MOBILE(0, 0),
      HOME(1, 1),
      WORK(2, 2),  ;  ...}

     

    Builders vs. Messages

    Message类里面由protocol buffer编译器自动生成的代码都是不可变的,一旦一个message对象被实例化之后,它就不能再被修改了,就像Java中的String一样。而如果想要实例化一个message类,你必须首先实例化一个builder类,然后设置好所有你想要设置的属性,然后再调用builder类的build()方法。你或许已经注意到了builder的每一个用来改变message的属性的方法都返回了另外一个builder。不要怀疑,这个 builder就是为了让你更加方便的定义其他属性而存在的。下面就展示了一段用来创建一个新的Person类的代码:

    Person john = Person.newBuilder().setId(1234).setName("John Doe").setEmail("jdoe@example.com") .addPhone( Person.PhoneNumber.newBuilder().setNumber("555-4321")        .setType(Person.PhoneType.HOME))    .build();

    标准的Message方法

    每一个message以及builder类都包含了一些其他的方法用来检测和操作所有的message,这些方法包括:

    ·isInitialized()检测所有用required修饰的域是否都设置了初始值。

    ·toString()返回一个可读的message,特别是用来测试的时候 

    ·mergeFrom(Message other): (builder独有的把另一个message合并到这个message当中,重写单独域并且连接反复的域。

    ·clear(): (builder独有的)清空所有的域并且返回空的状态

    这些方法都实现了MessageMessage.Builder 接口并且接口被所有的Java messages以及builders共享。

     

    解析和序列化

    最后,所有的protocol buffer类都有writingreading你所选择的protocol buffer (二进制)格式数据的方法。他们包括:

    ·byte[] toByteArray();序列化这个 message 并且返回一个字节数组。

    ·static Person parseFrom(byte[] data);从给定的字节数组中解析一条message

    ·void writeTo(OutputStream output);序列化这个message并且将它写入 OutputStream.

    ·static Person parseFrom(InputStream input);读取并且解析一条InputStream中的message

    这里只是其中的几个解析和序列化的方法而已,如果想要知道其中所有的方法可以将该类生成doc文档然后查看。

     

    Protocol BuffersO-O Design

     Protocol buffer的类基本上是无声的数据持有者(就像 C++里面的结构体一样);他们最开始在一个对象模型中表现的并不友好,如果你想要为生成的类添加一个更加友好的方法的话,最好的方式是将protocol buffer生成的类包装在一个特定的应用程序里面。对于不太懂.proto文件设计的人来说包装protocol buffer也是一个不错的主意。你也可以使用包装类生成接口以便更好地适应特定的程序环境:隐藏一些数据和方法,并且显示一些便捷的功能,等等。特别需要注意的是,你最好不要写一些行为去继承这些生成的类。那会打破它内部的机制况且它对于你来说也不是一次很好的面向对象的练习机会。

     

    Writing A Message

    OK,说了这么多,那就来使用一下protocol buffer生成的类吧。首先,你肯定希望你的这个“addressbook”的应用程序可以write一个你定义的message。为了完成这项工作,你需要创建一个新的类来调用protocol buffer类里面的方法并将message写入OutputStream.

    下面就是一个将用户在控制台输入的AddressBook 的相关信息写入文件的一个类,当然,你首先得创建一个文件(当然你也可以在文件不存在的情况下使用File类的createNewFile()方法来创建一个新的文件),为了个性化你的程序,不妨以.book作为你的后缀名,具体代码如下:

     

    Java代码  收藏代码
    1. import java.io.BufferedReader;  
    2. import java.io.FileInputStream;  
    3. import java.io.FileNotFoundException;  
    4. import java.io.FileOutputStream;  
    5. import java.io.IOException;  
    6. import java.io.InputStreamReader;  
    7. import java.io.PrintStream;  
    8.   
    9. import com.example.tutorial.AddressBookProtos.AddressBook;  
    10. import com.example.tutorial.AddressBookProtos.Person;  
    11. class AddPerson {  
    12.     /** 
    13.      * 将用户输入的Person message写入输出流中   
    14.      * @param stdin 输入流 
    15.      * @param stdout 打印输出流 
    16.      * @return Person类 
    17.      * @throws IOException 
    18.      */  
    19.     static Person PromptForAddress(BufferedReader stdin,PrintStream stdout)  
    20.             throws IOException {  
    21.           
    22.         Person.Builder person = Person.newBuilder();  
    23.         stdout.print("Enter person ID: ");  
    24.         person.setId(Integer.valueOf(stdin.readLine()));  
    25.   
    26.         stdout.print("Enter name: ");  
    27.         person.setName(stdin.readLine());  
    28.   
    29.         //空白表示没有  
    30.         stdout.print("Enter email address (blank for none): ");  
    31.         String email = stdin.readLine();  
    32.         if (email.length() > 0){  
    33.             person.setEmail(email);  
    34.         }  
    35.         while (true) {  
    36.             //按下Enter键结束输入  
    37.             stdout.print("Enter a phone number (or leave blank to finish): ");  
    38.             String number = stdin.readLine();  
    39.             if (number.length() == 0) {  
    40.                 break;  
    41.             }  
    42.               
    43.             Person.PhoneNumber.Builder phoneNumber = Person.PhoneNumber.newBuilder().setNumber(number);  
    44.               
    45.             //输入完成之后需要确定你输入的是手机号、家庭电话还是工作电话  
    46.             stdout.print("Is this a mobile, home, or work phone? ");  
    47.             String type = stdin.readLine();  
    48.             if (type.equals("mobile")) {  
    49.                 phoneNumber.setType(Person.PhoneType.MOBILE);  
    50.             } else if(type.equals("home")){  
    51.                     phoneNumber.setType(Person.PhoneType.HOME);  
    52.             } else if (type.equals("work")) {  
    53.                 phoneNumber.setType(Person.PhoneType.WORK);  
    54.             } else {  
    55.                 stdout.println("Unknown phone type.Using default.");  
    56.             }  
    57.             person.addPhone(phoneNumber);  
    58.             }  
    59.             return person.build();  
    60.         }  
    61.       
    62.     //Main function:  Reads the entire address book from a file,  
    63.     //adds one person based on user input, then writes it back out to the same  
    64.     //file.    
    65.     public static void main(String[] args)  
    66.             throws Exception {  
    67.           
    68. //      if (args.length != 1) {  
    69. //          System.err.println("Usage:  AddPerson ADDRESS_BOOK_FILE");  
    70. //          System.exit(-1);  
    71. //          }  
    72.         AddressBook.Builder addressBook = AddressBook.newBuilder();  
    73.           
    74.         // 检验是否存在这个文件  
    75.         try {  
    76.             addressBook.mergeFrom(new FileInputStream("src/Book/TestPerson.book"));  
    77.             } catch (FileNotFoundException e) {  
    78.                 System.out.println("src/Book/TestPerson.book" + ": File not found.Creating a new file.");  
    79.             }      
    80.           
    81.         //将这条Person message添加到AddressBook中  
    82.         addressBook.addPerson(PromptForAddress(new BufferedReader(new InputStreamReader(System.in)),System.out));  
    83.           
    84.         //将新建的AddressBook写入文件当中  
    85.         FileOutputStream output = new FileOutputStream("src/Book/TestPerson.book");  
    86.         addressBook.build().writeTo(output);  
    87.         output.close();    
    88.     }  
    89. }  

    Reading A Message

    当然,这个程序肯定不止一个写入消息的类,还要能把存在文件中的数据读出来。如下:

     

    Java代码  收藏代码
    1. import com.example.tutorial.AddressBookProtos.AddressBook;  
    2. import com.example.tutorial.AddressBookProtos.Person;  
    3. import java.io.FileInputStream;  
    4. class ListPeople {  
    5.     /** 
    6.      * 迭代遍历并且打印文件中所包含的信息 
    7.      * @param addressBook AddressBook对象 
    8.      */  
    9.     static void Print(AddressBook addressBook) {  
    10.         for (Person person: addressBook.getPersonList()){  
    11.             System.out.println("Person ID: " + person.getId());  
    12.             System.out.println("Name: " + person.getName());  
    13.             if (person.hasEmail()) {  
    14.                 System.out.println("E-mail address:"+ person.getEmail());  
    15.                 }  
    16.             for (Person.PhoneNumber phoneNumber : person.getPhoneList()) {  
    17.                 switch (phoneNumber.getType()) {  
    18.                 case MOBILE:  
    19.                     System.out.print("Mobile phone #: ");  
    20.                     break;      
    21.                 case HOME:  
    22.                     System.out.print("Home phone #: ");  
    23.                     break;  
    24.                 case WORK:  
    25.                     System.out.print("Work phone #: ");  
    26.                     break;  
    27.                     }        
    28.                     System.out.println(phoneNumber.getNumber());  
    29.                 }  
    30.             }  
    31.         }  
    32.       
    33.     public static void main(String[] args) throws Exception {  
    34. //      if (args.length != 1) {  
    35. //          System.err.println("Usage:  ListPeople ADDRESS_BOOK_FILE");   
    36. //          System.exit(-1);  
    37. //      }   
    38.         // 读取已经存在.book文件  
    39.         AddressBook addressBook = AddressBook.parseFrom(new FileInputStream("src/Book/TestPerson.book"));  
    40.         Print(addressBook);  
    41.     }  
    42.  }  

     

    扩展一个Protocol Buffer

    当你发布了一段由你的protocol buffer编写的代码之后,你或许迫不及待的想要扩展它的功能。如果你想要使你的新buffers是反向兼容的或者你的旧buffers是正向兼容的话,那么下面有几条规则是你需要遵守的,在新的protocol buffer的版本中:· 你最好不要改变已经存在的域的标签(Tag)

       ·你最好不要添加或者删除任何用required修饰的域

          ·你可以删除optional或者repeated修饰的域

       ·你可以添加新的用optional或者repeated修饰的域但你必须使用Tag数字(从未被这个protocol所使用过的tag,即使是被删除了的也不行).

    如果你遵循这些规则,旧的代码也会非常“高兴”的读取新的消息。对于旧的代码来说,那些被删除了的用optional修饰的域会有他们的默认值并且被删除了用repeated修饰的域会为空,新的代码读取旧的消息也会很轻松。然而,请记住,新的optional域不会存在于旧的message当中,所以你应该明确的知道它们是否被设置为has_或者在你的.proto 文件提供了一个合理的默认值[default = value]。如果默认值没有明确是一个optional元素,而是按默认类型定义的话对于string来说默认值就是empty string,其他类型也类型,在本文的上面已经提到过了,这里就不在累赘。特别声明,如果你添加了一个新的用repeated修饰的域而没有has_标志的话,你的新代码将无法识别它是否为空,而你的旧代码则完全无法设置它。

     

    高级用法

    Protocol buffers还有一些用法是一般的存取器和序列化所无法办到的,如果你需要了解更多的信息可以上Google的官方文档上面去查看。

    Protocolmessage类提供的一个关键特征就是映射 在一个message里面你可以反复声明不同的域并且操作他们的值而不需要在任何类型的message之前写代码。使用映射来从其他的编码中转换一条message无疑是一个非常有效的方法,即使面对XML 或者JSON也一样。关于映射的一个更加高级的用法恐怕就是找出两条相同类型的message类之间的不同点了,或者是为Protocol buffer生成一系列的“规则映像”,使用这些映像你可以匹配确切的消息内容。发挥你的想象,Protocol Buffers可以应用到更多的领域当中去。





    展开全文
  • 这里介绍如何通过protobufjava中做序列化。首先,我们需要准备protoc编译环境,这个是前提,我们写好了proto文件,当然需要生成对应的java类,这时候,就需要protoc-2.x.x-win32工具将其转为java类。这里我使用的...

    protobuf是google开源的序列化工具,他支持很多语言。这里介绍如何通过protobuf在java中做序列化。首先,我们需要准备protoc编译环境,这个是前提,我们写好了proto文件,当然需要生成对应的java类,这时候,就需要protoc-2.x.x-win32工具将其转为java类。这里我使用的是protoc-2.4.1-win32工具,这个在网上可以下载,下载之后,解压到本地,然后将/path/to/protoc路径加入环境变量的path变量中,这样,我们可以在命令行下运行protoc命令了。如下所示:

    准备一个User.proto文件,内容如下,我们定一个User类,下面有4个属性,分别是id,name,age,mobile。

    option java_outer_classname="UserProtoBuf";

    option java_package="com.xxx.serial";

    message User{

    required int32 id=1;

    required string name=2;

    optional int32 age=3;

    optional string mobile=4;

    }

    虽然语法和java有些区别,而且数据类型也不太一样,但是基本能够看得懂这个proto文件表示的含义。它指定了生成的java类名和对应的包名package,使用message关键字定义了User对象,最后定义了四个属性字段。

    我们在命令行下通过如下命令,就可以生成一个Java类了:

    E:\javaworkspace\javaee\src\main\java>protoc --java_out=. com\xxx\serial\User.proto

    没有任何报错,那么生成的文件就是UserProtoBuf.java,截图如下:

    接下来,我们需要在maven工程中加入protobuf-java依赖,我们要使用protoc创建的java类,必须要加入protobuf-java这个依赖。

    com.google.protobuf

    protobuf-java

    2.6.1

    最后,我们就通过一个测试类来使用我们序列化之后的UserProtoBuf类。

    package com.xxx.serial;

    import com.xxx.serial.UserProtoBuf.User;

    public class UserProtoMain {

    public static void main(String[] args) {

    UserProtoBuf.User.Builder builder = UserProtoBuf.User.newBuilder();

    builder.setId(1);

    builder.setName("protoc-java");

    builder.setAge(18);

    builder.setMobile("15011186302");

    User user = builder.build();

    System.out.println(user.toString());

    }

    }

    运行之后,打印信息如下:

    这篇文章,简单介绍了如何从proto文件到java文件,再到使用生成的java文件做序列化的过程。在实际应用中,protobuf的数据类型比这个还要多,序列化可能更复杂。

    展开全文
  • java调用protobuf

    千次阅读 2014-07-19 11:26:41
    google 提供了三种语言的实现java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构...

        protocolbuffer(以下简称PB)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了三种语言的实现:java、c++ 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。---摘自百度百科。

        从上面可以看出,protobuf其实是一种传输协议,这个跟thrift有很大不同。thrift是Facebook开源的一整套的RPC方案,但是它却总是被拿来跟protobuf做对比,这个有很大的误导,正确的说是,thrift里面不仅仅包含了传输协议。因此,跟protobuf相比较的应该xml或者是json。我们可以来简单的说一下,protobuf相较于xml或者json的区别,protobuf是基于将数据采用二进制来序列化的,而xml或者json则是基于文本数据的,现在高下立判,protobuf比xml或者说json更加高效和安全,而且序列化反序列化方便。

        之前,看protobuf总是一副高大上的样子,让人望而生畏。最近需要写个项目,采用udp去跟另一边用.net开发的服务通讯,所以采用protobuf作为两边之间通信协议(对象序列化反序列化协议),于是就写了下demo。发觉其实非常简单,我们如果用protobuf更多的只需要关注底层的传输就OK了,剩下的序列化反序列化它其实已经说好了。

        用java写最简单的protobuf只需要两件东西,一件是将proto文件(protobuf独有的用来规定各种不同语言间统一数据描述规范)转成java实体类的程序(我在windows下用protoc.exe文件,这个文件code.google.com有编译好的),另一个就是java的protobuf 实现的jar包了。

       <dependency>
              <groupId>com.google.protobuf</groupId>
              <artifactId>protobuf-java</artifactId>
              <version>2.5.0</version>
       </dependency>
    
    接下来在写proto文件

    package protobuf;
    option java_package = "com.xxx.simpleProtobuf";
    option java_outer_classname = "LogMessageProtobuf";
    message LogMessage  {
      required int32 ID = 1;
      required string Url = 2;
      required string message = 3;
    }

    定义好了之后,需要用protoc.exe 翻译成java文件,记住protoc跟java的接口jar包的版本要一致。

    在命令行运行protoc  --java_out=./   LogMessageProtobuf.proto

    接下来就可以在同一个目录下看到com/xxx/simpleProtobuf里面的 LogMessageProtobuf.java文件

    import com.google.protobuf.InvalidProtocolBufferException;
    
    /**
     * Created by lsz on 2014/7/18.
     */
    public class ProtobufDemo{
        public static void main(String[] args) throws InvalidProtocolBufferException {
            LogMessageProtobuf.LogMessage  logMsg = LogMessageProtobuf.LogMessage.newBuilder()
    						 .setID(1)
    						.setUrl("http://www.xxxx.com")
    					       .setMessage("xxxx").build();
            System.out.println(logMsg);
            byte[] bs = tb.toByteArray();
            LogMessageProtobuf.LogMessage  logMsgFromBytes = LogMessageProtobuf.LogMessage.parseFrom(bs);
            System.out.println(logMsgFromBytes);
    
    
    
        }
    }


    运行成可以看到logMsg跟logMsgFromBytes的内容是一样的,根据上面的例子,我们会发现,真正保存这个对象数据的就是bs这个字节数组,没有其他的内容,那我们是不是可以通过其他途径去传输这个byte数组,让其他的地方也可以得到这个数据对象呢。




    展开全文
  • 一:protobuf编码基本数据类型public enumFieldType {DOUBLE (JavaType.DOUBLE , WIRETYPE_FIXED64 ),FLOAT (JavaType.FLOAT , WIRETYPE_FIXED32 ),INT64 (JavaType.LONG , WIRETYPE_VARI...
  • Netty是当前非常流行的网络...通过学习可以快速学习Netty的底层实现机制,熟练运用Netty解决网络高并发问题。Netty涉及内容很多(比如:设计模式、数据结构、并发、同步、异步,阻塞等),目前网上的Netty课程不多,...
  • 原标题:用 Maven 实现一个 protobufJava 例子来源:jihite ,www.cnblogs.com/kaituorensheng/p/9022591.html1. 介绍Protocol BuffersProtocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化...
  • 我计划使用 springboot + protocol buffer + https 完成一个java后台,但是在使用protocol buffer做接口的数据交换...但是protocol buffer作为接口的数据交换格式的时候却没法找到这样的实现技术。我希望找到能够解...
  • 注:试验环境在Mac Idea环境下1. 介绍Protocol BuffersProtocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化...其类似于XML生成和解析,但protobuf的效率高于XML,不过protobuf生成的是字节码,...
  • 1. 介绍Protocol Buffers在我们学习Java语言的路上,我们知道Protocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化、反序列化,常用于RPC 系统(Remote Procedure Call Protocol System)和持续...
  • 我来记一下流水帐(仅实现“服务器端发送,客户端读取后显示”):一、服务器端环境搭建(基于PHP)1、服务器的搭建工作就略去了,列举一下环境版本如下:PHP Version 5.3.2-1,Apache/2.2.14,Ubuntu10.04 LTS2、由于...
  • 问题背景是:线上有若干的C#的WCF服务需要调用,由于我们的应用是先切换的,在对方服务不改变的情况下,要能做到我们切换成java之后,能够实现访问的平滑过渡。其中一部分服务的请求有对应的.proto契约文件,利用pr....
  • 比如protobuf、thrift等等。服务发现。指服务提供者更新接口后,服务使用者如何知道该接口更新。Protobuf协议使用的是预编译方式,dubbo中使用的是zk作为媒介。负载均衡。流量控制、熔断。运维工具。常见RPC框架有...
  • 上一篇博客中只讲解到了简单的使用protobuf,还不会的可以先去看一下【Android项目使用Protobuf教程(结合Retrofit+RxJava及HttpURLConnection使用)】,有位小伙伴问我如何使用泛型呢?比如每次网络请求都会有一些...
  • Protobuf协议--java实现

    2019-10-03 04:12:37
    Protobuf协议,全称:Protocol Buffer它跟JSON,XML一样,是一个规定好的数据传播格式。不过,它的序列化和反序列化...定义protobuf 文件,并使用protoc.exe 编译 为java文件。 protoc.exe 下载地址https://github.c...
  • 这种情况在分布式用的比较多,类似的Xml和Json也可以实现这种效果;下面放一个Java序列化的例子1.创建实例化对象package com.bean;import java.io.Serializable;import java.util.List;/*** @author: jane* @...
  • 我们之前曾讲过java自带的一种序列化机制,...这篇文章我们就看一下第一个序列化框架protobuf,给出一个简单案例,看看其是如何实现的。注:若你对序列化概念和基本使用还有疑惑,可以翻看我之前的文章,或者百度...
  • jmeter实现protobuf格式参数传输

    千次阅读 2018-07-20 10:38:54
    第一步:通过java代码实现将参数转换成pd格式并保存 第二步:通过jmeter发压 1、将protobuf格式的数据文件保存在一个文件夹下,如/mnt/test_data/intelligent_scheduling/drs/csvdata/filter/ 2、将文件的名字...
  • 一、extension的用法由于extension是protobuf2中一个比较高级,但是在proto3中禁用的功能,所以在这里还是看下这个内容的实现,完整的实现参考来自下面文章。为了避免跳转或者连接失效,这里把原文章内容拷贝一份:...
  • 来源:jihite ,www.cnblogs.com/kaituorensheng/p/9022591.html1. 介绍Protocol BuffersProtocal Buffers(简称protobuf)是谷歌的一项技术,用于结构化的数据序列化、反序列化,常用于RPC 系统(Remote Procedure ...
  • 使用protobuf实现跨语言序列化 Java和Python实例首先下载安装protocgithub.releases:https://github.com/protocolbuffers/protobuf/releases对于OS X可以通过brew直接安装brew install protobuf安装完可以通过protoc...
  • protobuf序列化实例(Java实现

    千次阅读 2019-07-05 13:09:34
    这里介绍如何通过protobufjava中做序列化。首先,我们需要准备protoc编译环境,这个是前提,我们写好了proto文件,当然需要生成对应的java类,这时候,就需要protoc-2.x.x-win32工具将其转为java类。这里我使用的...
  • Java 实现grpc实例--json转protobuf

    千次阅读 2019-09-08 16:32:57
    如上链接中讲的已经很清楚了,相同的内容不再赘述,这里只说下用Java实现grpc客户端代码的时候,如何直接解析参数到对应的类中(即直接将json转为protobuf)。 public void greet(String name)...
  • 上一篇中大概描述了c++ java中关于protobuf反序列化对象实体和实体处理(函数)关系,并贴出了java实现方案,针对c++版本也只是简单的描述了一下 采用std::bind().这里采用 id+ 指针,通过ID索引到指针,利用多态性...
  • 比如protobuf、thrift等等。服务发现。指服务提供者更新接口后,服务使用者如何知道该接口更新。Protobuf协议使用的是预编译方式,dubbo中使用的是zk作为媒介。负载均衡。流量控制、熔断。运维工具。常见RPC框架有...
  • protobuf(JAVA)

    2019-03-15 23:22:40
    1.介绍 参考: 用Maven实现一个protobufJava例子 Java使用protobuf入门四步骤 protobuf中文教程(第一篇) protobuf 3.5 java使用介绍(一) 2.使用 后续更新…
  • JAVA中如何使用 maven引入: com.dyuproject.protostuff protostuff-core 1.0.12 com.dyuproject.protostuff protostuff-runtime 1.0.12 工具类: package com.protobuf.util; import java.io....
  • 我们之前曾讲过java自带的一种序列化机制,...这篇文章我们就看一下第一个序列化框架protobuf,给出一个简朴案例,看看其是若何实现的。注:若你对序列化观点和基本使用另有疑惑,可以翻看我之前的文章,或者百度...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 545
精华内容 218
关键字:

java实现protobuf

java 订阅