protocolBuf的跨平台基础使用1

pb的知识点:

1.协议文件

.proto文件,这是协议原始文件,通过它,可以转换成对应平台的代码,java中直接转成java类,cpp中转成.h和.cc文件。.proto文件中有几个关键字:optional的字段表示可用可不用,required表示必须要用,repeated的作用类似于数组了。转换后的文件中,包含协议中的字段,还包含各种对应的函数。


2.编码要点

pb中的编码是比较特殊的,常规的设计中 int32占4个字节,填入的数据,不管多大,都占4个字节;而pb中的int32是一种特殊编码,称作varint,如果数据小,比如<128,则只占一个字节,这是比较特殊的地方,联想到其他类型,基本上都是这种情况。另外负数情况(因为负数在计算机一般是很大的数据),pb中使用了zigzag方式来处理这种情况,依然使字节可保持的很小。另外pb中的字节序是小端。


3.简单使用

pb2.6.0,服务端java+netty,客户端cpp vs2017

首先找到pb2.6.0,下载对应的lib库和一个编译好的protoc.exe如图


配置cpp平台:使用vs2017,解压上面的protobuf-2.6.0.zip后,如图


打开vsprojects文件夹后,打开protobuf.sln,然后使用release方式编译全部项目

期间可能出现的问题:

1.右击 libprotobuf,属性->c/c++预处理器->预处理器定义中加入:_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS



编译成功后会生成几个用到的lib。这是在改解决方案中创建一个测试项目,这里我创建了一个控制台项目:


右击改测试项目,属性->c/c++->附加包含目录中填入H:\protobuf-2.6.0\src,这里我把那个上面的zip解压到了H盘。

接着,链接器->附加包含目录中填入H:\protobuf-2.6.0\vsprojects\Release,这个release是之前编译生成的。


定义一个协议


最后在cmd中,找到前面说到的protoc.exe文件夹,进入,使用  protoc.exe ./GCToLS.proto --java_out=./    生成java的目标文件;再使用 protoc.exe ./GCToLS.proto --cpp_out=./   生成cpp的目标文件。


在cpp测试项目中:

// ConsoleApplication1.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "GCToLS.pb.h"
#include <winsock2.h>
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include <google/protobuf/io/coded_stream.h>
#define BUFSIZE 4096 /*缓冲区大小*/
#pragma comment(lib, "libprotobuf.lib")  
#pragma comment(lib, "libprotoc.lib")
#pragma comment(lib, "WS2_32")
using namespace std;
int main()
{
	GCToLS::AskLogin ask;
	ask.set_platform(321);
	ask.set_sessionid("888");
	ask.set_uin("555");

	cout << ask.platform() << endl;

	int length = ask.ByteSize()+4;
	char *msg = new char[length];
	google::protobuf::io::ArrayOutputStream arrayOut(msg, length);
	google::protobuf::io::CodedOutputStream codedOut(&arrayOut);
	codedOut.WriteVarint32(ask.ByteSize());
	ask.SerializeToCodedStream(&codedOut);

	//ask.SerializePartialToArray(msg, length);

	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0)
	{
		return 0;
	}

	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sclient == INVALID_SOCKET)
	{
		printf("invalid socket !");
		return 0;
	}

	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(10001);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		printf("connect error !");
		closesocket(sclient);
		return 0;
	}

	send(sclient, msg, length, 0);

	char recData[255];
	int ret = recv(sclient, recData, 255, 0);
	if (ret > 0)
	{
		recData[ret] = 0x00;
		printf(recData);
	}
	closesocket(sclient);
	WSACleanup();

	while (true) {};
	return 0;

}



在java中:

创建加入protocol的jar,2.6.0,创建一个netty简单项目,

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;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import GCToLS.GCToLSOut;
public class ProtoBufServer {
    
    public void bind(int port) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                            ch.pipeline().addLast(new ProtobufDecoder(GCToLSOut.AskLogin.getDefaultInstance()));
                            ch.pipeline().addLast(new ProtoBufServerHandler());
                        }
                    });

            ChannelFuture f = b.bind(port).sync();
            System.out.println("init start");
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 10001;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException e) {
            }
        }
        new ProtoBufServer().bind(port);
    }

}


import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.List;

import GCToLS.GCToLSOut;
import GCToLS.GCToLSOut.AskLogin;

public class ProtoBufServerHandler extends ChannelInboundHandlerAdapter {
    int count=0;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    	System.out.println(count++);
    	GCToLSOut.AskLogin req = (AskLogin) msg;
        System.out.println("平台:" + req.getPlatform() + ","+req.getUin() + "," + req.getSessionid());

    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close(); 
    }

}

亲测,一切ok。


另外对上述几个说明:

google::protobuf::io::ArrayOutputStream arrayOut(msg, length);
google::protobuf::io::CodedOutputStream codedOut(&arrayOut);
codedOut.WriteVarint32(ask.ByteSize());
ask.SerializeToCodedStream(&codedOut);

替换成

      ask.SerializePartialToArray(msg, length);

那么将java中的ProtobufVarint32FrameDecoder与new ProtobufDecoder(GCToLSOut.AskLogin.getDefaultInstance()去掉,在ProtoBufServerHandler中直接获取字节,在用

AskLogin req = GCToLSOut.AskLogin.parseFrom(target);来获取对应的内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Protocol Buffers(简称ProtoBuf)是一种由Google开发的轻量级的数据交换格式,它可以用于数据存储、通信协议,甚至是API更新的新版本。相比XML等格式,ProtoBuf更加高效,且在跨语言系统之间的数据交换中具有更好的兼容性。 在C语言中使用Protocol Buffers就需要下载Protocol Buffers for C的库。它提供了几个库函数来编译和解析协议缓冲区,同时也提供了一个示例工程,演示如何使用ProtoBuf来实现协议缓冲区的编解码,可以实现跨平台的数据交换。 在下载Protocol Buffers for C时,需要先确定适合自己的操作系统和编译器。首先需要去官网(https://developers.google.com/protocol-buffers/docs/downloads)下载安装Protocol Buffers源码包,并提取出C源码库,然后打开命令行窗口,进入到Protocol Buffers for C的源码库目录下运行make命令编译。 编译成功后,再进行安装,使用make install命令将include目录中的头文件和lib目录中的库文件拷贝到相应的系统目录中。在自己的工程中包含头文件,并链接上对应的库文件,即可在自己的项目中使用ProtoBuf。 总之,下载和使用Protocol Buffers for C的过程需要注意一些细节问题,但一旦成功进行了集成,便可以为C语言项目带来更加高效和便捷的数据交换方式。 ### 回答2: protocolbuff是一个可扩展的二进制数据交换格式,它可以在不同的平台和语言之间进行数据通信与存储,并且具有高效、快速、可读性好的特点。在C语言中使用protocolbuff,我们需要下载相应的库来支持。 首先,打开网页浏览器,进入protocolbuff的官方网站(https://developers.google.com/protocol-buffers/),在网页上的下载页面中可以找到适用于C语言的protocolbuff库的下载链接。 点击链接后,进入下载页面,可以看到不同版本的protocolbuff库的下载链接。选择适合自己操作系统和编译环境的版本,点击下载链接即可开始下载。 下载完成后,解压缩下载的文件,可以看到其中包含了protocolbuff相关的头文件和库文件。将这些文件复制到自己的项目文件夹中,或者将其与系统的头文件和库文件路径进行关联,以便在C语言的程序中调用protocolbuff相关函数。 接下来,在自己的C语言代码中引入protocolbuff的头文件,并编写相应的代码来使用protocolbuff进行数据的序列化和反序列化。 在编译过程中,需要将protocolbuff的库文件链接到自己的程序中,以便正确运行。 最后,编译并运行自己的C语言程序,就可以开始使用protocolbuff进行数据通信与存储了。 以上就是使用protocolbuff在C语言中进行数据交换的下载和使用过程的简要介绍。希望对你有所帮助! ### 回答3: protocolbuf-c是Protocol Buffers的一个C语言扩展库,用于在C语言程序中使用Protocol Buffers序列化数据。为了下载protocolbuf-c,可以按照以下步骤进行: 1. 打开网站https://github.com/protobuf-c/protobuf-c,这是Protocol Buffers C库的官方GitHub页面。 2. 点击绿色的"Code"按钮,然后选择“Download ZIP”选项,下载最新的protocolbuf-c压缩文件。 3. 解压下载的ZIP文件,你将得到一个包含protocolbuf-c代码的文件夹。 4. 打开该文件夹,你会看到一些示例代码、文档和编译脚本。 5. 根据你的需求,选择适合你的操作系统的编译脚本。 6. 执行编译脚本,这将会生成protocolbuf-c的可执行文件和库文件。 7. 将生成的可执行文件和库文件复制到你的项目中。 8. 在你的C程序中,包含protocolbuf-c头文件并链接protocolbuf-c库文件。 9. 现在,你可以在你的C程序中使用Protocol Buffers来序列化和反序列化数据了。 请注意,下载protocolbuf-c之前,你需要确保你的系统已经安装了Protocol Buffers的编译器protoc。如果你还没有安装protoc,你可以访问https://github.com/protocolbuffers/protobuf/releases下载适合你的操作系统的安装包,并按照官方指南安装。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值