SAP Cloud Platform  上で WebSocket を使ってみた

# はじめに
この記事は [chillSAP 夏の自由研究2020](https://note.com/chillsap/n/n66e12ef65d11?fbclid=IwAR3REd19CLNDvQHZDNjQaE_2Cki4bqGln4pVk7UJjHYEq39yUtF_5Xch1BQ) の記事として執筆しています

今回は、 SAPUI5 の API Reference を眺めていると WebSocket( [sap.ui.core.ws.WebSocket](https://sapui5.hana.ondemand.com/#/api/sap.ui.core.ws.WebSocket) )のライブラリを発見したので試してみました。


## WebSocket とは？
>WebSocket（ウェブソケット）は、コンピュータネットワーク用の通信規格の1つである。ウェブアプリケーションにおいて、双方向通信を実現するための技術規格である。2011年にRFC 6455として>最初の標準仕様が定義された。

[転載：WebSocket - Wikipedia](https://ja.wikipedia.org/wiki/WebSocket)

難しいのでなるべく簡単に説明すると、
Webの世界でブラウザとサーバが通信をする場合には、ブラウザからの呼びかけに対してサーバが返事する仕組みでしたが
WebSocket を使用すると、サーバからブラウザに対して呼びかけをしてくれる仕組みが作れるということです。

## 今回のブツ
SAP Cloud Platform 上で WebSocketを使用した ブラウザChat を作ってみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303235/9d4b42a9-8c32-0cc8-7bed-20724f806e33.png)

### 仕組み
1. ブラウザから SAP UI5 で準備したWebチャットを動かすと、 Webチャット は SAP Cloud Platform 上に nodejs で構築したチャットサーバと、 **WebSocket** で接続します。
2. 接続中の Webチャット から発言すると、発言内容は チャットサーバへと送られます。
3. チャットサーバは、Webチャットからの発言を受け取ると、接続中のすべてのWebチャット に対して受信した内容を返します。
4. Webチャットは、チャットサーバからの通信を受け取ると、画面上へ受信した内容を表示します。

![WebChat.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303235/75fc86b5-2cf0-3f67-5024-ab811585b414.gif)

# まとめ
送信したメッセージは、ほぼリアルタイムに相手側へ送られているのがご覧いただけると思います。
WebSocketを使わなくても、ポーリングや Comet を使用すれば似たようなことは実装できますが
ほぼリアルタイムの相互通信が簡単に実装出来るので、一度 WebSocket に興味を持っていただければと思います。

## 参考
- [class sap.ui.core.ws.WebSocket - SAPUI5 SDK](https://sapui5.hana.ondemand.com/#/api/sap.ui.core.ws.WebSocke)
- [WebSocket - Wikipedia](https://ja.wikipedia.org/wiki/WebSocket)
- [Simple chat server example using UI5 WebSocket](https://openui5.blogspot.com/2014/01/simple-chat-server-example-using-ui5.html)

# ここから先はソースコード
## チャットサーバ実装

```JavaScript:server.js
/*eslint no-console: 0*/
"use strict";

var WebSocketServer = require('ws').Server;
var ws = new WebSocketServer({ port: process.env.PORT || 8080 });

// 接続時に呼ばれる
ws.on('connection', function (socket) {
	
    // クライアントからのデータ受信時に呼ばれる
    socket.on('message', message => {
        console.log('received: %s', message);
        // 受け取ったメッセージを接続中のクライアントへ返信
        ws.clients.forEach(client => {
            client.send(message);
        });
    });
    
	socket.send(JSON.stringify({
		user: 'chat server',
		text: 'Hello from Server'
	}))

    // 切断時に呼ばれる
	socket.on('close', function() {
		console.log('Disconnected from Server');
	}); 
    
}); 
```

## Webチャット(クライアント)実装

```JavaScript:View1.controller.js
sap.ui.define([
	"sap/ui/core/mvc/Controller",
	"sap/ui/core/ws/WebSocket"
], function (Controller, WebSocket) {
	"use strict";
	return Controller.extend("cli.client.controller.View1", {
		oModel: new sap.ui.model.json.JSONModel(),

		// チャットサーバへデータ送信 
		notify: function(user, text) {
			var msg = user + ': ' + text;
			var msgAreaId = "chatInfo";
			
			// 現在表示中のメッセージを取得
			var chatMsg = sap.ui.getCore().byId(msgAreaId).getValue();
			
			// var lastInfo = this.oModel.oData.chat;
			
			if (chatMsg.length > 0) {
				chatMsg += "\r\n";
			}
			
			chatMsg += msg;
			sap.ui.getCore().byId(msgAreaId).setValue(chatMsg);
		},
		
		onAfterRendering: function() {
			var thisController = this;
			
			sap.ui.getCore().byId("userName").setValue("ななし");

			var url = "wss://チャットサーバ";
			var ws = new sap.ui.core.ws.WebSocket(url);
			this.ws = ws;
			
			//接続時
			ws.attachOpen(function (oControlEvent) {
				thisController.notify('system', 'connection opened...');
			});
			// エラー時
			ws.attachError(function (oControlEvent) {
				thisController.notify('system', 'connection Error!');
			});
			//受信時
			ws.attachMessage(function (oControlEvent) {
				var data = jQuery.parseJSON(oControlEvent.getParameter('data'));
				thisController.notify(data.user, data.text);
			});
			//終了時
			ws.attachClose(function (attachClose) {
				thisController.notify('system', 'Disconnected from Server');
			});
		},
		
		// 送信ボタン押下
		onSendMessage: function (oEvent) {
			var user = sap.ui.getCore().byId("userName").getValue();
			var msg = sap.ui.getCore().byId("chatMsg").getValue();
			if (msg.length > 0) {
				this.ws.send(JSON.stringify({
					user: user,
					text: msg
				}));
				sap.ui.getCore().byId("chatMsg").setValue("");
			}
		}
	});
});
```