【问题标题】:Communication management in client/server application客户端/服务器应用程序中的通信管理
【发布时间】:2015-11-20 12:27:15
【问题描述】:

让我解释一下我的应用程序的目的,以便您指导我了解最佳方法。

这个想法是构建一个 Web 应用程序来远程管理我公司制造的一些特定设备。这些设备会定期连接到远程服务器以发送/接收某些数据(通过简单的套接字通信,但它们不使用Java);这些数据将存储在相应的数据库中,并可通过网络应用程序供不同用户使用。

同理,当您通过网页界面访问时,每个客户端都可以看到自己的设备,并对配置进行不同的更改。此时有两种可能的选择,这就是这篇文章的原因:

  • 最简单但不是最好的选择:用户执行一些更改,我将这些更改保存在数据库中。当设备稍后与服务器建立通信时,它将读取这些更改并更新其配置。

  • 理想的解决方案:一旦用户通过网络界面保存更改并按下“发送”按钮,这些更改就会发送到相应的设备。

如上所述,这些设备会定期向服务器打开一个套接字通信(假设每 5 分钟一次)以发送它们的配置。此时,为了实现“理想方案”,我能想到的唯一选择就是不关闭那个socket,这样当某个用户做出任何改变时,我可以用它立即将信息发送回设备。

如果这个应用程序随着时间的推移而增长,我担心打开的套接字/线程过多会使我的应用程序崩溃。

让我用一些我正在玩的代码来说明。我知道这远不是最终的解决方案,它只是为了帮助您了解我在寻找什么。

首先,我在web服务器(本例中为Tomcat)启动时注册了socket服务器:

package org.listeners;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.sockets.KKMultiServer;

public class ApplicationListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent event) {
        KKMultiServer kKMultiServer = new KKMultiServer();
        Thread serverThread = new Thread(kKMultiServer);
        serverThread.start();
        event.getServletContext().setAttribute("PlainKKMultiServer", kKMultiServer);
    }

    public void contextDestroyed(ServletContextEvent event) { }

}

这是侦听新连接的主要套接字服务器类:

public class KKMultiServer implements Runnable {
    private Map<Long, KKMultiServerThread_v2> createdThreads = new HashMap<Long, KKMultiServerThread_v2>();

    @Override
    public void run() {
        boolean listening = true;
        try (ServerSocket serverSocket = new ServerSocket(5000)) {
            while (listening) {
                KKMultiServerThread_v2 newServerThread = new KKMultiServerThread_v2(serverSocket.accept(), this);
                Thread myThread = new Thread(newServerThread);
                myThread.start();
                Long threadId = myThread.getId();
                System.out.println("THREAD ID: " + threadId);
            }
        } catch (IOException e) {
            System.err.println("Could not listen on port " + 5000);
            System.exit(-1);
        }
    }

    public Map<Long, KKMultiServerThread_v2> getCreatedThreads() {
        return createdThreads;
    }
}

并且使用来自每个设备(分配器)的每个请求创建的线程类来处理套接字通信:

public class KKMultiServerThread_v2 implements Runnable {
    private Socket socket = null;
    PrintWriter out = null;
    BufferedReader in = null;
    private long dispenserCode;
    private KKMultiServer kKMultiServer;

    public KKMultiServerThread_v2(Socket socket, KKMultiServer kKMultiServer) {
        this.socket = socket;
        this.kKMultiServer = kKMultiServer;
    }

    public void run() {
        try {
            out = new PrintWriter(socket.getOutputStream(), true);
            in = new BufferedReader(
                new InputStreamReader(
                    socket.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        readDataFromDispenser();
    }

    private void readDataFromDispenser() {
        String inputLine;
        try {
            while ((inputLine = in.readLine()) != null) {
                if (inputLine.equals("Bye")) {
                    break;
                }
                if (dispenserCode == 0) {
                    dispenserCode = 1111; // this code will be unique per equipment
                    this.kKMultiServer.getCreatedThreads().put(dispenserCode, this);
                }
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendDataToDispenser(String dataToSend) {
        if (!socket.isClosed() && socket.isConnected()) {
            out.println(dataToSend);
        } else {
            this.kKMultiServer.getCreatedThreads().remove(this);
        }
    }
}

现在套接字已创建并处于活动状态,我可以直接从 Web 应用程序使用它来将消息发送回设备(本例中为 Struts Action)

public class HelloWorldAction extends ActionSupport {

    private static final long serialVersionUID = 1L;

    public String sendMessageToDispenser() throws Exception {
        ServletContext context = ServletActionContext.getServletContext();
        KKMultiServer kKMultiServer = (KKMultiServer) context.getAttribute("PlainKKMultiServer");
        Map<Long, KKMultiServerThread_v2> currentThreads = kKMultiServer.getCreatedThreads();
        Iterator<Long> it = currentThreads.keySet().iterator();
        while (it.hasNext()) {
            Long key = (Long) it.next();
            KKMultiServerThread_v2 currentThread = currentThreads.get(key);
            currentThread.sendDataToDispenser("DATA TO YOU!");
        }
        return SUCCESS;
    }

}

您认为可以执行此解决方案吗?我的意思是,保持这些连接打开,以便我可以在必要时访问我的设备(无需等待定期连接)。最好的方法是什么?如果您有任何其他建议,请告诉我。

非常感谢。

【问题讨论】:

    标签: java sockets web-applications server


    【解决方案1】:

    在我看来,这显然取决于将有多少设备连接到您的系统。套接字并不总是发送数据,因此它对整体性能的影响很小。虽然,众所周知 Socket 有点慢,但如果您有很多数据要发送到您的设备/从您的设备发送,您应该考虑这一点。

    如果你想从你的服务器向你的客户端发送数据,你有几个解决方案

    • 例如,您的服务器在注册后知道您的所有设备。启动设备时,连接到服务器。 (注意本地网络重定向)
    • 您的设备和服务器使用套接字进行通信

    我认为没有其他解决方案,但我可能是错的。如果您的设备每 X 秒请求一次您的服务器,那么它永远不会完全准时。

    【讨论】:

    • 感谢您的回复@RPresle。发送/接收的数据量不会太多,但连接的设备数量可能很大,大约有几千个。正如您还提到的,这是我能想到的从服务器发送数据的唯一 2 个选项:第一个是不可能的,因为我无法控制客户端的内部网络参数,所以我必须使用套接字进行通信。我想我必须进行一些测试,但我想提前有一个简短的想法,以了解这是否可能,或者这太疯狂了,我不得不忘记它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-20
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    相关资源
    最近更新 更多