浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志...

本文介绍一个基于websocket实现的远程实时日志系统,可以通过浏览器查看远程移动设备的实时运行日志。
系统由三个部分组成:
1. 服务器:与移动设备和浏览器建立websocket连接,将移动设备websocket上读取的实时日志转发到对应的浏览器的websocket中
2. 浏览器日志查看页面:与服务器建立websocket连接,通过websocket接收指定设备的实时运行日志并显示
3. 移动设备:与服务器建立websocket连接,将运行日志通过websocket连接上传至服务器
服务器端实现
Tomcat 7.0.27 开始支持Websocket了。本文的服务器端Servlet程序是搭建在Tomcat上的。关于在Tomcat上面实现支持websocket的servlet,可以参考
服务器Servlet源码:
importjava.io.IOException;
importjava.nio.ByteBuffer;
importjava.nio.CharBuffer;
importjava.util.Map;
importjava.util.Set;
importjava.util.concurrent.ConcurrentHashMap;
importjava.util.concurrent.CopyOnWriteArraySet;
importjavax.servlet.annotation.WebServlet;
importjavax.servlet.http.HttpServletRequest;
importorg.apache.catalina.websocket.MessageInbound;
importorg.apache.catalina.websocket.StreamInbound;
importorg.apache.catalina.websocket.WebSocketServlet;
importorg.apache.catalina.websocket.WsOutbound;
/**
* Servlet implementation class WebLogcat
*/
@WebServlet("/WebLogcat")
publicclassWebLogcatextendsWebSocketServlet {
privatestaticfinallongserialVersionUID = 1L;
privatefinalSet connections =
newCopyOnWriteArraySet();
privatefinalMap devices =
newConcurrentHashMap();
privatefinalMap> browsers =
newConcurrentHashMap>();
@Override
protectedStreamInbound createWebSocketInbound(String arg0,
HttpServletRequest arg1) {
String id = arg1.getParameter("id");
String type = arg1.getParameter("type");
if( id !=null&& type !=null) {
if( type.equalsIgnoreCase("device") ) {
returnnewDeviceMessageInbound( id );
} elseif( type.equalsIgnoreCase("browser") ) {
returnnewBrowserMessageInbound( id );
}
}
// return NULL will lead to Exception
returnnewLogMessageInbound();
}
privatefinalclassDeviceMessageInboundextendsMessageInbound {
privateString _id;
DeviceMessageInbound(String id) {
_id = id;
}
@Override
protectedvoidonClose(intstatus) {
// remove me from device hash map
devices.remove(_id);
super.onClose(status);
}
@Override
protectedvoidonOpen(WsOutbound outbound) {
// add me to device hash map
devices.put(_id, this);
super.onOpen(outbound);
}
@Override
protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {
}
@Override
protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {
// broadcast to all browser with the same id as me
String message = newString(arg0.array());
Set list = browsers.get( _id );
if( list !=null) {
for(BrowserMessageInbound connection : list) {
try{
CharBuffer buffer = CharBuffer.wrap(message);
connection.getWsOutbound().writeTextMessage(buffer);
} catch(IOException ignore) {
// Ignore
【浏览器动态显示服务器日志,基于 websocket 实现远程实时日志 在浏览器中查看设备的运行日志...】}
}
}
}
}
privatefinalclassBrowserMessageInboundextendsMessageInbound {
privateString _id;
@Override
protectedvoidonClose(intstatus) {
synchronized( browsers ) {
Set list = browsers.get( _id );
if( list !=null) {
list.remove(this);
if( list.isEmpty() ) {
browsers.remove(_id);
}
}
}
super.onClose(status);
}
@Override
protectedvoidonOpen(WsOutbound outbound) {
synchronized( browsers ) {
if( browsers.containsKey(_id) ) {
browsers.get(_id).add(this);
} else{
Set list = newCopyOnWriteArraySet();
list.add(this);
browsers.put(_id, list);
}
}
super.onOpen(outbound);
}
BrowserMessageInbound(String id) {
_id = id;
}
@Override
protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {
}
@Override
protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {
}
}
privatefinalclassLogMessageInboundextendsMessageInbound {
@Override
protectedvoidonClose(intstatus) {
connections.remove(this);
super.onClose(status);
}
@Override
protectedvoidonOpen(WsOutbound outbound) {
super.onOpen(outbound);
connections.add(this);
}
@Override
protectedvoidonBinaryMessage(ByteBuffer arg0)throwsIOException {
}
@Override
protectedvoidonTextMessage(CharBuffer arg0)throwsIOException {
}
}
}
要实现支持websocket的servlet,需要继承WebSocketServlet, 实现createWebSocketInbound函数,返回代表websocket连接的对象。
本文中,程序接收请求中传递的id和type参数,iid代表了设备的id,或者浏览器想要查看实时日志的设备的id。type代表了连接请求是来自设备还是浏览器。Servlet根据type的值创建对应类型的websocket连接。如果连接请求是来自设备device,则创建DeviceMessageInbound。如果设备请求是来自浏览器browser,则创建BrowserMessageInbound。
DeviceMessageInbound 和 BrowserMessageInbound 都继承自MessageInbound
对于DeviceMessageInbound,
在onOpen()中,将自己加入到设备Map表,
在onClose()中,将自己从设备Map表中移除。
onTextMessage()就是Servlet收到了来自设备的文本消息。Servlet根据设备id,找到所有正在监听此设备实时日志的browser,把文本消息转发给browser。
对于BrowserMessageInbound,
在onOpen()中,要将自己加入到监听对于id的browser列表中。由于针对同一个设备id,允许多个浏览器同时查看其实时日志。所以要先判断是否已经存在对应id的浏览器连接。如果不存在,则创建一个列表,并将此列表插入到browsers这个Map中。如果已经存在,则根据id找到列表,将自己加入到此列表中。
在onClose中,通过id找到列表,将自己从列表中移除。移除后如果列表为空,在将列表从browsers Map中移除。
浏览器端实现
html>
WebLogcat input#chat {
width: 410px
}
#console-container {
width: 400px;
}
#console {
border: 1px solid #CCCCCC;
border-right-color: #999999;
border-bottom-color: #999999;
height: 170px;
overflow-y: scroll;
padding: 5px;
width: 100%;
}
#console p {
padding: 0;
margin: 0;
}
var Chat= {};
Chat.socket=null;
Chat.connect= (function(host) {
if ('WebSocket' in window) {
Chat.socket=newWebSocket(host);
} else if ('MozWebSocket' in window) {
Chat.socket=newMozWebSocket(host);
} else {
Console.log('Error: WebSocket is not supported by this browser.');
return;
}
Chat.socket.onopen=function() {
Console.log('Info: WebSocket connection opened.');
document.getElementById('chat').οnkeydοwn=function(event) {
if (event.keyCode== 13) {
Chat.sendMessage();
}
};
};
Chat.socket.onclose=function() {
document.getElementById('chat').οnkeydοwn=null;
Console.log('Info: WebSocket closed.');
};
Chat.socket.onmessage=function(message) {
Console.log(message.data);
};
});
Chat.initialize=function() {
if (window.location.protocol== 'http:') {
Chat.connect('ws://' + window.location.host + '/WebLogcat/WebLogcat?id=fv0557&type=browser');
} else {
Chat.connect('wss://' + window.location.host + '/WebLogcat/WebLogcat?id=fv0557&type=browser');
}
};
Chat.sendMessage= (function() {
var message=document.getElementById('chat').value;
if (message != '') {
Chat.socket.send(message);
document.getElementById('chat').value='';
}
});
var Console= {};
Console.log= (function(message) {
var console=document.getElementById('console');
var p=document.createElement('p');
p.style.wordWrap='break-word';
p.innerHTML=message;
console.appendChild(p);
while (console.childNodes.length >25) {
console.removeChild(console.firstChild);
}
console.scrollTop=console.scrollHeight;
});
Chat.initialize();

Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable Javascript and reload this page!

    推荐阅读