.개요
웹 브라우저 JavaScript 런타임은 기본적으로 단일 스레드 환경입니다.
그러나 HTML 표준은 웹 브라우저가 메인 스레드와 백그라운드 스레드(워커 스레드)에서 JavaScript를 실행할 수 있도록 하는 웹 워커 기능을 도입하여 개발자가 웹 브라우저에서 다중 스레드 JavaScript 응용 프로그램을 구현할 수 있도록 합니다.
본 문서는 JavaScrip 웹 워커를 사용하여 WebSocket API 웹 애플리케이션을 구현하는 방법을 보여줍니다.

.웹소켓 개요
WebSocket 사양은 웹 페이지가 원격 호스트와의 양방향 통신을 위해 WebSockets 프로토콜을 사용할 수 있도록 하는 API를 정의합니다. WebSocket 인터페이스를 소개하고 웹을 통해 단일 소켓을 통해 작동하는 전이중 통신 채널을 정의합니다. 이 사양은 나중에 Python, Ruby, Go, Java 등과 같은 다른 클라이언트 기술 측면에도 적용되었습니다.
.웹워커 개요
웹 작업자는 브라우저 스레드와 백그라운드에서 실행되는 하나 이상의 JavaScript 스레드의 동시 실행을 허용합니다. 기본 JavaScript 애플리케이션 스레드는 이벤트 모델 및 postMessage() 함수를 통해 작업자 스레드 애플리케이션과 통신합니다. postMessage() 함수는 문자열 또는 JSON 객체를 단일 인수로 받아들일 수 있습니다.

.웹워커 테스트
main.js let wk = new Worker("worker.js"); wk.addEventListener("message", function (e) { console.log('Message from Workers: ', e.data); }, false); wk.postMessage('This is from main.js'); worker.js self.addEventListener('message', function(e) { self.postMessage(e.data); }, false); self.postMessage('This is from Workers.js');
.지원되는 웹브라우져
- 크롬
- 파이어폭스
- 엣지
.파일구조
혹시 모를 내 mac 경로 /cofo~~~~/worker2
혹시 모를 서버경로 /131서버/home/예s키/에이치티/worker2/
접속주소 https://ye스키.cofor~.com/worker2/
index.html: 애플리케이션 HTML 페이지 app/main.js: 애플리케이션 메인 파일 app/worker.js: 애플리케이션 웹 작업자 파일 css/cover.css: 애플리케이션 CSS 파일 libs/jquery-3.2.1.min.js: jQuery 라이브러리 파일 bootstrap/css, bootstarp/fonts 및 bootstrap/js 폴더: Bootstrap CSS 및 라이브러리 파일 용 폴더 node_modules 폴더: 웹 서버 실행을 위한 Node.js 및 Express.js 모듈용 폴더 server.js: 웹 서버 애플리케이션 파일 package.json: 프로젝트 npm 종속성 파일
.구현도 사진
마지막 구현하면 아래와 같은 그림으로 표현할수 있습니다.

.적용된 소스
main.js (function ($) { // worker 서비스 등록 navigator.serviceWorker.register('./app/ws_workers.js').then(function(registration) { console.log('ServiceWorker registration successful with scope: ', registration.scope); }).catch(function(err){ alert("sw error : "+err); console.log('ServiceWorker registration failed: ', err); }); // 알림 허용/차단 Notification.requestPermission().then(function (result) { if (result === 'granted') { console.log('[Notification] 허용: ', result); } else { console.log('[Notification] 차단: ', result); } }); //Define variables var serverurl = '', username = '', itemID = 0, wk = new Worker("./app/worker.js"); const protocol = 'tr_json2'; const loginID = 1; /* ----------------- UI Events ----------------- */ $(document).ready(function () { $('#btnConnect').click(function () { // serverurl = `ws://${$('#txtServerurl').val()}/WebSocket`; //serverurl = 'wss://' + $('#txtServerurl').val() + '/WebSocket'; serverurl = 'wss://' + $('#txtServerurl').val(); connect(serverurl); }); $('#btnLogin').click(function () { let username = $('#txtUsername').val(); sendLoginrequest(username); }); $('#btnSubscribe').click(function () { let servicename = $('#txtServiceName').val(); let itemname = $('#txtItemName').val(); sendItemrequest(servicename, itemname); }); $('#btnUnSubscribe').click(function () { sendItemCloserequest(); }); $('#btnLogout').click(function () { sendLoginCloserequest(); }); }); /* ----------------- Web Workers Events ----------------- */ //Receive events from Web Workers ws_worker.js file wk.addEventListener("message", function (oEvent) { let response = oEvent.data; console.log(response); //Get command parameter to identify operation let command = response.command; const res = JSON.parse(response); console.log(res); if(res.Key.Name == "incomingMsg") { $('#messagesPre').html(response); } if (command === 'connect') { //WebSocket connection event processConnectionEvent(response); } else if (command === 'incomingMsg') { //Receive incoming messages from ADS WebSocket processData(response.msg); } else if(command === 'disconnect') { //Receive Disconnect sucess from ADS WebSocket $('#btnConnect').html('Connect'); } }, false); function processConnectionEvent(response) { $('#btnConnect').html(response.msg); } //Process incoming messages from ADS WebSocket function processData(data) { let msgtype = data.Type; console.log(">>>:"+msgtype); //Clear previous message $('#messagesPre').html(''); //If incoming message is REFRESH_RESP if (msgtype === 'Refresh') { if (data.Domain === 'Login') { $('#messagesPre').html('Receive: Login REFRESH_RESP:<br/>'); //Login Refresh_resp } else { $('#messagesPre').html('Receive: Data REFRESH_RESP:<br/>'); //Data Refresh_resp } //$('#messagesPre').html(`${$('#messagesPre').html()} ${JSON.stringify(data, undefined, 2)}`); //IE10 does not support JS Template literals $('#messagesPre').html($('#messagesPre').html() + JSON.stringify(data, undefined, 2)); //Display REFRESH_RESP } else if (msgtype === 'Update') { //If incoming message is UPDATE_RESP //$('#messagesPre').html(`Receive: UPDATE_RESP:<br/> ${JSON.stringify(data, undefined, 2)}`); //Update_resp $('#messagesPre').html('Receive: UPDATE_RESP:<br/>' + JSON.stringify(data, undefined, 2)); //Display Update_resp } else if (msgtype === 'Status') {//If incoming message is STATUS_RESP //$('#messagesPre').html(`Receive: STATUS_RESP:<br/> ${JSON.stringify(data, undefined, 2)}`); //Status_resp $('#messagesPre').html('Receive: STATUS_RESP:<br/>' + JSON.stringify(data, undefined, 2)); //Display Status_resp } else if (msgtype === 'Error'){//If incoming message is ERROR_RESP $('#messagesPre').html('Receive: ERROR_RESP:<br/>' + JSON.stringify(data, undefined, 2)); //Display Status_resp } else if (msgtype === 'Ping') { //If incoming message is PING (server ping) //$('#messagesPre').html(`Recieve Ping:</br> ${JSON.stringify(data, undefined, 2)}`); //Server Ping $('#messagesPre').html('Recieve Ping:</br>' + JSON.stringify(data, undefined, 2)); //Server Ping sendPong(); } } /* ----------------- WebSocket functions ----------------- */ //Establish WebSocket connection function connect(serverurl) { //$('#commandsPre').html(`ws = new WebSocket('${serverurl}', '${protocol}');`); $('#commandsPre').html('ws = new WebSocket("' + serverurl + '", "' + protocol + '");'); let connectObj = { 'commandObj': { 'serverurl': serverurl, 'protocol': protocol }, 'command': 'connect' }; //Send message to Web Workers wk.postMessage(connectObj); } //Send a Login Request message to Real-Time Advanced Distribution Server WebSocket function sendLoginrequest(username) { //Create Login request message let loginMsg = { 'ID': loginID, 'Domain': 'Login', 'Key': { 'Elements': { 'ApplicationId': '256', 'Position': '127.0.0.1' }, 'Name': '' } }; loginMsg.Key.Name = username; //$('#commandsPre').html(`Sending Login request message to Web Workers: WebWorkers.post(${JSON.stringify(loginMsg, undefined, 2)});`); $('#commandsPre').html('Sending Login request message to Web Workers: WebWorkers.post(' + JSON.stringify(loginMsg, undefined, 2) + ');'); let loginObj = { 'commandObj': loginMsg, 'command': 'login' } //Send Login message to Web Workers wk.postMessage(loginObj); } //Send Item Request message to ADS WebSocket function sendItemrequest(service, itemname) { //Create stream ID, must not be 1 (Login) if (itemID === 0) { itemID = loginID + 1; } else { itemID += 1; } //create Market Price request message, if user does not set service name, let the Real-Time Advanced Distribution Server use default service let itemrequestMsg = { 'ID': itemID, 'Key': { 'Name': itemname } }; //if user set service name, request interested service name if(service !==''){ itemrequestMsg.Key.Service = service } let itemrequestObj = { 'commandObj': itemrequestMsg, 'command': 'requestitem' } //Send Item Request message to Web Workers wk.postMessage(itemrequestObj); //$('#commandsPre').html(`Sending Item request message to Web Workers: WebWorkers.post(${JSON.stringify(itemrequestMsg, undefined, 2)});`); $('#commandsPre').html('Sending Item request message to Web Workers: WebWorkers.post(' + JSON.stringify(itemrequestMsg, undefined, 2) + ');'); } //Send Item Close Request message to ADS WebSocket function sendItemCloserequest() { let closeitemrequestMsg = { 'ID': itemID, 'Type': 'Close' }; let closeitemrequestObj = { 'commandObj': closeitemrequestMsg, 'command': 'closeitem' } //Send Item Close Request message to Web Workers wk.postMessage(closeitemrequestObj); //$('#commandsPre').html(`Sending Item Close request message to Web Workers: WebWorkers.post(${JSON.stringify(closeitemrequestMsg, undefined, 2)});`); $('#commandsPre').html('Sending Item Close request message to Web Workers: WebWorkers.post(' + JSON.stringify(closeitemrequestMsg, undefined, 2) + ');'); } //Send { 'Type': 'Pong' } for acknowledge Server PING function sendPong() { let pongObj = { 'commandObj': { 'Type': 'Pong' }, 'command': 'pong' } //Send PONG message to Web Workers wk.postMessage(pongObj); //$('#commandsPre').html(`Sending Client Pong: ws.send(${JSON.stringify({ 'Type': 'Pong' }, undefined, 2)});`); $('#commandsPre').html('Sending Client Pong to Web Workers: WebWorkers.post(' + JSON.stringify({ 'Type': 'Pong' }, undefined, 2) + ');'); } //Send Login Close Request message to ADS WebSocket function sendLoginCloserequest() { let closeloginrequestMsg = { 'Domain': 'Login', 'ID': loginID, 'Type': 'Close' }; let closeloginrequestObj = { 'commandObj': closeloginrequestMsg, 'command': 'logout' } //Send Login Close Request message to Web Workers wk.postMessage(closeloginrequestObj); //$('#commandsPre').html(`Sending Login Close Request: ws.send(${JSON.stringify(closeloginrequestMsg, undefined, 2)});`); $('#commandsPre').html('Sending Login Close Request to Web Workers: WebWorkers.post(' + JSON.stringify(closeloginrequestMsg, undefined, 2) + ');'); } })($);
worker.js (function () { //Define WebSocket and protocol variables let ws = null; let sid = null; //https://ayushgp.github.io/scaling-websockets-using-sharedworkers/ //const broadcastChannel = new BroadcastChannel("WebSocketChannel"); //Define based object response to market_price_app.js let onMessage_response = { 'command': 'incomingMsg', 'msg': null, 'sid': null }; //Receive message from market_price_app.js self.addEventListener('message', function (e) { let data = e.data; //Get command parameter to identify operation let command = data.command; //console.log("[worker.js] data.command:"+data.command); //console.log("[worker.js] data.commandObj:"); //console.log(data.commandObj); if (command === 'connect') { connect(data.commandObj); //Establish WebSocket connection } else if (command === 'logout') { sendOMMmessage(data.commandObj); disconnect(); //Terminate WebSocket connection } else { sendOMMmessage(data.commandObj); } //self.postMessage(response); }, false); /* ----------------- Application events functions ----------------- */ //Establish WebSocket connection function connect(commandObj) { ws = new WebSocket(commandObj.serverurl, commandObj.protocol); ws.onopen = onOpen; ws.onmessage = onMessage; ws.onerror = onError; ws.onclose = onClose; } function disconnect() { ws.close(); var disconnect_response = { 'command': 'disconnect', 'msg': 'Disconnected', 'sid': sid }; self.postMessage(disconnect_response); } //Send message to ADS WebSocket function sendOMMmessage(commandObj) { console.log("[worker.js] sendOMMessage start"); ws.send(JSON.stringify(commandObj)); } /* ----------------- WS events ----------------- */ //Establish WebSocket connection success function onOpen(event) { console.log("[worker.js] onOpen start"); console.log(event); var onOpen_response = { 'command': 'connect', 'msg': 'Connected', 'sid': sid }; self.postMessage(onOpen_response); } //Receives incoming message from WebSocket function onMessage(event) { console.log("[worker.js] onMessage start"); let incomingdata = JSON.parse(event.data.toString()); self.postMessage(event.data); //Iterate each JSON message and send it to market_price_app.js for (let index = 0; index < incomingdata.length; index++) { onMessage_response.msg = incomingdata[index]; //console.log(">>>[worker.js] incomingdata"); //console.log(onMessage_response); self.postMessage(onMessage_response); } }; function onError(event) { var onError_response = { 'command': 'error', 'msg': event, 'sid': sid }; self.postMessage(onError_response); }; function onClose(event) { var onClose_response = { 'command': 'close', 'msg': 'WebSocket Closed', 'sid': sid }; self.postMessage(onClose_response); }; })();
index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>WebSocket API Market Price Streaming Example</title> <!-- Bootstrap Latest compiled and minified CSS --> <link rel="stylesheet" href="./bootstrap/css/bootstrap.min.css"> <!-- Bootstrap Optional theme --> <link rel="stylesheet" href="./bootstrap/css/bootstrap-theme.min.css"> <!-- customize CSS --> <link href="./css/cover.css" rel="stylesheet"> <!-- jQuery --> <script src="./libs/jquery-3.2.1.min.js"></script> <!-- Bootstrap Latest compiled and minified JavaScript --> <script src="./bootstrap/js/bootstrap.min.js"></script> <!-- Application JS --> <script src="./app/main.js"></script> </head> <body> <!-- WebSocket connection UI input form --> <div class="container-fluid"> <h4>WebSocket API with Web Workers Example</h4> <form class="form-inline"> <div class="form-group"> <label for="txtServerurl">Server:</label> <input type="text" value="yeskey.coforward.com:9101" id="txtServerurl" placeholder="Server IP:WebSocket Port" class="form-control"> <button id="btnConnect" type="button" class="btn btn-sm btn-primary">Connect</button> </div> <br/><br/> <div class="form-group"> <label for="txtUsername" >User:</label> <input type="text" id="txtUsername" class="form-control" placeholder="user"> <button id="btnLogin" type="button" class="btn btn-sm btn-primary">Login</button> <button id="btnLogout" type="button" class="btn btn-sm btn-primary">Logout</button> </div> <br/> <br/> <div class="form-group"> <!--<label for="txtServiceName">Service Name:</label> <input type="text" id="txtServiceName" class="form-control"> --> <label for="txtItemName">Item Name:</label> <input type="text" id="txtItemName" value="" placeholder="IBM.N" class="form-control"> <label for="txtServiceName">Service Name:</label> <input type="text" id="txtServiceName" class="form-control" placeholder="[Optional]"> <button id="btnSubscribe" type="button" class="btn btn-sm btn-primary">Subscribe</button> <button id="btnUnSubscribe" type="button" class="btn btn-sm btn-primary">Unsubscribe</button> </div> </form> <!-- Display outgoing messages and commands --> <h5>Commands:</h5> <div id="command"> <pre id="commandsPre"> </pre> </div> <!-- Display incoming messages --> <h5>Incoming Messages:</h5> <div id="message"> <pre id="messagesPre"> </pre> </div> </div> </body> </html>
.Node 웹소켓 서버
웹워커를 사용하기 위해서는 https 필요하기 때문에 웹소켓 서버 wss 와 port:9101 사용한다.
server.js var https = require('https'); var fs = require('fs'); var WebSocketServer = require('websocket').server; var https_options = { ca: fs.readFileSync("chain1.pem"), key: fs.readFileSync("privkey1.pem"), cert: fs.readFileSync("cert1.pem") }; var httpsServer = https.createServer( https_options, function(request, response) { console.log((new Date()) + ' Received request for ' + request.url); response.writeHead(404); response.end(); }) httpsServer.listen(9101); var WebSocketServer = require('ws').Server; var wss = new WebSocketServer({ server: httpsServer, autoAcceptConnections: false }); //https://stackoverflow.com/questions/13364243/websocketserver-node-js-how-to-differentiate-clients let CLIENTS=[]; wss.getUniqueID = function () { function s4() { return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); } return s4() + s4() + '-' + s4(); }; wss.on('connection', function connection(ws,request) { ws.id = wss.getUniqueID(); CLIENTS.push(ws); const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress; console.log(ws.id+` 새로운 클라이언트[${ip}] 접속`); if(ws.readyState === ws.OPEN){ let sendData = {command: 'message', msg: ' 클라이언트'+ip+' 접속을 환영합니다 from 서버', sid:ws.id}; //ws.send(JSON.stringify(sendData)); sendAll(sendData); } ws.on('message', function incoming(message) { message = JSON.parse(message); console.log('received: %s', message); //ws.send(JSON.stringify(message)); sendAll(message); }); }); function sendAll (message) { for (var i=0; i<CLIENTS.length; i++) { console.log(i); CLIENTS[i].send(JSON.stringify(message)); } }
.실행방법
$ npm install $ node server.js

- YouTube
www.youtube.com
'프로그래밍 > Js' 카테고리의 다른 글
websocket 바이너리 전송 arraybuffer (0) | 2022.05.06 |
---|---|
Shared Workers 이용하여 WebSocket 연결 방법 (웹소켓을 연결한 상태에서 새로고침 또는 다른 페이지로 이동시 웹소켓 연결을 유지하는게 목적) (0) | 2021.12.17 |
node + ws + express webSocket 샘플 예제 (0) | 2021.11.08 |
[ES6+] 버튼 클릭시 클랙스 메소드 바로실행(if문 사용 안함) (0) | 2021.11.04 |
service worker push API 제작기 (1) - 권한요청 (0) | 2021.10.29 |