我正在将 Swing 程序的业务逻辑转移到服务器上。
客户端-服务器和服务器-客户端通信的最有效方式是什么?
服务器将负责身份验证、获取和存储数据,因此程序必须经常通信。
我正在将 Swing 程序的业务逻辑转移到服务器上。
客户端-服务器和服务器-客户端通信的最有效方式是什么?
服务器将负责身份验证、获取和存储数据,因此程序必须经常通信。
这取决于很多事情。如果您想要一个真正的答案,您应该明确说明您的程序将做什么以及您对“高效”的定义到底是什么
如果快速生产力属于您对高效的定义,那么我过去使用的一种方法涉及序列化以将普通的旧 java 对象发送到套接字。最近我发现,结合netty api,我能够快速原型化相当健壮的客户端/服务器通信。
胆量相当简单;客户端和服务器都在管道中运行带有 ObjectDecoder 和 ObjectEncoder 的 Netty。为每个设计用于处理数据的对象创建一个类。例如,HandshakeRequest 类和 HandshakeResponse 类。
握手请求可能如下所示:
public class HandshakeRequest extends Message {
private static final long serialVersionUID = 1L;
}
握手响应可能如下所示:
public class HandshakeResponse extends Message {
private static final long serialVersionUID = 1L;
private final HandshakeResult handshakeResult;
public HandshakeResponse(HandshakeResult handshakeResult) {
this.handshakeResult = handshakeResult;
}
public HandshakeResult getHandshakeResult() {
return handshakeResult;
}
}
在 netty 中,当客户端连接时,服务器会发送一个握手请求:
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
Channel ch = e.getChannel();
ch.write(new HandshakeRequest();
}
客户端接收到 HandshakeRequest 对象,但它需要一种方法来告诉服务器刚刚发送了什么样的消息。为此,Map<Class<?>, Method>
可以使用 a。当您的程序运行时,它应该通过反射遍历类的方法并将它们放置在映射中。这是一个例子:
public HashMap<Class<?>, Method> populateMessageHandler() {
HashMap<Class<?>, Method> temp = new HashMap<Class<?>, Method>();
for (Method method : getClass().getMethods()) {
if (method.getAnnotation(MessageHandler.class) != null) {
Class<?>[] methodParameters = method.getParameterTypes();
temp.put(methodParameters[1], method);
}
}
return temp;
}
此代码将遍历当前类并查找标有 @MessageHandler 注释的方法,然后查看该方法的第一个参数(该参数是一个对象,例如public void handleHandshakeRequest(HandshakeRequest request)
)并将该类作为键放入映射中方法,因为它的价值。
有了这个映射,很容易接收消息并将消息直接发送到应该处理消息的方法:
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
try {
Message message = (Message) e.getMessage();
Method method = messageHandlers.get(message.getClass());
if (method == null) {
System.out.println("No handler for message!");
} else {
method.invoke(this, ctx, message);
}
} catch(Exception exception) {
exception.printStackTrace();
}
}
真的什么都没有了。netty 处理所有杂乱的东西,使我们可以轻松地来回发送序列化对象。如果您决定不想使用 netty,您可以将自己的协议包装在 java 的Object Output Stream周围。总体而言,您将不得不做更多的工作,但沟通的简单性保持不变。
很难说哪种方法在什么方面“最有效”,而且我不知道您的用例,但这里有几个选项:
最基本的方法是简单地使用“原始”TCP 套接字。好处是网络上没有任何额外的移动,您可以自己创建协议,后者也是一个缺点;您必须设计和实现自己的通信协议,以及在服务器端处理多个连接的基本框架(如果需要的话)。
使用 UDP 套接字,您可能会节省一点延迟和带宽(不多,除非您使用移动数据之类的东西,否则您可能不会注意到 TCP 在延迟方面有任何区别),但网络代码是有点困难的任务;UDP 套接字是“无连接的”,这意味着所有客户端消息都将在同一个处理程序中结束,并且必须相互区分。如果服务器需要跟上客户端状态,这可能有点难以实现。
MadProgrammer 提出了RMI(远程方法调用),我个人没用过,设置起来似乎有点麻烦,但从实现上看长远来看可能还是不错的。
可能最常见的方式之一是使用 http 进行通信,例如通过Web服务的 REST 接口。有多个框架(我个人更喜欢Spring MVC)来帮助实现,但是现在学习一个新框架可能超出了你的范围。此外,复杂的 http 查询或长 url 可能会更多地占用您的带宽,但除非我们谈论大量同时存在的客户端,否则这通常不是问题(假设您在数据中心运行服务器) 100/100MBit 连接)。这可能是最容易扩展的解决方案,如果涉及到的话,因为有很多负载平衡解决方案可用于 Web 服务器。