2017-09-04 30 views
0

Netty 및 프로토콜 버퍼 (및 암호화는 사용하고 있지만이 문제에는 영향을주지 않음)를 사용하려고합니다. 서버는 Netty를 사용하여 Java로 작성되고 클라이언트는 C 및 Java로 작성되어야합니다. 다음은 Java 서버 측 코드입니다.Netty + 프로토콜 버퍼 Java <-> C 통신 문제

Application 클래스 :

@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 - 자바는 2.6.1
protobuf-C는 1.0.2
libprotoc 2.6.1

는 기본적으로 이것이 일부 예제 코드는 그물에서 가져 와서 조금 수정했습니다. 하드 코드 된 값이나 다른 결함을 보지 마십시오. 이는 학습 목적으로 만 사용됩니다. 나는 문제없이 자바 클라이언트를 사용하여 서버와 통신 할 수있다. 그러나 C 클라이언트에서 동일한 메시지를 보내려고하면 서버가 즉시 예외를 throw합니다. 구분에 대한 내용을 읽었지만이를 처리하는 방법을 모르겠습니다. 나는 또한 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); 

당신은 당신 whant으로 바이트 순서 BIG_ENDIAN 또는 LITTLE_ENDIAN를 지정해야합니다.