这实际上是我在这里的第一篇文章,一段时间以来我一直在试图弄清楚这一点,但是我终于打电话给该旗帜,并将尝试寻求有关此主题的一些帮助。
因此,我有一个客户端和一个服务器,它们是根据回显客户端/服务器和安全聊天客户端/服务器建模的。我对聊天的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; } }
再次感谢大家,您可以为我提供的任何帮助,帮助我理解这一点。
您忘记附加\r\n到"test"。它应该是:channel.write("test\r\n").`
\r\n
"test"
channel.write("test\r\n")
从管道中可以看到,解码部分由两个处理程序组成。第一个将接收到的数据拆分并合并为单个字符串行,并剥离以其结尾的行。第二个将字符串的单行转换为java.lang.String。
java.lang.String
在编码方面,只有一个处理程序将a转换java.lang.String为ByteBuf,这就是它的全部工作。也许是更好的引入处理函数调用LineEncoder,LineDecoder以及LineCodec那确实平时工作预计:https://github.com/netty/netty/issues/1811
ByteBuf
LineEncoder
LineDecoder
LineCodec