Skip to main content
  1. Posts/

C++与Java Socket通信使用Protobuf

·295 words·2 mins
Blogs Tutorials Netty C++ Socket Protobuf
Gwen0x4c3
Author
Gwen0x4c3
java n go is my mother tongue
Table of Contents

Protobuf: Protobuf-3.6.1,内容是CMake、vs2017编译后的使用protobuf所需要的lib与protoc.exe,有Debug和Release版本,可以直接使用不用编译了。也包含需要到的头文件

我把要用的东西都放在gitee里了,不知道你们能不能用。我C++只学到了stl水平,windows mfc的没学,讲的不对的话见谅。Java用的Netty

例子:

一、写个message.proto的文件
#

    syntax = "proto2";
    package com.li.cppserver;  //java: com.li.cppserver.Message.CppMessage
    //package Msg;             //c++:   Msg::CppMessage
    message CppMessage
    {
    	required int32 action = 1;
        optional string username = 2;
    	optional string password = 3;
    	optional bool success = 4;
    	optional bytes content = 5;
    }

数据类型对照:

C++给Java发还涉及编码的问题,所以我的content使用了bytes,java生成的.java文件中提供了转码的函数,使用方便多了。

二、在protoc.exe同级目录下执行cmd命令
#

C++:

    protoc.exe --cpp_out=./ message.proto

Java:

    protoc.exe --java_out=./ message.proto

C++的会生成两个文件:message.pb.cc和message.pb.h,我们直接复制到工程的头文件目录下。

Java的直接复制到对应package下,然后引入依赖:

    <dependency>
         <groupId>com.google.protobuf</groupId>
         <artifactId>protobuf-java</artifactId>
         <version>3.6.1</version>
    </dependency>

三、修改项目配置
#

需要分别配置Release和Debug

相同配置:

1、项目 - 属性 - 常规,我的配置如图。

2、项目 - 属性 - C/C++ - 常规,在“附加包含目录”中添加包含所需头文件的文件夹的路径,就是我仓库里src的路径,你也可以下载官方的,就是图中的这个文件夹。

不同配置:

项目 - 属性 - 链接器 - 常规 - 附加库目录中 添加lib所在文件夹的路径,Release配置Release的,Debug配Debug的。

四、代码中的使用
#

C++:就酱紫蟀,基本的set,取success属性的时候是message.success()。他这个message需要parse一下,我就不多说了,可以上网搜。

    Msg::CppMessage SocketManager::receive(char * outBuf, int len){
    	Msg::CppMessage message;
    	message.set_success(false);
    	try{
    		int	charCount;
    		if (socket == INVALID_SOCKET || socket == SOCKET_ERROR)
    			return message;
    
    		charCount = recv(socket, outBuf, len, 0);
    		if( charCount != SOCKET_ERROR && charCount > 0) {
    			outBuf[charCount] = '\0';
    		} else {
    			return message;
    		}
    		string receiveStr(outBuf, charCount + 1);
    		message.ParseFromString(receiveStr);
    		return message;
    	}catch(ConnException e){
    		cout<<"error in receive.."<<endl;
    		return message;
    	}
    }	

Java:非常简单,Encoder是我写的,Decoder是用的netty中的

    //ProtobufEncoder
    public class ProtobufEncoder extends MessageToByteEncoder<Message.CppMessage> {
        @Override
        protected void encode(ChannelHandlerContext channelHandlerContext, Message.CppMessage message, ByteBuf out) throws Exception {
            byte[] bytes = message.toByteArray();
            out.writeBytes(bytes);
        }
    }

    //Initializer
        @Override
        protected void initChannel(SocketChannel channel) {
    
            ChannelPipeline pipeline = channel.pipeline();
    
            pipeline.addLast("decoder", new ProtobufDecoder(Message.CppMessage.getDefaultInstance()));
    
            pipeline.addLast(new ProtobufEncoder());
    
            pipeline.addLast(new IdleStateHandler(60, 70, 80));
    
            pipeline.addLast(serverHandler);
        }

    //Handler
    @Slf4j
    @Component
    @ChannelHandler.Sharable
    public class CppServerHandler extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
            Message.CppMessage message = (Message.CppMessage) msg;
            NioSocketChannel channel = (NioSocketChannel) ctx.channel();
            String id = (String) channel.attr(AttributeKey.valueOf("id")).get();
            log.info("接收到消息{}", message.info());
    
            switch (message.getAction()) {
                case 0:
                    this.register(channel, message);
                    break;
                case 1:
                    sendMessage(id, message);
                    break;
                case 2:
                    Message.CppMessage.Builder builder = Message.CppMessage.newBuilder()
                            .setAction(1)
                            .setSuccess(true)
                            .setContent(encode("[INFO] 服务器获取到心跳"));
                    ctx.writeAndFlush(builder.build());
                    break;
            }
    
        }
    
        private ByteString encode(String content) {
            try {
                return ByteString.copyFrom(content, "GBK");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        ...
    }

希望对你们有用^_^