이 문서에서 하려고 하는 목적
해결방법
사용자가 귀하의 페이지에 없는 경우에도 연결되도록 하는 것은 일종의 보안 위반이기 때문에 이것이 작동하는지 확실하지 않습니다.
(새로 고침을 하다 보면 페이지를 빠져나왔다가 다시 들어가게 되기 때문입니다.)
가장 좋은 방법은 백그라운드에서 실행할 수 있는 서비스 워커를 사용하는 것입니다
즉 웹소켓을 연결한 상태에서 새로고침 또는 다른 페이지로 이동시 웹소켓 연결을 유지하는데 목적이 있습니다.
해결방법 결과
위와 같은 목적을 달성하기 위해서 아래 3가지에 대해서 알아보도록 하겠습니다.
- WebWorkers
- SharedWorkers
- BroadcastChannels
WebWorkers : 페이지를 로드할 때 각 탭 또는 브라우저에서 새 세션을 생성
SharedWorkers : 각 탭에서 동일한 세션을 사용한다는 것입니다.
따라서 모든 탭이나 창에는 작업할 동일한 작업자와 동일한 웹 소켓 연결이 있습니다.
웹 소켓
문제
WebWorkers, SharedWorkers and BroadcastChannels
SharedWorkers 를 사용하여 서버 부하 줄이기
기본 웹 소켓 서버 설정
$ git clone https://github.com/ayushgp/shared-worker-socket-example.git $ npm install websocket
var https = require('https'); var fs = require('fs'); var WebSocketServer = require('websocket').server; var https_options = { ca: fs.readFileSync("/home/workermymq/html/worker2/fullchain.pem"), key: fs.readFileSync("/home/workermymq/html/worker2/privkey.pem"), cert: fs.readFileSync("/home/workermymq/html/worker2/cert.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) { console.log("A new client connected!"); ws.id = wss.getUniqueID(); CLIENTS.push(ws); const ip = request.headers['x-forwarded-for'] || request.connection.remoteAddress; if(ws.readyState === ws.OPEN){ //let sendData = {command: 'message', msg: ' 클라이언트'+ip+' 접속을 환영합니다 from 서버', sid:ws.id}; //let sendData = ''; //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); }); */ ws.on("message", data => { console.log(`Message from client: ${data}`); const parsed = JSON.parse(data); sendAll(data); ws.send( JSON.stringify({ ...parsed.data, messageFromServer: `Hello tab id: ${parsed.data.from}` }) ); }); ws.on("close", () => { CLIENTS.pop(ws); console.log("Sad to see you go :("); }); /* setTimeout( () => ws.send(JSON.stringify({ broadcast: "A broadcast for all clients!" })), 15000 ); */ }); function sendAll (message) { for (var i=0; i<CLIENTS.length; i++) { console.log(">>>>:"+i); CLIENTS[i].send(JSON.stringify(message)); //CLIENTS[i].send(message); } }
SharedWorker 만들기
// Open a connection. This is a common // connection. This will be opened only once. const ws = new WebSocket("wss://workermymq.co.com:9101"); // Create a broadcast channel to notify about state changes const broadcastChannel = new BroadcastChannel("WebSocketChannel"); // Mapping to keep track of ports. You can think of ports as // mediums through we can communicate to and from tabs. // This is a map from a uuid assigned to each context(tab) // to its Port. This is needed because Port API does not have // any identifier we can use to identify messages coming from it. const idToPortMap = {}; // Let all connected contexts(tabs) know about state cahnges ws.onopen = () => broadcastChannel.postMessage({ type: "WSState", state: ws.readyState }); ws.onclose = () => broadcastChannel.postMessage({ type: "WSState", state: ws.readyState }); // When we receive data from the server. ws.onmessage = ({ data }) => { // Construct object to be passed to handlers const parsedData = { data: JSON.parse(data), type: "message" }; if (!parsedData.data.from) { // Broadcast to all contexts(tabs). This is because // no particular id was set on the from field here. // We're using this field to identify which tab sent // the message broadcastChannel.postMessage(parsedData); } else { // Get the port to post to using the uuid, ie send to // expected tab only. idToPortMap[parsedData.data.from].postMessage(parsedData); } }; // Event handler called when a tab tries to connect to this worker. onconnect = e => { const port = e.ports[0]; port.onmessage = msg => { // Collect port information in the map idToPortMap[msg.data.from] = port; // Forward this message to the ws connection. ws.send(JSON.stringify({ data: msg.data })); }; // We need this to notify the newly connected context to know // the current state of WS connection. port.postMessage({ state: ws.readyState, type: "WSState" }); };
index.html 만들기
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8" /> <title>Web Sockets</title> </head> <body> <input type="text" id="msg" name="msg" class="form-control"> <button id="btnMsg" type="button" class="btn btn-sm btn-primary">보내기</button> <p></p> <div id="message">메세지 출력 자리 </div> <!-- jQuery --> <script src="../../worker2/libs/jquery-3.2.1.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/node-uuid/1.4.8/uuid.min.js"></script> <script src="main.js"></script> </body> </html>
main.js 만들기
const worker = new SharedWorker("worker.js"); const id = uuid.v4(); // Set initial state let webSocketState = WebSocket.CONNECTING; console.log(`Initializing the web worker for user: ${id}`); worker.port.start(); worker.port.onmessage = event => { switch (event.data.type) { case "WSState": webSocketState = event.data.state; break; case "message": handleMessageFromPort(event.data); break; } }; const broadcastChannel = new BroadcastChannel("WebSocketChannel"); broadcastChannel.addEventListener("message", event => { switch (event.data.type) { case "WSState": webSocketState = event.data.state; break; case "message": handleBroadcast(event.data); break; } }); // Listen to broadcasts from server function handleBroadcast(data) { console.log("This message is meant for everyone!!!!!"); console.log(data); if(typeof data.data == "string") { const parsed = JSON.parse(data.data); console.log(parsed.data.data_message); $("#message").html(parsed.data.data_message); } } function handleMessageFromPort(data) { console.log(`This message is meant only for user with id: ${id}`); } // Use this method to send data to the server. function postMessageToWSServer(input) { if (webSocketState === WebSocket.CONNECTING) { console.log("Still connecting to the server, try again later!"); } else if ( webSocketState === WebSocket.CLOSING || webSocketState === WebSocket.CLOSED ) { console.log("Connection Closed!"); } else { worker.port.postMessage({ // Include the sender information as a uuid to get back the response from: id, data_message: input }); } } setTimeout(() => postMessageToWSServer(" 새로운 분 들어오셨습니다."), 2500); const sendItemrequest = msg=>{ /* let itemrequestMsg = { 'ID': id, 'Key': { 'Name': 'itemName', 'msg' : msg } }; */ let itemrequestMsg = { 'ID': id, 'data_message': msg } /* let itemrequestObj = { 'commandObj': itemrequestMsg, 'command': 'requestitem' } */ //Send Item Request message to Web Workers worker.port.postMessage(itemrequestMsg); }; $(document).ready(function () { $("#btnMsg").on("click", _=>{ let msg = $("input[name=msg]").val(); sendItemrequest(msg); }); });
Scaling WebSocket Connections using Shared Workers
You can find the code for this post on SharedWorker WebSocket example.
ayushgp.xyz
Run websocket in web worker or service worker - javascript
I have 9 websocket connections from different sites working to update the DOM with data. Currently I am connecting to all and listening to all websockets and updating the data with a function call....
stackoverflow.com
'프로그래밍 > Js' 카테고리의 다른 글
vue3 Composition API 에서 jQuery 사용하기 (1) | 2023.01.17 |
---|---|
websocket 바이너리 전송 arraybuffer (0) | 2022.05.06 |
웹워커 안에서 웹소켓 구동 시험 테스트 + Node 소켓 서버 (1) | 2021.11.09 |
node + ws + express webSocket 샘플 예제 (0) | 2021.11.08 |
[ES6+] 버튼 클릭시 클랙스 메소드 바로실행(if문 사용 안함) (0) | 2021.11.04 |