實時消息的推送,PC端的推送技術可以使用socket建立一個長連接來實現。傳統的web服務都是客戶端發出請求,服務端給出響應。但是現在直觀的要求是允許特定時間內在沒有客戶端發起請求的情況下服務端主動推送消息到客戶端。
有哪些可以實現web消息推送的技術:
-
不斷地輪詢(俗稱“拉”,polling)是獲取實時消息的一個手段:Ajax 隔一段時間(通常使用 JavaScript 的 setTimeout 函數)就去服務器查詢是否有改變,從而進行增量式的更新。但是間隔多長時間去查詢成了問題,因爲性能和即時性造成了嚴重的反比關係。間隔太短,連續不斷的請求會沖垮服務器,間隔太長,務器上的新數據就需要越多的時間才能到達客戶機。
-
優點:服務端邏輯簡單;
-
缺點:其中大多數請求可能是無效請求,在大量用戶輪詢很頻繁的情況下對服務器的壓力很大;
-
應用:併發用戶量少,而且要求消息的實時性不高,一般很少採用;
-
-
長輪詢技術(long-polling):客戶端向服務器發送Ajax請求,服務器接到請求後hold住連接,直到有新消息或超時(設置)才返回響應信息並關閉連接,客戶端處理完響應信息後再向服務器發送新的請求。
-
優點:實時性高,無消息的情況下不會進行頻繁的請求;
-
缺點:服務器維持着連接期間會消耗資源;
-
-
基於Iframe及htmlfile的流(streaming)方式:iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間創建一條長鏈接,服務器向iframe傳輸數據(通常是HTML,內有負責插入信息的javascript),來實時更新頁面。
-
優點:消息能夠實時到達;
-
缺點:服務器維持着長連接期會消耗資源;
-
-
插件提供socket方式:比如利用Flash XMLSocket,Java Applet套接口,Activex包裝的socket。
-
優點:原生socket的支持,和PC端和移動端的實現方式相似;
-
缺點:瀏覽器端需要裝相應的插件;
-
-
WebSocket:是HTML5開始提供的一種瀏覽器與服務器間進行全雙工通訊的網絡技術。
-
優點:更好的節省服務器資源和帶寬並達到實時通訊;
-
缺點:目前還未普及,瀏覽器支持不好;
-
綜上,考慮到瀏覽器兼容性和性能問題,採用長輪詢(long-polling)是一種比較好的方式。
netty-socketio是一個開源的Socket.io服務器端的一個java的實現, 它基於Netty框架。 項目地址爲: https://github.com/mrniko/netty-socketio
以下是一個來自netty-socketio的推送示例,web聊天系統。
服務器端push server:
消息實體:
package com.nettysocketio.test;
public class ChatObject {
private String userName;
private String message;
public ChatObject() {
}
public ChatObject(String userName, String message) {
super();
this.userName = userName;
this.message = message;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
監聽事件:
package com.nettysocketio.test;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.DataListener;
public class CharteventListener implements DataListener<ChatObject> {
SocketIOServer server;
public void setServer(SocketIOServer server) {
this.server = server;
}
public void onData(SocketIOClient client, ChatObject data,
AckRequest ackSender) throws Exception {
// chatevent爲 事件的名稱, data爲發送的內容
this.server.getBroadcastOperations().sendEvent("chatevent", data);
}
}
推送服務:
package com.nettysocketio.test;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOServer;
public class App {
public static void main(String[] args) throws InterruptedException
{
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(9092);
SocketIOServer server = new SocketIOServer(config);
CharteventListener listner = new CharteventListener();
listner. setServer(server);
// chatevent爲事件名稱
server.addEventListener("chatevent", ChatObject.class, listner);
//啓動服務
server.start();
Thread.sleep(Integer.MAX_VALUE) ;
server.stop();
}
}
瀏覽器端Client:客戶端使用的是socket.io-client,項目地址爲:https://github.com/Automattic/socket.io-client
客戶端聊天代碼:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Socketio chat</title>
<script src="./jquery-1.7.2.min.js" type="text/javascript"></script>
<script type="text/javascript" src="./socket.io/socket.io.js"></script>
<style>
body {
padding: 20px;
}
#console {
height: 400px;
overflow: auto;
}
.username-msg {
color: orange;
}
.connect-msg {
color: green;
}
.disconnect-msg {
color: red;
}
.send-msg {
color: #888
}
</style>
</head>
<body>
<h1>Netty-socketio chat demo</h1>
<br />
<div id="console" class="well"></div>
<form class="well form-inline" onsubmit="return false;">
<input id="name" class="input-xlarge" type="text" placeholder="用戶名稱. . . " />
<input id="msg" class="input-xlarge" type="text" placeholder="發送內容. . . " />
<button type="button" onClick="sendMessage()" class="btn">Send</button>
<button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
</form>
</body>
<script type="text/javascript">
var socket = io.connect('http://localhost:9092');
socket.on('connect',function() {
output('<span class="connect-msg">Client has connected to the server!</span>');
});
socket.on('chatevent', function(data) {
output('<span class="username-msg">' + data.userName + ' : </span>'
+ data.message);
});
socket.on('disconnect',function() {
output('<span class="disconnect-msg">The client has disconnected! </span>');
});
function sendDisconnect() {
socket.disconnect();
}
function sendMessage() {
var userName = $("#name").val()
var message = $('#msg').val();
$('#msg').val('');
socket.emit('chatevent', {
userName : userName,
message : message
});
}
function output(message) {
var currentTime = "<span class='time' >" + new Date() + "</span>";
var element = $("<div>" + currentTime + " " + message + "</div>");
$('#console').prepend(element);
}
</script>
</html>
先運行push server,再打開chat html就可以看到連接信息和服務器推送的聊天信息。
參考:
https://github.com/mrniko/netty-socketio
https://github.com/mrniko/netty-socketio-demo