[英]Netty Client to Server message
This is actually my first post on here and I have been trying to figure this out for some time but I am finally calling in the flag and going to try to get some help on this topic. 这实际上是我在这里发表的第一篇文章,我一直试图解决这个问题已经有一段时间了,但我终于打电话给国旗并尝试在这个主题上寻求帮助。
So I have a Client and a Server that was modeled after the echo client/server and the secure chat client/server. 所以我有一个客户端和一个服务器,它是在echo客户端/服务器和安全聊天客户端/服务器之后建模的。 I am not interested in the SSL part of the chat and using the echo just to ensure that I am getting responses to and from the client/server.
我对聊天的SSL部分并不感兴趣,只是为了确保我收到客户端/服务器的响应。 I will add all the pertinent code at the bottom of this post.
我将在本文的底部添加所有相关代码。 The problem I am getting at the moment is that I can send a message from the server to the client upon the client connecting but I am not able to send a message from the client to the Server upon the server sending the client the initial message.
我目前遇到的问题是,我可以在客户端连接时从服务器向客户端发送消息,但是在服务器向客户端发送初始消息时,我无法从客户端向服务器发送消息。 The message being sent from the server is:
从服务器发送的消息是:
Welcome to the server!
The message from the client is 来自客户端的消息是
test
I should know that I got the message from the client cause it should echo back 我应该知道我收到了来自客户端的消息,因为它应该回显
[You] test
I do know that the Server sees the Client and it gives me status updates but I cannot send a message to the server for some reason. 我知道服务器看到客户端,它给我状态更新但我不能出于某种原因向服务器发送消息。 Now for a question on top of this... By some chance I am currently using a StringDecoder and StringEncoder as the Decoder and Encoder... If you are making a game (which is what I am doing) and you will have things like logins, player movements, world updates, etc... is sending Strings the best way to do this?
现在提出一个问题...有一次机会我正在使用StringDecoder和StringEncoder作为解码器和编码器...如果你正在制作一个游戏(这就是我正在做的),你会有类似的东西登录,玩家动作,世界更新等......发送Strings是最好的方法吗? I know I see a lot with byte streams and in my programming class I went through we touched on manipulating byte streams but I am still not 100% comfortable with them yet.
我知道我看到很多字节流,在我的编程课中,我经历过触摸操作字节流,但我仍然不是很满意它们。 If byte streams are the better/best way to do this then can someone please explain in some detail how it works to manipulate a byte stream to be able to handle different items.
如果字节流是更好/最好的方法,那么有人可以详细解释它如何操作字节流以处理不同的项目。
As stated before this is the start of everything in the client: 如前所述,这是客户端中所有内容的开始:
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: 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());
}
}
And finally the ClientPipeline: 最后是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;
}
}
Server Side: 服务器端:
package simple.server;
public class Server {
public static void main(String[] args) throws Exception {
ServerChannelHandler.init();
}
}
ServerChannelHandler: 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: 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;
}
}
Once again everyone I appreciate any help you can give me on understanding this. 大家再一次感谢你能帮助我理解这一点。
You forgot to append \\r\\n
to "test"
. 你忘了将
\\r\\n
附加到"test"
。 It should be: channel.write("test\\r\\n")
.` 它应该是:
channel.write("test\\r\\n")
`
As you see from the pipeline, the decoding part is composed of two handlers. 从管道中可以看出,解码部分由两个处理程序组成。 The first one splits and merges the received data into a single line of string and strips the line ending from it.
第一个将接收到的数据拆分并合并为一行字符串,并删除从它结束的行。 The second one converts the single line of string into
java.lang.String
. 第二个将单行字符串转换为
java.lang.String
。
On the encoding side, there's only one handler, which converts a java.lang.String
into ByteBuf
, and that's all it does. 在编码方面,只有一个处理程序,它将
java.lang.String
转换为ByteBuf
,就是这样。 Perhaps it is better introduce a handler called LineEncoder
, LineDecoder
, and LineCodec
that does the usually expected job: https://github.com/netty/netty/issues/1811 也许最好引入一个名为
LineEncoder
, LineDecoder
和LineCodec
的处理程序来完成通常预期的工作: https : //github.com/netty/netty/issues/1811
DO new String("test"). 做新的字符串(“测试”)。 That would be more generic.
那将是更通用的。 Answer to latter part of your post- Create a Class Object with all your information like Logins, Player movements etc. in the Object and pass it.
回答你的后期部分 - 创建一个类对象,包含对象中的所有信息,如登录,玩家移动等,并传递它。 Make sure your class implements Serializable.
确保您的类实现Serializable。 Passing it as String is a bad way to do it since I assume it will become sort of hardcoded.Client code will look like this:
将它作为String传递是一种不好的方法,因为我认为它将成为一种硬编码。客户端代码将如下所示:
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);
Server code will look like this: 服务器代码如下所示:
ChannelPipeline p = ch.pipeline();
p.addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader())),
new DiscardServerHandler());
}
Server Handler will be: 服务器处理程序将是:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("channelRead"+((TestObj)msg).getCurrency());
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.