2017-09-04 78 views
0

我試圖使用Netty和協議緩衝區(和加密,但這不會影響此問題)。服務器使用Netty以Java編寫,客戶端應該使用C語言和Java語言編寫。這是Java服務器端代碼。Netty +協議緩衝區Java <-> C通信問題

應用類:

@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類:

@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類:

@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:

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客戶機:

#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; 
} 

服務器日誌:

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-C 1.0.2
libprotoc 2.6.1

基本上,這是一些示例代碼取自網絡並修改了一下。不要看硬編碼值或其他缺陷。這僅用於學習目的。我可以使用Java客戶端與服務器通信,而不會有任何問題。但是,當我嘗試從C客戶端發送相同的消息時,服務器立即引發異常。我已經閱讀了一些關於分隔的內容,但我不知道如何處理。我也嘗試發送一個消息的長度作爲int首先,然後從C客戶端的實際消息,但它也沒有幫助。加密在這裏不是問題。如果我禁用它,我會得到相同的結果。

我在這裏錯過了什麼?這是否可以與C客戶進行溝通?

+0

你分析過網絡流量嗎?像'wireshark'這樣的工具?只是想知道在哪一邊搜索問題。 – blafasel

回答

0

嘗試使用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); 

你需要指定字節順序BIG_ENDIAN或LITTLE_ENDIAN你whant。