如何实现后台向前台传数据
技術交流群:233513714
?
?
?
這兩天正在研究如何讓后天主動向前臺展現數據,只要后臺有數據上傳的時候就向前臺上傳(因為公司有個項目,硬件設備會不斷的上傳數據,服務端將接收到的數據向前臺展示)。在網上查了一下,下面將介紹一下其中的兩種解決辦法
一、WebSocket
WebSocket?是web客戶端和服務器之間新的通訊方式, 依然架構在HTTP協議之上。使用WebSocket連接, web應用程序可以執行實時的交互, 而不是以前的poll方式。
WebSocket是HTML5開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議,可以用來創建快速的更大規模的健壯的高性能實時的web應用程序。WebSocket通信協議于2011年被IETF定為標準RFC 6455,WebSocketAPI被W3C定為標準。
在WebSocket API中,瀏覽器和服務器只需要做一個握手的動作,然后,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。
什么是WebSocket?
一個WebSocket是通過一個獨立的TCP連接實現的、異步的、雙向的、全雙工的消息傳遞實現機制。WebSockets不是一個HTTP連接,卻使用HTTP來引導一個WebSocket連接。一個全雙工的系統允許同時進行雙向的通訊。陸地線路電話是一個全雙工設施的例子,因為它們允許兩個通話者同時講話并被對方聽到。最初WebSocket被提議作為HTML5規范的一部分,HTML5承諾給現代的交互式的web應用帶來開發上的便利和網絡效率,但是隨后WebSocket被移到一個僅用來存放WebSockets規范的獨立的標準文檔里。它包含兩件事情 -- WebSocket協議規范,即2011年12月發布的RFC 6455,和WebSocket JavaScript API。
WebSocket協議利用HTTP 升級頭信息來把一個HTTP連接升級為一個WebSocket連接。HTML5 WebSockets 解決了許多導致HTTP不適合于實時應用的問題,并且它通過避免復雜的工作方式使得應用結構很簡單。
最新的瀏覽器都支持WebSockets,
WebSocket是如何工作的?
每一個WebSocket連接的生命都是從一個HTTP請求開始的。HTTP請求跟其他請求很類似,除了它擁有一個Upgrade頭信息。Upgrade頭信息表示一個客戶端希望把連接升級為不同的協議。對WebSockets來說,它希望升級為WebSocket協議。當客戶端和服務器通過底層連接第一次握手時,WebSocket連接通過把HTTP協議轉換升級為WebSockets協議而得以建立。一旦WebSocket連接成功建立,消息就可以在客戶端和服務器之間進行雙向發送
- WebSockets比其它工作方式比如輪詢更有效也更高效。因為它需要更少的帶寬并且降低了延時。
- WebSockets簡化了實時應用的結構體系。
- WebSockets在點到點發送消息時不需要頭信息。這顯著的降低了帶寬。
一些可能的WebSockets使用案例有:
- 聊天應用
- 多人游戲
- 股票交易和金融應用
- 文檔合作編輯
- 社交應用
JSR 356,WebSocket的Java API,規定了開發者把WebSockets 整合進他們的應用時可以使用的Java API — 包括服務器端和Java客戶端。JSR 356是Java EE 7標準中的一部分。這意味著所有Java EE 7兼容的應用服務器都將有一個遵守JSR 356標準的WebSocket協議的實現。開發者也可以在Java EE 7應用服務器之外使用JSR 356。目前Apache Tomcat 8提供了JSR 356 API的WebSocket支持。 Jboss Wildfly 8 (原JBoss Application Server)也支持JSR 356.
一個Java客戶端可以使用兼容JSR 356的客戶端實現,來連接到WebSocket服務器。對web客戶端來說,開發者可以使用WebSocket JavaScript API來和WebSocket服務器進行通訊。WebSocket客戶端和WebSocket服務器之間的區別,僅在于兩者之間是通過什么方式連接起來的。一個WebSocket客戶端是一個WebSocket終端,它初始化了一個到對方的連接。一個WebSocket服務器也是一個WebSocket終端,它被發布出去并且等待來自對方的連接。在客戶端和服務器端都有回調監聽方法 -- onOpen , onMessage , onError, onClose。
怎么創建你的第一個WebSocket應用呢?基本上我們還是會使用Javascript API編寫WebSocket客戶端, 在服務器端, 本文使用JSR 356規范定義的通用模式和技術處理WebSocket的通訊。
下面看一個簡單的例子, 演示了如果使用JavaScript WebSocket客戶端與運行在Wildfly 8服務器通信.
客戶端代碼
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <html> <head> <meta?http-equiv="content-type"content="text/html; charset=ISO-8859-1"> </head> <body> <meta?charset="utf-8"> <title>HelloWorld Web sockets</title> <script?language="javascript"type="text/javascript"> var wsUri = getRootUri() +?"/websocket-hello/hello"; function?getRootUri() { return?"ws://" + (document.location.hostname ==?"" ??"localhost" :document.location.hostname) +?":" + (document.location.port ==?"" ??"8080" :document.location.port); } function?init() { output =?document.getElementById("output"); } function?send_message() { websocket =?new WebSocket(wsUri); websocket.onopen =?function(evt) { onOpen(evt) }; websocket.onmessage =?function(evt) { onMessage(evt) }; websocket.onerror =?function(evt) { onError(evt) }; } function?onOpen(evt) { writeToScreen("Connected to Endpoint!"); doSend(textID.value); } function?onMessage(evt) { writeToScreen("Message Received: " + evt.data); } function?onError(evt) { writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); } function?doSend(message) { writeToScreen("Message Sent: " + message); websocket.send(message); } function?writeToScreen(message) { var pre =?document.createElement("p"); pre.style.wordWrap =?"break-word"; pre.innerHTML = message; output.appendChild(pre); } window.addEventListener("load", init,?false); </script> <h1?style="text-align: center;">Hello World WebSocket Client</h2> <br> <div?style="text-align: center;"> <form?action=""> <input?οnclick="send_message()"?value="Send"type="button"> <input?id="textID"?name="message"value="Hello WebSocket!"?type="text"><br> </form> </div> <div?id="output"></div> </body> </html> |
如你所見,要想使用WebSocket協議與服務器通信, 需要一個WebSocket對象。它會自動連接服務器.
| 1 | websocket =?new WebSocket(wsUri); |
連接上會觸發open事件:
| 1 2 3 | websocket.onopen =?function(evt) { onOpen(evt) }; |
一旦連接成功,則向服務器發送一個簡單的hello消息。
| 1 | websocket.send(message); |
服務器端代碼
有兩種創建服務器端代碼的方法:
- 注解方式Annotation-driven: 通過在POJO加上注解, 開發者就可以處理WebSocket 生命周期事件.
- 實現接口方式Interface-driven: 開發者可以實現Endpoint接口和聲明周期的各個方法.
建議開發時采用注解方式, 這樣可以使用POJO就可以實現WebSocket Endpoint. 而且不限定處理事件的方法名。 代碼也更簡單。
本例就采用注解的方式, 接收WebSocket請求的類是一個POJO, 通過@ServerEndpoint標注. 這個注解告訴容器此類應該被當作一個WebSocket的Endpoint。value值就是WebSocket endpoint的path.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package com.sample.websocket; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/hello") public?class?HelloWorldEndpoint { @OnMessage public String?hello(String message) { System.out.println("Received : "+ message); return message; } @OnOpen public?void?myOnOpen(Session session) { System.out.println("WebSocket opened: " + session.getId()); } @OnClose public?void?myOnClose(CloseReason reason) { System.out.println("Closing a WebSocket due to " + reason.getReasonPhrase()); } } |
注意:這個例子還包括了其它兩個回調函數: @OnOpen標注的方法在WebSocket連接開始時被調用, Web Session作為參數。 另外一個@OnClose標注的方法在連接關閉時被調用。
就是這么簡單。但是為了編譯這個例子你還需要Websockets API的實現,它在WildFly 8發布中(或者你用JSR 356的參考實現,或其它的容器提供的jar, 如tomcat):
| 1 | modules\system\layers\base\javax\websocket\api\main\jboss-websocket-api_1.0_spec-1.0.0.Final.jar |
對于Maven用戶, 你需要增加undertow-websockets-jsr依賴:
| 1 2 3 4 5 | <dependency> <groupId>org.jboss.spec.javax.websocket</groupId> <artifactId>jboss-websocket-api_1.0_spec</artifactId> <version>1.0.0.Final</version> </dependency> |
這個例子比較早,應該是2013年寫的,jsr 256還未發布。 現在,你應該直接使用Java EE提供的API
| 1 2 3 4 5 | <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1.1</version> </dependency> |
編解碼器
前面的例子中WebSocket通信的消息類型默認為String。接下來的例子演示如何使用Encoder和Decoder傳輸更復雜的數據。
Websocket使用Decoder將文本消息轉換成Java對象,然后傳給@OnMessage方法處理; 而當對象寫入到session中時,Websocket將使用Encoder將Java對象轉換成文本,再發送給客戶端。
更常用的, 我們使用XML 或者 JSON 來傳送數據,所以將會會將Java對象與XML/JSON數據相互轉換.
下圖描繪了客戶端和服務器使用encoder/decoder標準通信過程。
聲明Encoder/Decoder也是相當的簡單: 你只需在@ServerEndpoint注解中增加encoder/decoder設置:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package com.sample.websocket; import java.util.logging.Level; import java.util.logging.Logger; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; @ServerEndpoint(value =?"/hello", decoders = { MessageDecoder.class,}, encoders = { MessageEncoder.class }) public?class?HelloWorldEndpoint { @OnMessage public Person?hello(Person person, Session session) { if (person.getName().equals("john")) { person.setName("Mr. John"); } try { session.getBasicRemote().sendObject(person); System.out.println("sent "); }?catch (Exception ex) { Logger.getLogger(HelloWorldEndpoint.class.getName()).log(Level.SEVERE,?null, ex); } return person; } @OnOpen public?void?myOnOpen(Session session) { } } |
正像你看到的, OnMessage方法使用Java Object person作為參數, 我們為名字增加個尊稱再返回給客戶端。通過session.getBasicRemote().sendObject(Object obj)返回數據.
容器負責使用你指定的Decoder將接收到的XML消息轉為Java對象:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | package com.sample.websocket; import java.io.StringReader; import javax.websocket.Decoder; import javax.websocket.EndpointConfig; import javax.xml.bind.*; public?class?MessageDecoder?implementsDecoder.Text<Person> { @Override public Person?decode(String s) { System.out.println("Incoming XML " + s); Person person =?null; JAXBContext jaxbContext; try { jaxbContext = JAXBContext.newInstance(Person.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); StringReader reader =?new StringReader(s); person = (Person) unmarshaller.unmarshal(reader); }?catch (Exception ex) { ex.printStackTrace(); } return person; } @Override public?boolean?willDecode(String s) { return (s !=?null); } @Override public?void?init(EndpointConfig endpointConfig) { // do nothing. } @Override public?void?destroy() { // do nothing. } } |
這里我們使用JAXB做轉換。我們只要實現一個泛型接口Decoder.Text?或者 Decoder.Binary, 根據你傳輸的數據是文本還是二進制選擇一個.
所以數據由Decoder解碼, 傳給Endpoint (這里的 HelloWorldEndpoint), 在返回給client之前, 它還會被下面的Encoder轉換成XML:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package com.sample.websocket; import java.io.StringWriter; import javax.websocket.EncodeException; import javax.websocket.Encoder; import javax.websocket.EndpointConfig; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; public?class?MessageEncoder?implementsEncoder.Text<Person> { @Override public String?encode(Person object)?throws EncodeException { JAXBContext jaxbContext =?null; StringWriter st =?null; try { jaxbContext = JAXBContext.newInstance(Person.class); Marshaller marshaller = jaxbContext.createMarshaller(); st =?new StringWriter(); marshaller.marshal(object, st); System.out.println("OutGoing XML " + st.toString()); }?catch (Exception ex) { ex.printStackTrace(); } return st.toString(); } @Override public?void?init(EndpointConfig endpointConfig) { // do nothing. } @Override public?void?destroy() { // do nothing. } } |
為了測試這個例子,將客戶端的網頁稍微修改一下以便能在textarea中粘帖XML:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <html> <head> <meta?http-equiv="content-type"content="text/html; charset=ISO-8859-1"> </head> <body> <meta?charset="utf-8"> <title>HelloWorld Web sockets</title> <script?language="javascript"type="text/javascript"> var wsUri = getRootUri() +?"/websocket-hello/hello"; function?getRootUri() { return?"ws://" + (document.location.hostname ==?"" ??"localhost" :document.location.hostname) +?":" + (document.location.port ==?"" ??"8080" :document.location.port); } function?init() { output =?document.getElementById("output"); } function?send_message() { websocket =?new WebSocket(wsUri); websocket.onopen =?function(evt) { onOpen(evt) }; websocket.onmessage =?function(evt) { onMessage(evt) }; websocket.onerror =?function(evt) { onError(evt) }; } function?onOpen(evt) { writeToScreen("Connected to Endpoint!"); doSend(textID.value); } function?onMessage(evt) { writeToScreen("Message Received: " + evt.data); } function?onError(evt) { writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); } function?doSend(message) { writeToScreen("Message Sent: " + message); websocket.send(message); } function?writeToScreen(message) { alert(message); } window.addEventListener("load", init,?false); </script> <h1?style="text-align: center;">Hello World WebSocket Client</h2> <br> <div?style="text-align: center;"> <form?action=""> <input?οnclick="send_message()"?value="Send"type="button"> <textarea?id="textID"?rows="4"?cols="50"name="message" > </textarea> </form> </div> <div?id="output"></div> </body> </html> |
在文本框中輸入下面的XML進行測試。
| 1 2 3 4 | <person> <name>john</name> <surname>smith</surname> </person> |
$(document).ready(function() {
setInterval(checkIsExist, 1000);
});
function checkIsExist() {
var urls = "/LogForPage/getShiftCarOrderCarId.do?ajax=1";
var htmlobj = $.ajax({
url : urls,
async : false
});
var list = eval(htmlobj.responseText);
$("#textarea-input").html(list);
}
?
后臺代碼:
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.aotoso.control.ConnectionManage.PortCommunication;
import com.aotoso.control.base.BaseController;
import com.aotoso.server.FirstDataManage.EverydayPlanService;
@Controller
@RequestMapping("/LogForPage")
public class LogForPage extends BaseController {
@Resource(name = "everydayPlanService")
EverydayPlanService everydayPlanService;
/**
* 時時打印上傳的信息!
* @param session
* @param request
* @param response
*/
@RequestMapping(value = "/TMRInfromation")
public void getShiftCarOrderCarId(HttpServletResponse response) {
pd = this.getPageData();
logger.info("時時打印上傳的信息!");
ObjectMapper objectMapper = new ObjectMapper();
try {
objectMapper.writeValue(response.getOutputStream(),new PortCommunication().getInfo());
System.out.println(new PortCommunication().getInfo());
} catch (Exception e) {
e.printStackTrace();
}
}
}
轉載于:https://www.cnblogs.com/cnndevelop/p/6034415.html
總結
以上是生活随笔為你收集整理的如何实现后台向前台传数据的全部內容,希望文章能夠幫你解決所遇到的問題。