简体   繁体   English

Netty +协议缓冲区Java <-> C通信问题

[英]Netty + Protocol Buffers Java <-> C communication issue

I am trying to use Netty and Protocol Buffers (and encryption but this does not affect this issue). 我正在尝试使用Netty和Protocol Buffer(以及加密,但这不会影响此问题)。 The server is written in Java using Netty and clients are supposed to be written in C and in Java. 服务器使用Netty用Java编写,而客户端应该用C和Java编写。 Here is the Java server side code. 这是Java服务器端代码。

Application class: 应用类别:

@SpringBootApplication
public class Application {

public static void main(String[] args) throws InterruptedException {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
    TcpServer tcpServer = context.getBean(TcpServer.class);
    tcpServer.start();
}

@Autowired
private SomethingChannelInitializer somethingChannelInitializer;

@SuppressWarnings({ "unchecked", "rawtypes" })
@Bean
public ServerBootstrap bootstrap() {
    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup(), workerGroup()).channel(NioServerSocketChannel.class)
            .handler(new LoggingHandler(LogLevel.DEBUG)).childHandler(somethingChannelInitializer);
    Map<ChannelOption<?>, Object> tcpChannelOptions = tcpChannelOptions();
    Set<ChannelOption<?>> keySet = tcpChannelOptions.keySet();
    for (ChannelOption option : keySet) {
        b.option(option, tcpChannelOptions.get(option));
    }
    return b;
}

@Bean(name = "tcpChannelOptions")
public Map<ChannelOption<?>, Object> tcpChannelOptions() {
    Map<ChannelOption<?>, Object> options = new HashMap<ChannelOption<?>, Object>();
    options.put(ChannelOption.SO_KEEPALIVE, true);
    options.put(ChannelOption.SO_BACKLOG, 3);
    return options;
}

@Bean(destroyMethod = "shutdownGracefully")
public NioEventLoopGroup bossGroup() {
    return new NioEventLoopGroup(2);
}

@Bean(destroyMethod = "shutdownGracefully")
public NioEventLoopGroup workerGroup() {
    return new NioEventLoopGroup(2);
}

@Bean
public InetSocketAddress tcpPort() {
    return new InetSocketAddress(12888);
}
}

SomethingChannelInitializer class: SomethingChannelInitializer类:

@Component
public class SomethingChannelInitializer extends ChannelInitializer<SocketChannel> {

@Autowired
private ChannelInboundHandlerAdapter somethingServerHandler;

@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
    ChannelPipeline pipeline = socketChannel.pipeline();

    // SSL stuff

    pipeline.addLast(new ProtobufVarint32FrameDecoder());
    pipeline.addLast(new ProtobufDecoder(ProtocolMessage.OneRequest.getDefaultInstance()));

    pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
    pipeline.addLast(new ProtobufEncoder());

    pipeline.addLast(somethingServerHandler);
}

}

SomethingServerHandler class: SomethingServerHandler类:

@Component
@Sharable
public class SomethingServerHandler extends ChannelInboundHandlerAdapter {

private static Logger logger = LoggerFactory.getLogger(SomethingServerHandler.class);

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {

}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    logger.debug("read ...");
    ProtocolMessage.OneRequest req = (ProtocolMessage.OneRequest) msg;
    switch (req.getType()) {
    case LOGIN:
        logger.debug("{}, {}", req.getLoginRequest().getLogin(), req.getLoginRequest().getPassword());
        break;
    case REGISTER:
        logger.debug("{}, {}", req.getRegistrationRequest().getEmail(), req.getRegistrationRequest().getPassword());
        break;
    default:
        break;
    }

    ProtocolMessage.RegistrationResponse registrationResponse = ProtocolMessage.RegistrationResponse.newBuilder().setStatus("got it").build();
    ProtocolMessage.OneResponse rsp = ProtocolMessage.OneResponse.newBuilder().setRegistrationResponse(registrationResponse).build();
    ctx.writeAndFlush(rsp);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    logger.error(cause.getMessage(), cause);
    //ctx.close();
}

@Override
public void channelInactive(ChannelHandlerContext ctx){

}
}

ProtocolMessage.proto: ProtocolMessage.proto:

package com.company.model;

message LoginRequest {
  required string login = 1;
  required string password = 2;
}

message RegistrationRequest {
  required string login = 1;
  required string email = 2;
  required string password = 3;
}

message RegistrationResponse {
  required string status = 1;
}

message OneRequest {
  enum Type { LOGIN = 1; REGISTER = 2; }

  required Type type = 1;
  oneof request {
    LoginRequest loginRequest = 2;
    RegistrationRequest registrationRequest = 3;
  }
}

message OneResponse {
  oneof response {
    RegistrationResponse registrationResponse = 1;
  }
}

C client: C客户:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#include "ProtocolMessage.pb-c.h"

#define HERR(source) (fprintf(stderr,"%s(%d) at  %s:%d\n",source,h_errno,__FILE__,__LINE__),\
         exit(EXIT_FAILURE))

int main(int argc, char **argv) {
// SSL initialization, making new connection etc.

Com__Company__Model__LoginRequest login = COM__COMPANY__MODEL__LOGIN_REQUEST__INIT;
login.login="sample_login";
login.password="secret_password";
Com__Company__Model__OneRequest req = COM__COMPANY__MODEL__ONE_REQUEST__INIT;
req.loginrequest=&login;
req.type=COM__COMPANY__MODEL__ONE_REQUEST__TYPE__LOGIN;

unsigned len = com__company__model__one_request__get_packed_size(&req);
void *buf = malloc(len);
com__company__model__one_request__pack(&req, buf);

SSL_write(clientssl, buf, len);
printf("SSL server sent %d\n", len);
SSL_shutdown(clientssl);
close(clientsocketfd);
SSL_free(clientssl);
SSL_CTX_free(ssl_client_ctx);
return 0;
}

Server log: 服务器日志:

2017-09-04 18:14:37.209 DEBUG 63166 --- [ntLoopGroup-3-1] io.netty.handler.ssl.SslHandler          : [id: 0x445a7c98, L:/127.0.0.1:12888 - R:/127.0.0.1:50688] HANDSHAKEN: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
2017-09-04 18:14:37.218 ERROR 63166 --- [ntLoopGroup-3-1] p.o.g.handlers.SomethingServerHandler    : com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).

io.netty.handler.codec.DecoderException: com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:98) ~[netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:310) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:297) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:413) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1273) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1084) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138) [netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_91]

Caused by: com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
    at com.google.protobuf.InvalidProtocolBufferException.invalidTag(InvalidProtocolBufferException.java:89) ~[protobuf-java-2.6.1.jar!/:na]
    at com.google.protobuf.CodedInputStream.readTag(CodedInputStream.java:158) ~[protobuf-java-2.6.1.jar!/:na]
    at com.company.model.ProtocolMessage$OneRequest.<init>(ProtocolMessage.java:2037) ~[classes!/:0.0.1-SNAPSHOT]
    at com.company.model.ProtocolMessage$OneRequest.<init>(ProtocolMessage.java:2000) ~[classes!/:0.0.1-SNAPSHOT]
    at com.company.model.ProtocolMessage$OneRequest$1.parsePartialFrom(ProtocolMessage.java:2116) ~[classes!/:0.0.1-SNAPSHOT]
    at com.company.model.ProtocolMessage$OneRequest$1.parsePartialFrom(ProtocolMessage.java:2111) ~[classes!/:0.0.1-SNAPSHOT]
    at com.google.protobuf.AbstractParser.parsePartialFrom(AbstractParser.java:137) ~[protobuf-java-2.6.1.jar!/:na]
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:168) ~[protobuf-java-2.6.1.jar!/:na]
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:174) ~[protobuf-java-2.6.1.jar!/:na]
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:49) ~[protobuf-java-2.6.1.jar!/:na]
    at io.netty.handler.codec.protobuf.ProtobufDecoder.decode(ProtobufDecoder.java:121) ~[netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.protobuf.ProtobufDecoder.decode(ProtobufDecoder.java:64) ~[netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:88) ~[netty-all-4.1.13.Final.jar!/:4.1.13.Final]
    ... 30 common frames omitted

protobuf-java 2.6.1 protobuf-java 2.6.1
protobuf-c 1.0.2 protobuf-c 1.0.2
libprotoc 2.6.1 libprotoc 2.6.1

Basically this is some example code taken from the net and modified a bit. 基本上,这是一些从网络上提取并经过一些修改的示例代码。 Don't look at hardcoded values or other imperfections. 不要看硬编码的值或其他缺陷。 This is only for learning purposes. 这仅用于学习目的。 I can communicate with the server using Java client without any issues. 我可以使用Java客户端与服务器进行通信,而不会出现任何问题。 However, when I try to send the same message from C client the server immediately throws an exception. 但是,当我尝试从C客户端发送相同的消息时,服务器立即引发异常。 I have read something about delimiting but I am not sure how to handle that. 我已经阅读了有关定界的内容,但不确定如何处理。 I have also tried sending the length of a message as int first, and then the actual message from C client but it doesn't help either. 我也尝试过先将消息的长度作为int发送,然后再从C客户端发送实际的消息,但这也无济于事。 Encryption is not an issue here. 加密在这里不是问题。 If I disable it I will get the same results. 如果禁用它,我将得到相同的结果。

What am I missing here? 我在这里想念什么? Is this possible at all to communicate with C clients? 是否可以与C客户端进行通信?

Try to use LengthFieldBasedFrameDecoder instead of ProtobufVarint32FrameDecoder, and send package lengh first: 尝试使用LengthFieldBasedFrameDecoder而不是ProtobufVarint32FrameDecoder,然后先发送lengh包:

void *buf = malloc(len);

// send package len
buf[0] = (len >> 24) & 0xFF;
buf[1] = (len >> 16) & 0xFF;
buf[2] = (len >> 8) & 0xFF;
buf[3] = len & 0xFF;
SSL_write(clientssl, buf, 4);

// send package
com__company__model__one_request__pack(&req, buf);
SSL_write(clientssl, buf, len); 

You need to specify byte order BIG_ENDIAN or LITTLE_ENDIAN as you whant. 您需要指定字节顺序BIG_ENDIAN或LITTLE_ENDIAN。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM