質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

リアルタイムチャットでのスマホの入室者カウントについて

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 824

nosonosolife

score 30

JavaScriptとPHPでリアルタイムチャットを製作しているのですが、どうやって現在の入室者数をカウントするかで嵌っています。

入室者数を記録するファイルを用意して、入室したらPHP側で1足して、退室したら1引く、とすればいいんですが、
問題は途中でページを離脱した際の処理です。
チャットには「退室ボタン」を用意するつもりですが、押さずに別のページに移行するユーザーもいます。
そういう時は、パソコンならJavaScriptの「beforeunload」でPHPにデータを送信して、
途中でページを離脱した際でも退室扱いにする事が出来ます。
しかし、スマホ・タブレット用の「pagehide」では、同じタブ(ウィンドウ)でページ移行したり、ページを更新した際にはイベントが実行されるのですが、タブを閉じた際には実行されないのです。
そうなると、もしユーザーが「退室ボタン」を押さずにタブを閉じた際、入室者数に誤差が発生することになります。

これをなんとか解決したいのですが、どうすればいいでしょうか?
ご提示よろしくお願いいたします。

==2019/07/25 追記==
mikkameさん、mts10806さんから頂いたヒントから、自分なりに考えてコードを組んでみました。

session_id(sha1(uniqid(microtime())));


まずチャットにアクセスした際に、セッションIDを作成しておきます。

function chatOnline(){
    $.ajax({
        type: "post",
        url: "php/usercount.php",
        data: { Penname: userName },
        timeout: 10000,  // 単位はミリ秒

        success: function(result, textStatus, xhr) {
            onlineTimer = setTimeout( "chatOnline()", 10000 );
        },
        // 通信失敗時の処理
        error: function(xhr, textStatus, error) {

        }
    });
}
    $.ajax({
        url: 'php/usercount.php',
        type: 'GET',
        dataType: 'json',
        data: {ut: (new Date().getTime())}
    })
    .done(function(result) {
        if(result){
            $("hoge").html('現在の入室者数:' + result[0].usercount + '人');
        }
    });


javasctiptで10秒ごとにPHPにデータを送信します。

<?php
session_start();
if($_SERVER["REQUEST_METHOD"] == "POST"){
    mb_language("uni");
    mb_internal_encoding("utf-8"); //内部文字コードを変更
    mb_http_input("auto");
    mb_http_output("utf-8");

    date_default_timezone_set('Asia/Tokyo');

    if(isset($_POST['Penname'])){
        $json = file_get_contents('../data/log/userlog.json');
        $records = json_decode($json, true);

        $Id = session_id();
        $Penname = htmlspecialchars($_POST["Penname"]);
        $Time = time();

        $result_index = array_keys(array_column($records, 'id'), $Id);

        if($result_index == null){
            $records[] = [
                'id' => $Id,
                'time' => $Time,
                'penname' => $Penname,
            ];
        }else{
            foreach ($records as $key => $value) {
                $nowtime = $Time;
                $lasttime = $value['time'];
                if(($nowtime - $lasttime) > 60){
                    unset($records[$key]);
                }else{
                    foreach ($result_index as $index) {
                        $records[$index]['time'] = $Time;
                    }
                }
            }
        }

        $out_json = json_encode($records);

        $result = file_put_contents("../data/log/userlog.json", $out_json, LOCK_EX);
    }
}
if($_SERVER["REQUEST_METHOD"] == "GET"){
    $json = file_get_contents('../data/log/userlog.json');
    $records = json_decode($json, true);
    $Num = count($records);
    $usercount[] = [
        'usercount' => $Num,
    ];

    echo json_encode($usercount);
}
?>


PHPでは、リクエストがPOSTの場合は、ユーザー別にセッションIDや最終アクセス時間などをファイルに記録します。
最終アクセス時間が1分を過ぎても変更されない場合、ファイルからデータを削除します。
リクエストがGETの場合は、現在の入室者数を返します。
以上です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • dyoshikawa

    2019/07/24 10:15

    これ以上どんな回答を求めているのでしょうか?

    キャンセル

  • m.ts10806

    2019/07/24 10:57

    mikkameさんの回答で充分解決可能です。(私も同じ方法を思いつきました)
    キーワードも出ているので探して何もコードのヒントが得られない内容ではないですよ。

    キャンセル

  • nosonosolife

    2019/07/24 11:52

    直近のリクエストの取得の仕方とか、判定の仕方とか、
    もう少し詳細な解説が欲しかったのです。
    どうしても書き方が思いつかなくて。

    キャンセル

回答 2

checkベストアンサー

+4

古典的な方法ですが、phpで行うなら、クライアントより、定期的(1秒毎とか)にリクエストを送信させ
直近のアクセスがあるクライアントをオンラインとし
アクセスがなくなったアカウントをオフラインとするとかですかね。
server sent eventを使う意味がほぼ無くなりますが

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/07/24 16:50

    nosonosolifeさん
    このままやりとりを続けていても我々から実コードは出てきませんし、
    考え方および設計・実装する上でのヒントは十分出ているものと思いますので、
    一旦ここは引いて、ここまでの回答・コメントから考えて自身でやってみてください。

    キャンセル

  • 2019/07/24 16:52

    mikkameさんの例に考え方を追加

    年齢を表示したい → 生年月日か、年齢が必要

    年齢だけだと毎年足していかないといけない

    生年月日のみ登録して年齢は現在日時から年齢を計算する

    キャンセル

  • 2019/07/25 21:27

    先ほど質問文に、頂いたヒントから考えたコードを追記いたしました。

    キャンセル

+2

Server-Sent Eventsでは多分無理
websocketなら多分できる

https://www.ibm.com/developerworks/jp/web/library/wa-http-server-push-with-websocket-sse/index.html

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る