客户端/服务器应用程序中的通信管理

时间:2015-11-20 12:27:15

标签: java sockets web-applications server

让我解释一下我申请的目的,这样你就可以指导我最好的方法。

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

同样,当您通过Web界面访问时,每个客户端都能够看到他们的设备并对配置执行不同的更改。此时有两种可能的选择,这就是这篇文章的原因:

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

  • 理想的解决方案:用户只要通过网络界面保存更改并按下"发送"按钮,这些更改将发送到相应的设备。

如上所述,这些设备将定期向服务器打开套接字通信(假设每5分钟一次)以发送其配置。此时,为了实现理想的解决方案",我能想到的唯一选择就是不要关闭该套接字,以便我可以使用它立即将信息发送回设备。任何改变。

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

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

首先,我在Web服务器启动期间注册套接字服务器(在本例中为Tomcat):

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

现在socket已经创建并且活着我可以直接从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;
    }

}

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

非常感谢。

1 个答案:

答案 0 :(得分:0)

在我看来,这显然取决于系统连接的设备数量。套接字并不总是发送数据,因此它对整体性能的影响很小。虽然,Socket被认为有点慢,如果你有很多数据要发送到你的设备,你应该考虑这个。

如果您想将数据从服务器发送到客户端,那么您的解决方案很少

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

我认为没有其他解决方案,但我可能错了。如果您的设备每隔X秒请求您的服务器,它将永远不会完全按时完成。