桌面程序与服务器通信

时间:2012-10-07 10:55:09

标签: java networking

我正在将Swing程序的业务逻辑移到服务器上。

与客户端 - 服务器和服务器 - 客户端进行通信的最有效方式是什么?

服务器将负责验证,获取和存储数据,因此程序必须经常进行通信。

2 个答案:

答案 0 :(得分:2)

这取决于很多事情。如果你想要一个真正的答案,你应该明确说明你的计划将做什么,以及你的“有效”定义的确切内容

如果快速生产力属于您的高效定义,我过去使用的方法涉及序列化以将简单的旧Java对象发送到套接字。最近我发现,结合netty api,我能够快速构建相当强大的客户端/服务器通信原型。

胆量相当简单;客户端和服务器都在管道中运行带有ObjectDecoder和ObjectEncoder的Ne​​tty。为每个用于处理数据的对象创建一个类。例如,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>。当你的程序运行时,它应该遍历带有反射的类的方法并将它们放在地图中。这是一个例子:

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周围。你将不得不做更多的工作,但沟通的简单性仍然完好。

答案 1 :(得分:0)

对于哪种方法而言,哪种方法“最有效”并且我不知道您的用例有点难,但这里有几个选项:

最基本的方法是简单地使用“原始”TCP套接字。好处是,没有什么额外的移动通过网络,你自己创建协议,后者也是一个缺点;你必须设计和实现自己的通信协议,以及在服务器端处理多个连接的基本框架(如果需要的话)。

使用UDP套接字,你可能会节省一点延迟和带宽(不多,除非你使用像移动数据这样的东西,你可能不会注意到在延迟方面与TCP有任何区别),但是网络代码是一项艰巨的任务; UDP套接字是“无连接”的,这意味着所有客户端消息都将在同一个处理程序中结束,并且必须彼此区分。如果服务器需要跟上客户端状态,那么实现这一点可能有些麻烦。

MadProgrammer提出RMI(远程方法调用),我个人从未使用它,设置似乎有点麻烦,但从长远来看,在实现方面可能相当不错。 / p>

最常见的一种方法是使用http进行通信,例如通过REST-interface Web services。有多个框架(我个人更喜欢Spring MVC)来帮助实现,但是现在学习一个新框架可能超出了你的范围。此外,复杂的http查询或长网址可能会占用更多带宽,但除非我们讨论的是大量并发客户端,否则这通常不是问题(假设您在数据中心运行服务器)与100 / 100MBit连接的东西)。这可能是最简单的扩展解决方案,如果有的话,因为有很多可用于Web服务器的负载平衡解决方案。