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>item可能有两个类:(.fake表示这个item是用户刚刚提交的预览,已经被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;

此查询是$.post()请求中使用的查询,其中$fromJavaScript 参数中的最后一个(代表显示给用户的最后一条消息)

4

8 回答 8

3

想想场景:

  1. refresh_chat() - 请求发送到服务器
  2. 3秒过去
  3. refresh_chat() - 发送到服务器的相同请求
  4. 第一次收到回复。添加了新条目。
  5. 收到第二个回复。再次添加了相同的条目。

解决这个问题的最简单方法是去掉 setInterval,并在处理完响应后添加 setTimeout。

于 2012-11-22T00:54:21.997 回答
1

当问题出现时,检查您从服务器返回的数据(即 ajax 响应正文)是否也存在“旧”数据。如果是这种情况,您必须调试您的查询。

此外,您的数据库查询很容易受到 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 回答
1

试试 Socket.io!即使您将消息发送到服务器,在那里对其进行解析和转换并将其发送回,也不会出现任何明显的延迟,因此无需进行预览。

将 Ajax 和 setTimeout 用于聊天应用程序只会给您带来痛苦。

于 2012-11-24T20:22:50.277 回答
0

A few points, the 'unread' class doesn't seem to do much. You're auto refreshing anyways, when is a private message read or unread? If someone closes the chat and goes back later should they get all of their messages or just the newest ones? Just things to think about...

You bug is likely because you use setInterval and resend the same request here's some code that corrects the issue. Notice the use of the "last_id" variable to stop repeated requests, as well as, calling setTimeout only after processing has completed.

I would suggest you use long polling or comet for your chat. Comet, long polling with jquery tutorial If you want to be really cutting edge go with HTML5 WebSockets, but you'll have to do some server configuration.

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 回答
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

您遇到的问题与每 3000 毫秒setInterval安排一次新的refresh_chat函数执行有关,无论当前是否正在进行执行。

相反,您应该做的是简单地refresh_chat自己调用一次,然后在内部回调函数的最底部(当有数据从服务器返回时调用的那个),添加一个setTimeout(refresh_chat, 3000).

这将确保客户端在refresh_chat您知道它已完成其工作之前不会尝试安排对该函数的新调用。

将其视为某种延迟递归调用(尽管从技术上讲,它不是一个,因为调用堆栈不会不断增加)。

于 2012-11-27T10:45:29.097 回答
-1

这个 ajax 聊天根本不是解决方案。你需要读这本书。如果您没有 jabber 服务器,我会给您访问权限(用户注册更新等)。看书,然后联系我。这是 XMPP + Strophe 库聊天(谷歌和 Facebook 正在使用的)!所以最好重新开始学习新的东西,然后在 ajax 聊天中修复错误!

于 2012-11-24T19:36:39.793 回答