3

これが私のシナリオです:

  • ユーザーが新しいメッセージを送信するたびに、リクエストがサーバーに保存している間、プレビューとして会話スレッドに追加しています。HTTP POST
  • 一定の間隔で、 を使用setIntervalして、会話内の新しいメッセージをチェックしています。
  • 新しいメッセージが返された場合は、メッセージのプレビュー バージョンを削除し、データベースから新しいメッセージを追加します。

これは、チャット コンテンツを生成するスクリプトです。

function refresh_chat(){
    var last = $('.conversation li:not(.fake):last').data('id');
    $.post('includes/router.php', {
        task: 'update_conversation',
        id: '<?=$_GET['conversationid']?>',
        last: last
    }, function (data, response) {
        var recibidas = $(data).find('li');

        /* IF there are new entries */
        if (recibidas.length > 0) {
            /* Remove all fake entries */
            $('.conversation li.fake').remove();

            /* Append new entries */
            $('.conversation').append($(data).filter('.notifications').html());

            /* If this new entries are not unread, 
               remove the unread to the previous ones*/
            if(!$(data).find('li:last').hasClass('unread')) {
                $('.conversation li.unread').removeClass('unread');
            }
        }
    });
}

var t = setInterval(function () {   
    refresh_chat();
}, 3000);

これは、ユーザーが入力したときに新しいエントリを追加する方法です。

$('body').on('submit', '.send_message_form_conversation', function(e) {
    e.preventDefault();
    var id_to = $(this).find('#id_to').val();
    var msj = $(this).find('#msj').val();
    if (msj.length >= 2) {
        $(this).find('#msj').val('');
        $(this).find('.nicEdit-main').html('');
        //alert(id_to);
        $('.conversation').append(
            '<li class="unread fake">' +
             '<div class="avatar">' +
              '<a href="index.php?userid=<?=sesion()?>">' +
               '<img alt="" src="<?=$_SESSION['avatar']?>">' +
              '</a>' +
             '</div>' +
             '<div class="txt">' +
              '<a class="userName" href="index.php?userid=<?=sesion()?>">' + 
               '<?=$_SESSION['alias']?> -- ' +
               '<span class="date">' +
                "<?=get_texto_clave('ahora mismo')?>" +
               '</span>' +
              '</a>
             '<span class="msj">' + msj + '</span>' + 
            '</div>' +
            '<span data-id="47" class="close">X</span>' + 
           '</li>');

        $.post('includes/msj.php?', {
            task  : 'post_message',
            id_to : id_to,
            msj   : msj
        }, function (data, response) {
            $(".conversation").scrollTop($(".conversation")[0].scrollHeight);
        });
    } else {
        $(this).parent().effect("shake", { times:0, distance: 3 }, 200);
    }                       
});

ご覧のとおり、<li>アイテムには 2 つのクラスがあります: .fake(これは、このアイテムがユーザーが送信したばかりのプレビューであり、js によって追加された.unreadことを意味します) または (受信者がメッセージを受信したばかりであることを意味します)

私が苦労しているのは、重複したエントリが表示されることがあることです (ただし、表示されるだけで、データベースでは重複していません)。私の間隔に何か問題があると思いますか?

何が原因でしょうか? (ずっと読んでいるのですが、変なところが見当たりません…)

PD: 基本的に、いくつかのメッセージが複数回表示されています:S

-編集-

$q = "SELECT * FROM pms " .
     "WHERE ((id_to = $id and id_from = " . sesion() . ") OR " .
     "       (id_from = $id and id_to = " . sesion() . ")) " .
     "AND (id > $from) " .
     "ORDER by fecha ASC " . $limit;

このクエリは、JavaScript パラメータの最後の$.post()リクエストで使用されるものです (これは$from、ユーザーに表示される最後のメッセージを表します)。

4

8 に答える 8

3

シナリオについて考えてみましょう:

  1. refresh_chat() - サーバーに送信されたリクエスト
  2. 3秒経過
  3. refresh_chat() - サーバーに送信された同一のリクエスト
  4. 最初に受け取った応答。新しいエントリが追加されました。
  5. 2 番目の応答を受け取りました。同じエントリが再び追加されました。

これを解決する最も簡単な方法は、setInterval を削除し、応答が処理された後に setTimeout を追加することです。

于 2012-11-22T00:54:21.997 に答える
1

Socket.io を試してみてください! メッセージをサーバーに送信し、そこで解析および変換して返送しても、目立った遅延は発生しないため、プレビューは不要です。

チャット アプリケーションに Ajax と setTimeout を使用すると、苦痛以外の何物でもありません。

于 2012-11-24T20:22:50.277 に答える
1

問題が発生したら、サーバーから返されたデータ (つまり ajax 応答本文) をチェックして、「古い」データもそこにあるかどうかを確認します。この場合、クエリをデバッグする必要があります。

また、DB クエリは SQL インジェクション攻撃に対して非常に脆弱ですhttp://en.wikipedia.org/wiki/SQL_injection (たとえば、最後に : x'; DROP TABLE pms; の値を指定します。 -- )

 $q = "SELECT * FROM pms " .
 "WHERE ((id_to = $id and id_from = " . sesion() . ") OR " .
 "       (id_from = $id and id_to = " . sesion() . ")) " .
 "AND (id > $from) " .
 "ORDER by fecha ASC " . $limit;

最後に、ポーリングはチャット アプリを実現する最良の方法ではないようです。おそらく、ソケットとポーリング (ブラウザがソケットをサポートしていない場合) を組み合わせた方がうまくいくでしょう。ベストC

于 2012-11-26T09:20:46.360 に答える
0

あなたが抱えている問題は、現在実行中かどうかに関係なく、3000 ミリ秒ごとに関数setIntervalの新しい実行をスケジュールするという事実に関連しています。refresh_chat

代わりにすべきことはrefresh_chat、自分で 1 回呼び出すだけで、内部コールバック関数 (サーバーからデータが戻ってくるときに呼び出される関数) の一番下にsetTimeout(refresh_chat, 3000).

refresh_chatこれにより、クライアントは、作業が終了したことがわかるまで、関数の新しい呼び出しをスケジュールしようとしません。

これは一種の遅延再帰呼び出しと考えてください (ただし、呼び出しスタックが増え続けるわけではないため、技術的にはそうではありません)。

于 2012-11-27T10:45:29.097 に答える
0

@zch が指摘しているように、この問題は実際の方法で発生する可能性があります。

私は2つのアプローチを提案します:

1.- refresh_chat() を呼び出す直前に、サーバーへの最後の投稿を中止します。こうすることで、応答がブラウザーに届かなかった (またはサーバーがまったく応答しなかった) 場合、3 秒後に最後の試行を破棄し、再度要求を送信します。これは、決して取得されない応答でサーバー リソースが浪費される可能性があるため、最善の方法ではありません。

2.- 循環カウンター (0 から 10 など) を作成し、リクエストごとにインクリメントして送信し、サーバーにそれを返送させてから、一致しない応答を破棄します。このようなもの:

Request with 0 -> Response with 0
Request with 1 -> Response with 1
Request with 2 -> (no response)
Request with 3 -> Response with 2 (Discarded as it doesn't match)
Request with 4 -> (no response yet)
           -> Response with 3 (Discarded as 4 is already sent)
           -> Response with 4 (Accepted, actual Request was with 4)

これが役に立つことを願っています。

于 2012-11-27T12:45:51.487 に答える
0

いくつかの点、「未読」クラスはあまり機能していないようです。プライベート メッセージが既読または未読になるのはいつですか? 誰かがチャットを閉じて後で戻ってきた場合、すべてのメッセージを受け取るべきですか、それとも最新のメッセージだけを受け取るべきですか? 思うことばかり…

setInterval を使用して同じリクエストを再送信するため、バグが発生する可能性があります。問題を修正するコードを次に示します。「last_id」変数を使用してリクエストの繰り返しを停止し、処理が完了した後にのみ setTimeout を呼び出すことに注意してください。

チャットにはロング ポーリングまたはコメットを使用することをお勧めします。コメット、jquery によるロング ポーリングのチュートリアル最先端の機能を使いたい場合は、HTML5 WebSocket を使用しますが、サーバーの構成をいくつか行う必要があります。

function refresh_chat(){
    var last = $('.conversation li:not(.fake):last').data('id');
    if(last_id == last) {
        /* this request was already successfully processed */
        /* wait some more and try again */
        if(auto_refresh) window.setTimeout(refresh_chat, 3000);
        return;
    }
    $.ajax({
    type: 'POST',
    url: 'includes/router.php',
    data: { task: 'update_conversation',id: '<?=$_GET['conversationid']?>',last: last },
    success: function(data) {
        var recibidas = $(data).find('li');
        /* IF there are new entries */
        if (recibidas.length > 0) {
            last_id = last;
            /* Remove all fake entries */
            $('.conversation li.fake').remove();
            /* Append new entries */
            $('.conversation').append($(data).filter('.notifications').html());
        }
        //
        if(auto_refresh) window.setTimeout(refresh_chat, 3000);
    },
    error: function(jqXHR, textStatus, errorThrown) {
        console.log("Ajax Error: " + (textStatus||""));
        //
        if(auto_refresh) window.setTimeout(refresh_chat, 3000);
    }
    });
}
var auto_refresh = true;
var last_id = -1;
refresh_chat();
于 2012-11-27T13:47:42.673 に答える
0

表示される新しいエントリがある場合にのみ、偽のエントリを「隠している」ためだと思います。

if(recibidas.length>0){
    $('.conversation li.fake').remove();

その if ステートメントからこれを取り出すことをお勧めします。送信機能のremove()内部のように、新しいメッセージを投稿した後に必要があるようです。function(data,response){

于 2012-11-07T21:08:19.687 に答える
-1

この ajax チャットはまったく解決策ではありません。この本を読む必要があります。jabber サーバーをお持ちでない場合は、私のサーバーへのアクセスを提供します (ユーザー登録の更新など)。本を読んでから連絡してください。XMPP + Strophe ライブラリ チャット (Google や Facebook が使用しているもの) です。したがって、最初からやり直して何か新しいことを学び、ajax チャットのエラーを修正することをお勧めします。

于 2012-11-24T19:36:39.793 に答える