2012-12-17 80 views
10

這實際上是我在這裏的第一篇文章,我一直試圖弄清楚這一段時間,但我終於打電話在國旗,並試圖得到一些關於這個話題的幫助。Netty客戶端到服務器的消息

所以我有一個客戶端和一個服務器,仿效回聲客戶端/服務器和安全聊天客戶端/服務器。我對聊天的SSL部分不感興趣,只是爲了確保我得到客戶端/服務器的響應。我將在這篇文章的底部添加所有相關的代碼。我現在遇到的問題是,我可以在客戶端連接時從服務器向客戶端發送消息,但是當服務器向客戶端發送初始消息時,我無法從客戶端向服務器發送消息。從服務器發送的信息是:

Welcome to the server! 

來自客戶機的消息是

test 

我應該知道,我得到了來自客戶端的消息,因爲它應該回顯

[You] test 

我知道服務器看到客戶端,它給了我狀態更新,但由於某種原因,我無法向服務器發送消息。現在在這個問題上...通過一些機會我目前正在使用一個 StringDecoder StringEncoder作爲解碼器和編碼器...如果你正在製作一個遊戲(這是我正在做的)和你會有登錄,玩家動作,世界更新等等的東西......發送字符串是最好的方式來做到這一點?我知道我看到很多字節流,並且在編程課上,我經歷了一些操縱字節流的操作,但我仍然不滿意。如果字節流是最好/最好的方式來做到這一點,那麼有人可以請詳細解釋它是如何處理字節流以處理不同的項目。

正如前面所說,這是一切的客戶端開始:

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

再次大家我感謝所有幫助你能給我理解這一點。

+0

任何服務器端代碼? – Peter

+0

當然抱歉不知道是否有人會想要它,但我還應該補充說,我可以Telnet localhost 45000到我的服務器,它按預期的那樣工作.....我正在編輯原始帖子以添加到服務器碼。 – Maxs728

+0

任何人都可以幫我解決這個問題嗎? – Maxs728

回答

4

你忘了附加\r\n"test"。它應該是:channel.write("test\r\n")。'

正如您從管道中看到的,解碼部分由兩個處理程序組成。第一個將接收到的數據拆分併合併到一行字符串中,然後剝離結束的行。第二個將字符串的單行轉換爲java.lang.String

在編碼方面,只有一個處理程序,它將java.lang.String轉換爲ByteBuf,這就是它的全部功能。也許是更好的引入處理函數調用LineEncoderLineDecoder,和LineCodec,做的通常預期的工作:https://github.com/netty/netty/issues/1811

+1

多數民衆贊成在我的問題是一個真正愚蠢的答案,但諷刺的是,它的工作....哇,我不知道它是挑剔的信息。 – Maxs728

+0

我不能猜到這一點。奇怪的行爲,是有原因的嗎? – Czechnology

+0

更新了答案以回答評論中的問題。 – trustin

1

DO新的String(「測試」)。這會更通用。 回答你的文章的後半部分 - 在對象中創建一個包含登錄,玩家動作等所有信息的類對象並傳遞它。確保你的類實現了Serializable。 將它作爲字符串是一個糟糕的方式做到這一點,因爲我認爲它會成爲某種hardcoded.Client代碼如下所示:

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()); 
} 
+0

這不提供問題的答案。要批評或要求作者澄清,在他們的帖子下留下評論 - 你總是可以評論你自己的帖子,一旦你有足夠的[聲譽](http://stackoverflow.com/help/whats-reputation),你會能夠[評論任何帖子](http://stackoverflow.com/help/privileges/comment)。 – Achrome

+0

我正在使用它,它的工作原理。通過使用ObjectEncoder和ObjectDecoder更適合於他的Usecase而不是String Decoder。那是他問題的一部分。儘管他原來的問題有一個公認的答案,但我提出了一個完全不同的方法。 – naves

+0

如果你使用這個,它的工作原理,你可以發佈一個代碼片段來說明嗎?你的答案的當前格式並沒有真正的幫助。 – Achrome

相關問題