Pusherの挙動

DevToolでコンソールを開きながら、getting startedで挙動を確認します。

### index.html

<h1>Pusher Test</h1>
    <p>
        Try publishing an event to channel <code>my-channel</code>
        with event name <code>my-event</code>.
    </p>
     <script src="https://js.pusher.com/5.0/pusher.min.js"></script>
    <script>
        Pusher.logToConsole = true;

        var pusher = new Pusher('****************',{
            cluster: 'ap3',
            forceTLS: true
        });

        var channel = pusher.subscribe('my-channel');
        channel.bind('my-event', function(data){
            alert(JSON.stringify(data));
        });
    </script>

### server side
$ curl -sS https://getcomposer.org/installer | php
$ php composer.phar require pusher/pusher-php-server

require __DIR__ . '/vendor/autoload.php';

$options = array(
	'cluster' => 'ap3',
	'useTLS' => true
);
$pusher = new Pusher\Pusher(
	'********************',
	'********************',
	'******',
	$options
);

$data['message'] = 'hello world';
$pusher->trigger('my-channel', 'my-event', $data);

server側からclient側でデータが渡っていることを確認できます。

Laravel 6.xでEventを作成してPusherからメッセージ

### event作成
$ php artisan make:event Test
app/Events/Test.php

class Test implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public $message;

    public function __construct($message)
    {
        //
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new Channel('test_channel');
    }

    public function broadcastAs()
    {
        return 'test_event';
    }
}

### config/broadcasting.php

'default' => env('BROADCAST_DRIVER', 'pusher'),
'connections' => [

        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'useTLS' => true,
            ],
        ],

### web.php

Route::get('/sent', function(){
	event(new \App\Events\Test('テストメッセージ'));
});

Route::get('receive', function(){
return <<<HTML

<!DOCTYPE html>
<head>
	<title>Pusher Test</title>
	<script src="https://js.pusher.com/3.2/pusher.min.js"></script>
	<script>
		Pusher.logToConsole = true;

		var pusher = new Pusher('******************',{
			cluster : 'ap3',
			forceTLS: true
		});

		var pusherChannel = pusher.subscribe('test_channel');

		pusherChannel.bind('test_event', function(data){
			alert(data.message);
		});
	</script>
</head>
HTML;
});

broadcastdriverが初期値のnullのままだと動かないので注意が必要です。

laravel-websockets

Laravel WebScokets
Git hub: laravel-websockets
Official Document: laravel-websockets

– Drop-in Pusher replacement, SSL support, Laravel Echo support and a debug dashboard are just some of its features.

### How to use
https://docs.beyondco.de/laravel-websockets/1.0/getting-started/introduction.html

### package install
$ php composer.phar require beyondcode/laravel-websockets

### publish migration file
$ php artisan vendor:publish –provider=”BeyondCode\LaravelWebSockets\WebSocketsServiceProvider” –tag=”migrations”
Copied File [/vendor/beyondcode/laravel-websockets/database/migrations/create_websockets_statistics_entries_table.php.stub] To [/database/migrations/2020_02_07_062321_create_websockets_statistics_entries_table.php]

$ php artisan migrate
+——————————-+
| Tables_in_chat |
+——————————-+
| failed_jobs |
| messages |
| migrations |
| password_resets |
| users |
| websockets_statistics_entries |
+——————————-+

mysql> describe websockets_statistics_entries;
+————————-+——————+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+————————-+——————+——+—–+———+—————-+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| app_id | varchar(255) | NO | | NULL | |
| peak_connection_count | int(11) | NO | | NULL | |
| websocket_message_count | int(11) | NO | | NULL | |
| api_message_count | int(11) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+————————-+——————+——+—–+———+—————-+
※アクセス情報が保存される

### Websocket Configuration File
$ php artisan vendor:publish –provider=”BeyondCode\LaravelWebSockets\WebSocketsServiceProvider” –tag=”config”
Copied File [/vendor/beyondcode/laravel-websockets/config/websockets.php] To [/config/websockets.php]

### config/broadcasting.php

'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'cluster' => env('PUSHER_APP_CLUSTER'),
                'encrypted' => true,
                'host' => '192.168.33.10', // ご自身のホスト名
                'port' => 6001,
                'scheme' => 'http' // or https
            ],
        ],

### .env

PUSHER_APP_ID=1
PUSHER_APP_KEY=laravelWebsockets
PUSHER_APP_SECRET=laravelWebsockets
PUSHER_APP_CLUSTER=ap1

$ php artisan serve –host 192.168.33.10 –port 8000
$ php artisan websockets:serve
Starting the WebSocket server on port 6001…

app.js:28574 WebSocket connection to ‘wss://192.168.33.10/app/laravelWebsockets?protocol=7&client=js&version=5.0.3&flash=false’ failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED

何故だ。。。

socket.joinとsocket.broadcast

### html
– roomの値とmsgをjsonでemitする

<form id="myForm">
		<select id="rooms">
			<option value="room-1">Room 1</option>
			<option value="room-2">Room 2</option>
		</select>
		<input type="text" id="msg">
		<input type="submit" value="send">
	</form>
	<ul id="logs">
	</ul>
	<script
  src="https://code.jquery.com/jquery-3.4.1.min.js"
  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
  crossorigin="anonymous"></script>
	<script src="/socket.io/socket.io.js/"></script>
	<script>
		$(function(){
			var socket = io.connect();
			// emit: イベント発信 (ブラウザ側)
			// on: イベント待ち受け (サーバ側)
			$('#myForm').submit(function(e){
				// preventDefaultでsubmit後に遷移しないようにする
				e.preventDefault();
				socket.json.emit('emit_from_client', {
					room: $('#rooms').val(),
					msg: $('#msg').val(),
				});
				$('#msg').val('').focus();
			});
			socket.on('emit_from_server', function(data){
				$('#logs').append($('<li>').text(data));
			});
		});
	</script>

### app.js
joinしたルームに対してsocket.broadcastする

io.sockets.on('connection', function(socket){
	socket.on('emit_from_client', function(data){
		// ルームの情報を割り当て
		socket.join(data.room);	
		socket.emit('emit_from_server', 'you are in ' + data.room);
		// 対象ルームに対してのみbroadcast
		socket.broadcast.to(data.room).emit('emit_from_server', data.msg);
	});
});

on, emit, join, broadcastの意味がわかると、楽しくなってきます。

socket.ioのsocket.emit、socket.broadcast.emit、socket.on

公式: https://socket.io/
– socket.ioはNode.jsで動いている

$ npm install socket.io

### node.jsサーバー起動
node app

var app = require('http').createServer(handler),
	io = require('socket.io').listen(app),
	fs = require('fs');
app.listen(1337);

function handler(req, res){
	fs.readFile(__dirname + '/index.html', function(err, data){
		if(err) {
			res.writeHead(500);
			return res.end('Error');
		}
		res.writeHead(200);
		res.write(data);
		res.end();
	})
}

### socket.io
emitで送って、onで待ち受ける

index.html

<script
  src="https://code.jquery.com/jquery-3.4.1.min.js"
  integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
  crossorigin="anonymous"></script>
	<script src="/socket.io/socket.io.js/"></script>
	<script>
		$(function(){
			var socket = io.connect();
			// emit: イベント発信 (ブラウザ側)
			// on: イベント待ち受け (サーバ側)
			$('#myForm').submit(function(){
				socket.emit('emit_from_client', $('#msg').val());
			});
		});
	</script>

app.js

io.sockets.on('connection', function(socket){
	socket.on('emit_from_client', function(data){
		console.log(data);
	});
});

### serverからclientにemit
app.js

io.sockets.on('connection', function(socket){
	socket.on('emit_from_client', function(data){
		// console.log(data);
		socket.emit('emit_from_server', 'hello from server: ' + data);
	});
});

html

<ul id="logs"></ul>
<script>
		$(function(){
			var socket = io.connect();
			// emit: イベント発信 (ブラウザ側)
			// on: イベント待ち受け (サーバ側)
			$('#myForm').submit(function(e){
				// preventDefaultでsubmit後に遷移しないようにする
				e.preventDefault();
				socket.emit('emit_from_client', $('#msg').val());
			});
			socket.on('emit_from_server', function(data){
				$('#logs').append($('<li>').text(data));
			});
		});
	</script>

### socket.broadcast.emit
– socket.emitは接続しているsocketのみ
– socket.broadcast.emitは接続しているsocket以外
– io.sockets.emitは自分も含めたsockets

io.sockets.on('connection', function(socket){
	socket.on('emit_from_client', function(data){
		// console.log(data);
		// socket.emitは接続しているsocketのみ
		// socket.emit('emit_from_server', 'hello from server: ' + data);
		// socket.broadcast.emitは接続しているsocket以外
		socket.broadcast.emit('emit_from_server', 'hello from server: ' + data);
	});
});

dbに保存しない場合は、単にsocket.broadcast.emit、socket.onでデータ通信が出来るわけか。

express.ioによるchat room

### server.js

var express = require('express.io');
var app = express();
var PORT = 3000;

var fs = require("fs");
var https = require("https");
var options = {
	key: fs.readFileSync('key.pem'),
	cert: fs.readFileSync('server.crt')
}
app.https(options).io();

console.log('server started' + PORT);

app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res){
	res.render('index.ejs');
});

app.io.route('ready', function(req){
	req.io.join(req.data)
	app.io.room(req.data).broadcast('announce', {
		message: 'New client in the ' + req.data + ' room.'
	})
})

app.io.route('send', function(req){
	app.io.room(req.data.room).broadcast('message', {
		message: req.data.message,
		author: req.data.author,
	})
})

app.listen(PORT);

### index.ejs

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>WebRTC</title>
	<link rel="stylesheet" type="text/css" href="public/styles.css">
	<script src="/socket.io/socket.io.js"></script>
</head>
<body>
	<p><button id="takeProfilePicture" type="button" autofocus="true">Create Profile Picture</button></p>
	<video id="videoTag" autoplay></video>
	<div>
		<label>Your Name</label><input id="myName" type="text">
		<label>Message</label><input id="myMessage" type="text">
		<input id="sendMessage" type="submit">
		<div id="chatArea">Message: output:<br></div>
	</div>

	<script>
		navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia;
		var constraints = {audio: false, video: {
				mandatory: {
					maxWidth: 240,
					maxHeight: 240
				}
			}
		};
		var videoArea = document.querySelector("video");
		var myName = document.querySelector("#myName");
		var myMessage = document.querySelector("#myMessage");
		var sendMessage = document.querySelector("#sendMessage");
		var chatArea = document.querySelector("#chatArea");
		var ROOM = "chat";

		io = io.connect();
		io.emit('ready', ROOM);

		io.on('announce', function(data){
			displayMessage(data.message);
		});

		io.on('message', function(data){
			displayMessage(data.author + ": " + data.message);
		});

		sendMessage.addEventListener('click', function(ev){
			io.emit('send', {"author":myName.value, "message":myMessage.value, "room":ROOM});
			ev.preventDefault();
		}, false);

		function displayMessage(message){
			chatArea.innerHTML = chatArea.innerHTML + "<br>" + message;
		}

		navigator.getUserMedia(constraints, onSuccess, onError);

		function onSuccess(stream){
			console.log("Success! we have a stream!");
			// videoArea.src = window.URL.createObjectURL(stream);
			// videoArea.className = "grayscale_filter";
			videoArea.srcObject = stream;
		}

		function onError(error){
			console.log("Error with getUserMedia: ", error);
		}
	</script>
</body>
</html>

chat room top -> chat room create -> insert into mysql -> chat room display -> other user join って流れか
socket ioは、HTTP通信ではなく、WebSocketによって通信を行なっている

あれ、待てよ、webrtcだと、SDPの送信が必要なわけだけど、Socket.ioは、TURN serverは提供していないわけだから、TURNで取得したpublic ipをsocket.ioで転送するってこと?