2017-08-21 7 views
0

J'écris un service de recherche de protocole personnalisé fonctionnant sur TCP. Tout fonctionne parfaitement lorsque vous utilisez EmbeddedChannel pour les tests. Pour tester plus loin, j'ai écrit un serveur et ajouter les gestionnaires. À la demande d'un client Socket java simple, le serveur reçoit les données, traite et renvoie une réponse. Cependant, la réponse n'atteint pas le socket client. Je pensais que je me suis trompé avec le grand canalPipeline. Je réduis donc l'implémentation à un seul gestionnaire entrant. Ça ne marche toujours pas. Quelqu'un peut-il aider ici?Simple Netty Server ne pas envoyer de réponse

Serveur:

public void start() throws Exception{ 
    EventLoopGroup bossGroup = new NioEventLoopGroup(); 
    EventLoopGroup workerGroup = new NioEventLoopGroup(); 

    try { 
     final KaiExceptionHandler kaiExceptionHandler = new KaiExceptionHandler(); 
     ServerBootstrap b = new ServerBootstrap(); 
     b.group(bossGroup, workerGroup) 
       .channel(NioServerSocketChannel.class) 
       .childHandler(new ChannelInitializer<SocketChannel>() { 
        @Override 
        protected void initChannel(SocketChannel socketChannel) throws Exception { 
         ChannelPipeline pipeline = socketChannel.pipeline(); 
         pipeline.addLast(new SimpleHandler()); 
        } 
       }); 
     ChannelFuture future = b.bind(new InetSocketAddress("localhost", 9400)).sync(); 
     future.addListener(new ChannelFutureListener() { 
      @Override 
      public void operationComplete(ChannelFuture channelFuture) throws Exception { 
       if(channelFuture.isSuccess()) { 
        LOGGER.info("Kai Server is bounded to '{}'", "localhost:9400"); 
       }else { 
        LOGGER.error("Failed to bound Kai to 'localhost:9400'", channelFuture.cause()); 
       } 
      } 
     }); 
     future.channel().closeFuture().sync(); 
    }finally { 
     workerGroup.shutdownGracefully(); 
     bossGroup.shutdownGracefully(); 
    } 

Handler simple:

public class SimpleHandler extends ChannelInboundHandlerAdapter { 

@Override 
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 
    Charset charset = Charset.defaultCharset(); 
    ctx.write(Unpooled.copiedBuffer("Client is not seeing this", charset)); 
    ctx.flush(); 
} 

@Override 
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 
    ctx.flush(); 
} } 

Client test. Une implémentation non-soignée. Cependant, juste pour le test.

public class TestClient { 


public static void main(String[] args) throws Exception { 

    Socket socket = new Socket("localhost", 9400); 
    InputStream is = socket.getInputStream(); 
    StringBuilder sb = new StringBuilder(); 
    byte[] buffer = new byte[64]; 
    int r = 0; 
    socket.setSoTimeout(10000); 
    System.out.println("Reading..."); 
    while ((r = is.read(buffer)) != -1) { 
     sb.append(new String(buffer).trim()); 
    } 
    System.out.println("String: " + sb.toString()); 
} 

}

Répondre

1

Votre programme de test en supposant que la connexion se ferme après avoir écrit les données, et ses chaînes ne sont pas fragmentés en supposant dans le cas de l'envoi de caractères multi-octets.

Si l'attente jusqu'à la fermeture de la prise est intentionnelle, vous devez changer votre déclaration write à ce qui suit:

ctx.write(Unpooled.copiedBuffer("Client is not seeing this", charset)) 
    .addListener(ChannelFutureListener.CLOSE); 

Ce moyen de communication est inefficace cependant, parce que vous avez besoin de rouvrir une autre connexion prise à chaque fois vous voulez dire quelque chose au serveur. Le meilleur moyen serait de rendre le protocole soit basé sur une ligne, ou délimité par un champ de longueur, puis lire les réponses ligne par ligne dans le client en utilisant BufferedReader, et sur le côté serveur, vous devez ajouter un retour à la fin de chaque message.

Incase le serveur doit recevoir un message, vous devez ajouter un new LineBasedFrameDecoder() au début de votre pipeline, suivi d'un new StringDecoder() en option pour laisser Netty tourner automatiquement ByteBuf s en chaînes, vous ne vous avez pas à faire plus . Netty peut également faire cela en sens inverse en utilisant un StringEncoder, de sorte que vous pouvez simplement écrire un objet chaîne, au lieu de l'encapsuler à chaque fois dans un ByteBuf.

+0

Je vois. 'read (buffer)! = -1' est vrai si le serveur ferme le socket. Cependant, je ne veux pas que ... Le protocole est basé sur les binaires. La requête et la réponse sont envoyées en binaire avec une taille d'en-tête fixe de 17 octets et un corps optionnel qui est également en binaire. Ai-je besoin d'un délimiteur, lorsque je connais la taille exacte du corps de la réponse à lire dans l'en-tête? Si oui, comment? –

0

Vu le dernier problème. Tout le problème était avec le client de test.

Premier problème:
Le premier a été pointé par Ferrybig, dans lequel le read(buffer) != -1 attend la fermeture du socket (Merci pour cet homme).

Deuxième problème:
La deuxième était que, le ChannelInboundHandlerAdapter#channelRead(ChannelHandlerContext ctx, Object msg) jamais été appelé car il n'y avait rien à lire car le socket n'envoie rien. Modifier mon client pour envoyer quelque chose le fait fonctionner.

Nouveau client:

public class TestClient { 


public static void main(String[] args) throws Exception { 

    Socket socket = new Socket("localhost", 9400); 
    socket.setSoTimeout(10000); 
    OutputStream raw = socket.getOutputStream(); 
    // Will be the request bytes 
    raw.write(1); 
    raw.flush(); 

    InputStream is = socket.getInputStream(); 
    StringBuilder response = new StringBuilder(); 
    // actual expected size will be here 
    final int expectedResponse = 128; 
    byte[] buffer = new byte[expectedResponse]; 
    int bytesRead = is.read(buffer); 
    if (bytesRead != -1) { 
     response.append(new String(buffer).trim()).append("\n"); 
    } 
    System.out.println("String: " + response.toString()); 
} 

}