【发布时间】: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