웹 소켓 기반 원격 실시간 로그 브라우저에서 장치 실행 로그 보기
시스템은 다음과 같은 세 가지 섹션으로 구성됩니다.
1. 서버: 모바일 기기와 브라우저와 웹소켓 연결을 구축하여 모바일 기기 웹소켓에서 읽은 실시간 로그를 해당 브라우저의 웹소켓에 전송
2. 브라우저 로그 보기 페이지: 서버와 웹소켓 연결을 맺고 웹소켓을 통해 지정된 장치의 실시간 실행 로그를 수신하여 표시합니다.
3. 모바일 장치: 서버와 웹소켓 연결을 만들고 실행 로그를 웹소켓 연결을 통해 서버에 업로드
서버 측 구현
Tomcat 7.0.27은 웹소켓을 지원하기 시작했습니다.본고의 서버 사이드 서브렛 프로그램은 Tomcat에 구축된 것이다.Tomcat 위에서 웹소켓을 지원하는 servlet에 대해서는 참고할 수 있습니다
Tomcat Websocket How-To
Tomcat 기반 웹소켓
서버 서브렛 소스:
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import org.apache.catalina.websocket.WsOutbound;
/**
* Servlet implementation class WebLogcat
*/
@WebServlet("/WebLogcat")
public class WebLogcat extends WebSocketServlet {
private static final long serialVersionUID = 1L;
private final Set connections =
new CopyOnWriteArraySet();
private final Map devices =
new ConcurrentHashMap();
private final Map browsers =
new ConcurrentHashMap();
@Override
protected StreamInbound createWebSocketInbound(String arg0,
HttpServletRequest arg1) {
String id = arg1.getParameter("id");
String type = arg1.getParameter("type");
if( id != null && type != null ) {
if( type.equalsIgnoreCase("device") ) {
return new DeviceMessageInbound( id );
} else if( type.equalsIgnoreCase("browser") ) {
return new BrowserMessageInbound( id );
}
}
// return NULL will lead to Exception
return new LogMessageInbound();
}
private final class DeviceMessageInbound extends MessageInbound {
private String _id;
DeviceMessageInbound(String id) {
_id = id;
}
@Override
protected void onClose(int status) {
// remove me from device hash map
devices.remove(_id);
super.onClose(status);
}
@Override
protected void onOpen(WsOutbound outbound) {
// add me to device hash map
devices.put(_id, this);
super.onOpen(outbound);
}
@Override
protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
}
@Override
protected void onTextMessage(CharBuffer arg0) throws IOException {
// broadcast to all browser with the same id as me
String message = new String(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
}
}
}
}
}
private final class BrowserMessageInbound extends MessageInbound {
private String _id;
@Override
protected void onClose(int status) {
synchronized( browsers ) {
Set list = browsers.get( _id );
if( list != null ) {
list.remove(this);
if( list.isEmpty() ) {
browsers.remove(_id);
}
}
}
super.onClose(status);
}
@Override
protected void onOpen(WsOutbound outbound) {
synchronized( browsers ) {
if( browsers.containsKey(_id) ) {
browsers.get(_id).add(this);
} else {
Set list = new CopyOnWriteArraySet();
list.add(this);
browsers.put(_id, list);
}
}
super.onOpen(outbound);
}
BrowserMessageInbound(String id) {
_id = id;
}
@Override
protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
}
@Override
protected void onTextMessage(CharBuffer arg0) throws IOException {
}
}
private final class LogMessageInbound extends MessageInbound {
@Override
protected void onClose(int status) {
connections.remove(this);
super.onClose(status);
}
@Override
protected void onOpen(WsOutbound outbound) {
super.onOpen(outbound);
connections.add(this);
}
@Override
protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
}
@Override
protected void onTextMessage(CharBuffer arg0) throws IOException {
}
}
}
웹소켓을 지원하는 servlet을 실현하려면 웹소켓 servlet을 계승하고createWebSocketInbound 함수를 실현하여 웹소켓 연결을 대표하는 대상을 되돌려야 합니다.
본고는 프로그램에서 요청에 전달된 id와 type 파라미터를 수신합니다. iid는 장치의 id를 대표하거나 브라우저가 실시간 로그를 보려는 장치의 id를 나타냅니다.type은 연결 요청이 장치에서 왔는지 브라우저에서 왔는지 나타냅니다.서브렛은 type의 값에 따라 대응하는 형식의 웹소켓 연결을 만듭니다.연결 요청이 장치 장치에서 온 경우 DeviceMessageInbound를 만듭니다.장치 요청이 브라우저 브라우저에서 온 경우 BrowserMessageInbound를 만듭니다.
DeviceMessageInbound 및 BrowserMessageInbound 모두 MessageInbound에서 상속
DeviceMessageInbound의 경우
onOpen()에서 장치 맵 테이블에 자신을 추가합니다.
onClose()에서 장치 맵 테이블에서 제거합니다.
onTextMessage () 는 Servlet에서 장치로부터 텍스트 메시지를 받았습니다.Servlet은 장치 id에 따라 이 장치의 실시간 로그를 감청하고 있는 모든 브라우저를 찾아 텍스트 메시지를 브라우저에 전달합니다.
BrowserMessageInbound의 경우
onOpen () 에서 id를 감청하는 브라우저 목록에 자신을 추가합니다.같은 장치 id를 대상으로 여러 브라우저가 동시에 로그를 볼 수 있도록 합니다.따라서 id에 대응하는 브라우저 연결이 있는지 먼저 판단해야 합니다.존재하지 않으면 목록을 만들고 브라우저 맵에 삽입합니다.이미 존재하면 id에 따라 목록을 찾아 이 목록에 추가합니다.
onClose에서 id를 통해 목록을 찾으면 목록에서 제거합니다.제거한 후 목록이 비어 있으면 브라우저 맵에서 목록을 제거합니다.
브라우저 측 구현
WebLogcat var Chat = {}; Chat.socket = null; Chat.connect = (function(host) { if ('WebSocket' in window) { Chat.socket = new WebSocket(host); } else if ('MozWebSocket' in window) { Chat.socket = new MozWebSocket(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').onkeydown = function(event) { if (event.keyCode == 13) { Chat.sendMessage(); } }; }; Chat.socket.onclose = function () { document.getElementById('chat').onkeydown = 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();
这其实是一个HTML5的页面,同样是需要部署到服务器上的。只不过用户通过浏览器访问,在浏览器上运行而已。
这个其实修改自Tomcat自带的一个Websocket的例子,叫Chat。里面关于device id是hard code为fv0557。
设备端实现
本文中的设备指的是一个嵌入式设备,是运行linux的ARM系统。所以选用libwebsocket来实现一个websocket的客户端。
#include
#include
static int was_closed = 0;
static int deny_deflate;
static int deny_mux;
/* dumb_increment protocol */
static int
callback_weblogcat(struct libwebsocket_context *this,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
{
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
LWS_SEND_BUFFER_POST_PADDING];
int l;
switch (reason) {
case LWS_CALLBACK_CLOSED:
fprintf(stderr, "LWS_CALLBACK_CLOSED
");
was_closed = 1;
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
/*
* LWS_CALLBACK_CLIENT_WRITEABLE will come next service
*/
fprintf(stderr, "LWS_CALLBACK_CLIENT_ESTABLISHED
");
libwebsocket_callback_on_writable(this, wsi);
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
((char *)in)[len] = '\0';
fprintf(stderr, "rx %d '%s'
", (int)len, (char *)in);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE:
l = sprintf((char *)&buf[LWS_SEND_BUFFER_PRE_PADDING],
"c #%06X %d %d %d;",
(int)random() & 0xffffff,
(int)random() % 500,
(int)random() % 250,
(int)random() % 24);
libwebsocket_write(wsi,
&buf[LWS_SEND_BUFFER_PRE_PADDING], l, LWS_WRITE_TEXT);
/* get notified as soon as we can write again */
libwebsocket_callback_on_writable(this, wsi);
sleep(3);
break;
/* because we are protocols[0] ... */
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
if ((strcmp(in, "deflate-stream") == 0) && deny_deflate) {
fprintf(stderr, "denied deflate-stream extension
");
return 1;
}
if ((strcmp(in, "x-google-mux") == 0) && deny_mux) {
fprintf(stderr, "denied x-google-mux extension
");
return 1;
}
break;
default:
break;
}
return 0;
}
static struct libwebsocket_protocols protocols[] = {
{
NULL,
callback_weblogcat,
0,
},
{ /* end of list */
NULL,
NULL,
0
}
};
int main( int argc, char* argv[])
{
struct libwebsocket_context *context;
struct libwebsocket *wsi_weblogcat;
const char *address = "192.168.xxx.xxx";
int port = 8080;
int use_ssl = 0;
int n = 0;
context = libwebsocket_create_context(CONTEXT_PORT_NO_LISTEN, NULL,
protocols, NULL,
NULL, NULL, NULL, -1, -1, 0, NULL);
if (context == NULL) {
fprintf(stderr, "Creating libwebsocket context failed
");
return 1;
}
/* create a client websocket using weblogcat protocol */
wsi_weblogcat = libwebsocket_client_connect(context, address, port, use_ssl,
"/WebLogcat/WebLogcat?id=fv0557&type=device", address, address,
protocols[0].name, -1);
if (wsi_weblogcat == NULL) {
fprintf(stderr, "libwebsocket weblogcat connect failed
");
return -1;
}
fprintf(stderr, "Websocket weblogcat connections opened
");
n = 0;
while (n >= 0 && !was_closed) {
n = libwebsocket_service(context, 1000);
if (n < 0)
continue;
}
fprintf(stderr, "Exiting
");
libwebsocket_context_destroy(context);
return 0;
}
이것도 libwebsocket에서 test-client를 수정합니다.c 파일.이것은 단지 시뮬레이션이 일정 시간마다 서버에 데이터를 보내는 것입니다.장치에서 웹 소켓을 실행하는 클라이언트를 테스트하는 데모는 데모일 뿐입니다.진정한 실시간 로그 시스템을 실현하려면, 이 프로세스는 시스템의 다른 프로세스에 로그를 보내는 인터페이스를 제공해야 한다.그리고 이 프로세스는 웹소켓을 통해 서버에 전송됩니다.또는 Android와 같이 시스템의 프로그램은 로그를 로그 장치에 기록하고 이 프로세스는 로그 장치에서 데이터를 읽고 웹소켓을 통해 서버에 보냅니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.