便利ページ:Javascriptでちょっとした便利な機能を作ってみた のシリーズです。
今回は、WebSocketをサクッと呼び出せるページを作ります。
最近のほとんどのブラウザには、WebSocket APIが実装されているため、それを使います。
もろもろは、GitHubに上げてあります。
Webページはこちらからも見ることができます。ユーティリティタブでWebSocketを選択してください。
また、今回からついで、ベースとしていたフレームワークをBootstrap 3.4.1からBootstrap 5に変更し、便利ページ内の各ページをVueのコンポーネントで構成するようにしました。
こうすることで、ページもメンテナンスしやすく、追加しやすくなりました。
もし、Bootstrap3.4.1との比較を見たい場合は以下を参照してください。
Bootstrap 3からBootstrap 5に移行してみた
画面はこんな感じです。
PWAにもしているので、アプリとして登録してあげてください。
Chromeを立ち上げなくても、いつでも便利ページを呼び出せますので。WindowsでもAndroidでもOKです。
#ソースコード
といっても、大したことはやっていないので、ソースコード(Vueコンポーネント)を示しておきます。
const log_separator = "\n----------\n";
export default {
mixins: [mixins_bootstrap],
template: `
<div>
<h2 class="modal-header">WebSocket</h2>
<label class="title">url</label>
<input type="text" class="form-control" v-model="ws_url" placeholder="wss://192.168.1.1:443/ws">
<div class="row">
<label class="col-auto title">protocols</label>
<span class="col-auto">
<input type="text" class="form-control" v-model="ws_protocols">
</span>
</div>
<button class="btn btn-secondary" v-on:click="ws_open" v-if="!ws_socket">Open</button>
<button class="btn btn-secondary" v-on:click="ws_close" v-else>Close</button>
<br>
status: {{ws_status_message}}
<br><br>
<div class="row">
<label class="col-auto title">message</label>
<span class="col-auto">
<select class="form-select" v-model="ws_send_type">
<option value="text">text</option>
<option value="binary">binary</option>
</select>
</span>
</div>
<input type="text" class="form-control" v-model="ws_send_message">
<button class="btn btn-secondary" v-on:click="ws_send" v-bind:disabled="!ws_socket">送信</button>
<br><br>
<textarea class="form-control col" id="ws_textarea" rows="10">{{ws_console_message}}</textarea>
<button class="btn btn-secondary" v-on:click="console_clear">クリア</button>
</div>`,
data: function () {
return {
ws_socket: null,
ws_url: "",
ws_protocols: "",
ws_console_message: "",
ws_send_type: "text",
ws_send_message: "",
ws_status_message: "closed",
}
},
methods: {
/* WebSocket */
ws_open: function(){
this.ws_close();
try{
this.ws_socket = new WebSocket(this.ws_url);
this.ws_socket.binaryType = "arraybuffer";
this.ws_socket.onopen = (event) => {
// console.log("websocket opened", event);
this.console_log(this.make_console_state_message(event));
var date_string = new Date().toLocaleString('ja-JP', { "hour12": false, "year": "numeric", "month": "2-digit", "day": "2-digit", "hour": "2-digit", "minute": "2-digit", "second": "2-digit" });
this.ws_status_message = "opened from " + date_string;
};
this.ws_socket.onclose = (event) =>{
// console.log("websocket closed", event);
this.console_log(this.make_console_close_message(event));
this.ws_socket = null;
this.ws_status_message = "closed";
};
this.ws_socket.onerror = (event) =>{
// console.log("websocket error", event);
this.console_log(this.make_console_state_message(event));
};
this.ws_socket.onmessage = (event) => {
// console.log("websocket message", event);
if (typeof (event.data) == "string" || event.data instanceof String ){
this.console_log(this.make_console_input_message(event));
}else{
this.console_log(this.make_console_input_message(event));
}
};
}catch(error){
alert(error);
}
},
ws_close: function(){
if (this.ws_socket)
this.ws_socket.close();
},
ws_send: function(){
if( this.ws_send_type == "text" ){
this.ws_socket.send(this.ws_send_message);
this.console_log(this.make_console_output_message("text", this.ws_send_message));
}else{
var data = hexStr2byteAry(this.ws_send_message);
var array = new Uint8Array(data);
this.ws_socket.send(array);
this.console_log(this.make_console_output_message("binary", array));
}
},
console_clear: function () {
this.ws_console_message = "";
},
make_console_output_message: function (type, data) {
var date_string = new Date().toLocaleString('ja-JP', { "hour12": false, "year": "numeric", "month": "2-digit", "day": "2-digit", "hour": "2-digit", "minute": "2-digit", "second": "2-digit" });
var message;
if (typeof (data) == "string" || data instanceof String) {
message = data;
} else {
// message = new Uint8Array(data).toString();
message = byteAry2hexStr(data);
}
return "[SEND] message " + type + " " + date_string + "\n" + message;
},
make_console_state_message: function (event) {
var date_string = new Date().toLocaleString('ja-JP', { "hour12": false, "year": "numeric", "month": "2-digit", "day": "2-digit", "hour": "2-digit", "minute": "2-digit", "second": "2-digit" });
return "[STATE] " + event.type + " " + date_string;
},
make_console_close_message: function (event) {
var date_string = new Date().toLocaleString('ja-JP', { "hour12": false, "year": "numeric", "month": "2-digit", "day": "2-digit", "hour": "2-digit", "minute": "2-digit", "second": "2-digit" });
return "[STATE] " + event.type + " code=" + event.code + " " + date_string;
},
make_console_input_message: function(event){
var date_string = new Date().toLocaleString('ja-JP', { "hour12": false, "year": "numeric", "month": "2-digit", "day": "2-digit", "hour": "2-digit", "minute": "2-digit", "second": "2-digit" });
var message;
var type;
if (typeof (event.data) == "string" || event.data instanceof String) {
message = event.data;
type = "text";
} else {
// message = new Uint8Array(event.data).toString();
message = byteAry2hexStr(event.data);
type = "binary";
}
return "[RECV] " + event.type + " " + type + " " + date_string + "\n" + message;
},
console_log: function(message){
this.ws_console_message = message + log_separator + this.ws_console_message;
}
},
mounted: function(){
var elem_in = document.querySelector("#ws_textarea");
elem_in.style.height = 10;
}
};
function hexStr2byteAry(hexs, sep = '') {
hexs = hexs.trim(hexs);
if (sep == '') {
var array = [];
for (var i = 0; i < hexs.length / 2; i++)
array[i] = parseInt(hexs.substr(i * 2, 2), 16);
return array;
} else {
return hexs.split(sep).map((h) => {
return parseInt(h, 16);
});
}
}
function byteAry2hexStr(bytes, sep = '', pref = '') {
if (bytes instanceof ArrayBuffer)
bytes = new Uint8Array(bytes);
if (bytes instanceof Uint8Array)
bytes = Array.from(bytes);
return bytes.map((b) => {
const s = b.toString(16);
return pref + (b < 0x10 ? ('0' + s) : s);
}).join(sep);
}
要は、以下でWebSocketサーバに接続して、
this.ws_socket = new WebSocket(this.ws_url);
以下で、サーバに送信して、
this.ws_socket.send(this.ws_send_message);
以下で、サーバから受信する。
this.ws_socket.onmessage = (event) => {
それだけです。
何かわかんなくなったり、パラメータで微調整したい場合は、以下を参考にしてください。
接続・切断・送信・受信の動きは、テキストエリアに逐一出力していますので、時系列で終えるようにしています。
WebSocket API (WebSockets)
https://developer.mozilla.org/ja/docs/Web/API/WebSockets_API
#終わりに
HTTPSで立ち上げたWebサーバのJavascriptからHTTPのWebSocketサーバに接続するには、WebSocketサーバをHTTPS接続にする必要があります。
以下を参考に、Web Proxyサーバを立ち上げてください。
HTTPで立ち上げたWebSocketサーバに、HTTPSで接続するためのWeb Proxyの立ち上げ
あと、こちらもご参考まで。
以上