Created
February 11, 2021 20:48
-
-
Save robertrypula/f8da8f89819068a97bef4f27d04ad5b7 to your computer and use it in GitHub Desktop.
WebSocket - binary broadcast example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<title>WebSocket client</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<script> | |
let webSocket; | |
const parseHex = string => { | |
return string | |
.split(' ') | |
.filter(value => value !== '') | |
.map(value => parseInt(value, 16) % 256) | |
.filter(value => value || value === 0); | |
}; | |
const getHex = rawBytes => rawBytes.map(rawByte => (rawByte < 16 ? '0' : '') + rawByte.toString(16)).join(' '); | |
const fileRead = file => { | |
return new Promise((resolve, reject) => { | |
const reader = new FileReader(); | |
if (file) { | |
reader.onload = () => resolve(Array.from(new Uint8Array(reader.result))); | |
reader.readAsArrayBuffer(file); | |
} else { | |
reject('File was not selected'); | |
} | |
}); | |
}; | |
const fileSave = (filename, bytes) => { | |
const blob = new Blob([new Uint8Array(bytes)]); // , { type: 'application/pdf' } | |
const link = document.createElement('a'); | |
link.href = window.URL.createObjectURL(blob); | |
link.download = filename; | |
link.click(); | |
}; | |
const toKiB = rawBytesLength => `${(rawBytesLength / 1024).toFixed(2)} KiB`; | |
const showError = error => { | |
console.log(error); | |
document.getElementById('container-error').style.display = 'block'; | |
document.getElementById('container-error').innerHTML = error instanceof Event ? 'Unknown error' : error; | |
connectionPendingDisable(); | |
}; | |
const connectionUp = () => { | |
document.getElementById('container-connect').style.display = 'none'; | |
document.getElementById('container-send').style.display = 'block'; | |
document.getElementById('container-error').innerHTML = ''; | |
sendChange(); | |
}; | |
const connectionDown = () => { | |
document.getElementById('container-connect').style.display = 'block'; | |
document.getElementById('container-send').style.display = 'none'; | |
connectionPendingDisable(); | |
}; | |
const connectionPendingEnable = () => { | |
document.getElementById('connect').setAttribute('disabled', 'disabled'); | |
document.getElementById('url').setAttribute('disabled', 'disabled'); | |
}; | |
const connectionPendingDisable = () => { | |
document.getElementById('connect').removeAttribute('disabled'); | |
document.getElementById('url').removeAttribute('disabled'); | |
}; | |
const fileReadHandler = rawBytes => { | |
if (confirm(`Would you like to put ${rawBytes.length} bytes (${toKiB(rawBytes.length)}) into the textarea?`)) { | |
document.getElementById('send').value = getHex(rawBytes); | |
sendChange(); | |
} | |
}; | |
const send = () => { | |
if (!webSocket || webSocket.readyState !== WebSocket.OPEN || webSocket.bufferedAmount) { | |
return false; | |
} | |
const arrayBuffer = new Uint8Array(parseHex(document.getElementById('send').value)).buffer; | |
webSocket.send(arrayBuffer); | |
return true; | |
}; | |
const sendChange = () => { | |
const rawBytes = parseHex(document.getElementById('send').value); | |
document.getElementById('send-button').innerHTML = `Send ${rawBytes.length} bytes (${toKiB(rawBytes.length)})`; | |
}; | |
const receive = rawBytes => { | |
const receivedDataElement = document.createElement('div'); | |
const receivedDataHexElement = document.createElement('div'); | |
const receivedDataSaveElement = document.createElement('button'); | |
receivedDataHexElement.innerHTML = getHex(rawBytes); | |
receivedDataSaveElement.innerHTML = `Save ${rawBytes.length} bytes to file (${toKiB(rawBytes.length)})`; | |
document.getElementById('container-received-data').appendChild(receivedDataElement); | |
receivedDataElement.appendChild(receivedDataHexElement); | |
receivedDataElement.appendChild(receivedDataSaveElement); | |
receivedDataSaveElement.addEventListener('click', event => { | |
const filename = new Date() | |
.toISOString() | |
.slice(0, 19) | |
.replace(/[:\-]/g, '') | |
.replace('T', '_'); | |
fileSave(filename + '.bin', parseHex(event.target.parentNode.querySelector('div').innerHTML)); | |
}); | |
}; | |
const connect = () => { | |
try { | |
connectionPendingEnable(); | |
webSocket = new WebSocket(document.getElementById('url').value, ['audio-network-reborn']); | |
webSocket.binaryType = 'arraybuffer'; | |
webSocket.addEventListener('open', () => connectionUp()); | |
webSocket.addEventListener('close', () => connectionDown()); | |
webSocket.addEventListener('error', event => showError(event)); | |
webSocket.addEventListener('message', event => receive([...new Uint8Array(event.data)])); | |
} catch (error) { | |
showError(error); | |
} | |
}; | |
</script> | |
<style> | |
body, | |
html { | |
margin: 0; | |
padding: 0; | |
} | |
h1 { | |
display: block; | |
margin: 0; | |
padding: 0; | |
} | |
.container { | |
padding: 16px; | |
} | |
.row { | |
padding-bottom: 8px; | |
} | |
textarea { | |
box-sizing: border-box; | |
display: block; | |
width: 100%; | |
min-height: 300px; | |
padding: 8px; | |
} | |
textarea::placeholder { | |
color: #cecece; | |
} | |
#container-received-data > div { | |
margin-bottom: 16px; | |
border-radius: 8px; | |
padding: 16px; | |
background-color: lightgray; | |
} | |
#container-received-data > div > div { | |
margin-bottom: 8px; | |
font-family: monospace; | |
font-size: 12px; | |
line-height: 16px; | |
max-height: 100px; | |
overflow-y: auto; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>WebSocket - binary broadcast example</h1> | |
</div> | |
<div class="container" id="container-connect"> | |
<input type="text" id="url" value="ws://sndu.pl:6175" /> | |
<button id="connect" onClick="connect()">Connect</button> | |
</div> | |
<div class="container" id="container-error" style="display: none"></div> | |
<div class="container" id="container-send" style="display: none"> | |
<div class="row"> | |
<input | |
type="file" | |
id="file-attachment" | |
onchange="fileRead(this.files[0]).then(rawBytes => fileReadHandler(rawBytes)).catch(error => showError(error))" | |
/> | |
</div> | |
<div class="row"> | |
<textarea | |
id="send" | |
oninput="sendChange()" | |
placeholder="Hex values like: 48 65 6c 6c 6f 20 77 6f 72 6c 64 21" | |
></textarea> | |
</div> | |
<div class="row"> | |
<button id="send-button" onClick="send()">Send</button> | |
</div> | |
<div class="row"> | |
<strong>NOTE:</strong> Server will fail when you will try to send more than 65535 bytes of data (>= 64 KiB) | |
</div> | |
</div> | |
<div class="container" id="container-received-data"></div> | |
<div class="container"> | |
Copyright (c) 2019-2021 Robert Rypuła - | |
<a href="https://github.com/robertrypula" target="_blank">https://github.com/robertrypula</a> | |
</div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment