有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

与服务器通信的java桌面程序

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

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

服务器将负责身份验证、获取和存储数据,因此程序必须频繁通信


共 (2) 个答案

  1. # 1 楼答案

    这取决于很多事情。如果你想得到一个真实的答案,你应该明确你的程序将要做什么,以及你对“高效”的定义

    如果快速生产力属于高效的定义,那么我在过去使用的一种方法涉及序列化,将普通的旧java对象发送到套接字。最近,我发现,结合nettyapi,我能够快速原型化相当健壮的客户机/服务器通信

    勇气相当简单;客户机和服务器都运行Netty,并在管道中使用ObjectDecoder和ObjectEncoder。为每个设计用于处理数据的对象创建一个类。例如,握手请求类和握手响应类

    握手请求可能如下所示:

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

    客户端接收到握手请求对象,但它需要一种方法来告诉服务器刚刚发送了什么类型的消息。为此,可以使用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包装自己的协议。总的来说,你需要做更多的工作,但沟通的简单性仍然保持不变

  2. # 2 楼答案

    很难说哪种方法在哪些方面“最有效”,我也不知道您的使用案例,但这里有几个选项:

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

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

    MadProgrammer提出了RMI(远程方法调用),我个人从未使用过它,设置起来似乎有点麻烦,但从长远来看,在实现方面可能会非常好

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