web-dev-qa-db-ja.com

Nettyクライアントからサーバーへのメッセージ

これは実際にここでの私の最初の投稿であり、私はこれをしばらくの間理解しようとしていましたが、私はついに旗を呼び、このトピックについていくつかの助けを得ようとしています。

したがって、エコークライアント/サーバーとセキュアチャットクライアント/サーバーをモデルにしたクライアントとサーバーがあります。チャットのSSL部分に興味がなく、クライアント/サーバーとの間で応答を確実に取得するためにエコーを使用しています。この投稿の最後に、関連するすべてのコードを追加します。現在発生している問題は、クライアントが接続するとサーバーからクライアントにメッセージを送信できるが、サーバーがクライアントに最初のメッセージを送信すると、クライアントからサーバーにメッセージを送信できないことです。サーバーから送信されるメッセージは次のとおりです。

Welcome to the server!

クライアントからのメッセージは

test

クライアントからメッセージを受け取ったので、エコーバックする必要があることを知っておく必要があります

[You] test

サーバーがクライアントを認識し、ステータスが更新されることは知っていますが、何らかの理由でサーバーにメッセージを送信できません。これに加えて質問があります...たまたま私は現在使用しています StringDecoder そして StringEncoder デコーダーおよびエンコーダーとして...ゲームを作成していて(これは私が行っていることです)、ログイン、プレーヤーの動き、世界の更新などがあります...これを行うための最良の方法はStringsを送信することですか?私はバイトストリームをたくさん見ていることを知っています。プログラミングクラスでは、バイトストリームの操作に触れましたが、まだ100%快適ではありません。バイトストリームがこれを行うためのより良い/最良の方法である場合、誰かがバイトストリームを操作してさまざまなアイテムを処理できるようにする方法を詳細に説明できますか?.

前に述べたように、これはクライアントのすべての始まりです。

public class Client {

public Client() {
    // Initialize the window
    GameWindow.init();
    // Initialize the server connection
    ClientHandler.init();
}

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

    // Set a default server address if one isn't specified in the arguments
    if (args.length < 2 || args.length > 3) {
        System.err.println("Usage: " + Client.class.getSimpleName() + " <Host> <port> [<first message size>]");
        System.err.println("Using default values.");
    } else {
        // Parse arguments
        Settings.Host = args[0];
        Settings.port = Integer.parseInt(args[1]);
    }

    // start client
    new Client();
}

ClientHandler:

package simple.client.net;

import Java.net.InetSocketAddress;
import Java.util.concurrent.Executors;
import Java.util.logging.Level;
import Java.util.logging.Logger;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.WriteCompletionEvent;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

import simple.client.Settings;

public class ClientHandler extends SimpleChannelUpstreamHandler {

private static final Logger logger = Logger.getLogger(ClientHandler.class.getName());

public static Channel channel;

public ClientHandler() {
}

public static void init() {
    // Configure the client.
    ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));

    // Set up the pipeline factory.
    bootstrap.setPipelineFactory(new ClientPipelineFactory());

    // Start the connection attempt.
    ChannelFuture future = bootstrap.connect(new InetSocketAddress(Settings.Host, Settings.port));

    // Wait until the connection is closed or the connection attempt fails.
    channel = future.awaitUninterruptibly().getChannel();

    // This is where the test write is <<------
    ChannelFuture test = channel.write("test");

    if (!future.isSuccess()) {
        future.getCause().printStackTrace();
        bootstrap.releaseExternalResources();
        return;
    }
}

@Override
public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Bound: " + e.getChannel().isBound());
}

@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Connected: " + e.getChannel().isConnected());
    System.out.println("Connected: " + e.getChannel().getRemoteAddress());
}

@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Closed: " + e.getChannel());
}

@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Disconnected: " + e.getChannel());
}

@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Open: " + e.getChannel().isOpen());
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
    System.out.println("Error: " + e.getCause());
}

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
    System.out.println("Message: " + e.getMessage());
}
}

そして最後にClientPipeline:

package simple.client.net;

import static org.jboss.netty.channel.Channels.*;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;

public class ClientPipelineFactory implements ChannelPipelineFactory {

public ChannelPipeline getPipeline() throws Exception {
    ChannelPipeline pipeline = pipeline();

    pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    pipeline.addLast("decoder", new StringDecoder());
    pipeline.addLast("encoder", new StringEncoder());
    pipeline.addLast("handler", new ClientHandler());

    return pipeline;
}

}

サーバ側:

package simple.server;

public class Server {
public static void main(String[] args) throws Exception {
    ServerChannelHandler.init();
}
}

ServerChannelHandler:

package simple.server;

import Java.net.InetSocketAddress;
import Java.util.concurrent.Executors;
import Java.util.logging.Logger;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

public class ServerChannelHandler extends SimpleChannelHandler {

private static final Logger logger = Logger.getLogger(ServerChannelHandler.class.getName());

private static ChannelGroup channels;
private static ServerBootstrap bootstrap;

public ServerChannelHandler() {
}

/**
 * Initialize the Server Channel Handler
 */
public static void init() {
    // create a channels group to add incoming channels to
    channels = new DefaultChannelGroup();

    // create the server bootstrap (fancy Word for pre-made server setup)
    bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
            Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));

    // set the server pipeline factory
    bootstrap.setPipelineFactory(new ServerPipelineFactory());

    // server settings
    bootstrap.setOption("keepAlive", true);

    // bind the server to the port
    bootstrap.bind(new InetSocketAddress(Settings.PORT_ID));
}

@Override
public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Bound: " + e.getChannel());
}

@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Connected: " + e.getChannel());
    channels.add(e.getChannel());
    e.getChannel().write("Welcome to the test server!\n\r");
}

@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Closed: " + e.getChannel());
}

@Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Disconnected: " + e.getChannel());
}

@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
    System.out.println("Open: " + e.getChannel());
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
    System.out.println("Error: " + e.getCause());
}

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
    System.out.println("Message: " + e.getMessage());
    for (Channel c : channels) {
        if (e.getMessage().equals("shutdown")) {
            shutdown();
        }
        if (c != e.getChannel()) {
            c.write("[" + e.getChannel().getRemoteAddress() + "] " + e.getMessage() + "\n\r");
        } else {
            c.write("[You] " + e.getMessage() + "\n\r");
        }
    }
}

/**
 * Shuts down the server safely
 */
public static final void shutdown() {
    channels.close();
    bootstrap.releaseExternalResources();
    System.exit(0);
}
}

ServerPipelineFactory:

package simple.server;

import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;

import simple.server.decoder.Decoder;
import simple.server.encoder.Encoder;

public class ServerPipelineFactory implements ChannelPipelineFactory {
@Override
public ChannelPipeline getPipeline() throws Exception {
    ChannelPipeline pipeline = Channels.pipeline();

    pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
    pipeline.addLast("decoder", new StringDecoder());
    pipeline.addLast("encoder", new StringEncoder());
    pipeline.addLast("handler", new ServerChannelHandler());

    return pipeline;
}
}

もう一度、皆さんがこれを理解する上で私に助けてくれることを感謝します。

11
Maxs728

_\r\n_を_"test"_に追加するのを忘れました。 channel.write("test\r\n")。 `のようになります。

パイプラインからわかるように、デコード部分は2つのハンドラーで構成されています。最初のものは、受信したデータを分割して1行の文字列にマージし、それで終わる行を取り除きます。 2つ目は、1行の文字列を_Java.lang.String_に変換します。

エンコーディング側では、_Java.lang.String_をByteBufに変換するハンドラーは1つだけで、それだけです。おそらく、通常期待される仕事をするLineEncoderLineDecoder、およびLineCodecと呼ばれるハンドラーを導入する方が良いでしょう: https://github.com/netty/ netty/issues/1811

5
trustin

新しいString( "test")を実行します。それはもっと一般的でしょう。投稿の後半への回答-ログイン、プレーヤーの動きなどのすべての情報をオブジェクトに含めてクラスオブジェクトを作成し、渡します。クラスがSerializableを実装していることを確認してください。文字列として渡すことは、ハードコードされたものになると思うので、それを行うには悪い方法です。クライアントコードは次のようになります。

ChannelPipeline p = ch.pipeline();
                        p.addLast(
                                new ObjectEncoder(),                            
                                new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader())),
                                 new ClientHandler());              
                     }                  
                 });

                // Start the connection attempt.
            ChannelFuture f=  b.connect(Host, port);
            channel=f.awaitUninterruptibly().channel();
            TestObj obj= new TestObj();
            channel.writeAndFlush(obj);

サーバーコードは次のようになります。

 ChannelPipeline p = ch.pipeline();

                p.addLast(
                       new ObjectEncoder(),                         
                       new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader())),
                        new DiscardServerHandler());
            }

サーバーハンドラーは次のようになります。

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    System.out.println("channelRead"+((TestObj)msg).getCurrency());
}
1
naves