3

我一直在做一个快速简单的 jQuery/PHP 聊天,放在我的网站上供访问者交流。我已经估计了 200 个同时网站用户(连接用户)的峰值,最多 10-20 人实际聊天。

这是怪癖:

正如我已经经历过两次(认为这似乎是一个不太可能发生的事件,而不是在您执行特定操作后发生的事情),聊天恰好加载了多条已经是红色的消息并显示它们。

为了让聊天系统尽可能简单,我想出了以下代码:


HTML 代码:

<div class="chat">

    <ul class="chat">

        <li class="chat" >

            <h5 class="chat">Date</h5>
            <h6 class="chat">Time</h6>
            <h4 class="chat">User</h4>
            <br/>
            <q class="chat">Message</q>

        </li>

    </ul>

    <input class="chat" placeholder="write something..."/>

</div>

如您所见,我为 jQuery 放置了一个占位符li元素,以用作片段来使用实际消息创建新的li元素,并将它们添加到ul元素中。


jQuery代码:

发送消息:

$(document).ready(function(){

    chatSnippet = $('ul.chat').html(); // here chatSnippet is a global variable
    $('ul.chat').html('');

    $('input.chat').change(function(event){// Send your message

    message = $(this).attr('value');

// first thing I perform an asynchronous POST to the receiving php script

    $.post(

        'php/chatRec.php',

        {

            user : currentUser,
            message: message,

        }

    );

// meanwhile I add a new li element to the chat html with the content just submitted


    date.setTime(event.timeStamp);

    hours = ''+date.getHours();

    if(hours.length < 2) hours = '0'+hours;

    minutes = ''+date.getMinutes();

    if(minutes.length < 2) minutes = '0'+minutes;

    day = ''+date.getDate();

    if(day.length < 2) day = '0'+day;

    newChatMessage = chatSnippet.replace('Date', ''+day+' '+months[date.getMonth()]);
    // here months is an array with the months names (in italian)
    newChatMessage = newChatMessage.replace('Time', ''+hours+':'+minutes);

    newChatMessage = newChatMessage.replace('User', connectedUser);

    newChatMessage = newChatMessage.replace('Message', message);

    $mess = $(newChatMessage);

    $mess.hide().prependTo('ul.chat').fadeIn(500);

    $(this).attr('value','');

});

refreshChat(''); // this function retrives new messages from the DB

// Here I perform a void refreshChat call so I'll get all the messages in the DB regardless from the currentUser (at page refresh)

});

接收消息:

// This code is placed outside (before) the .ready function

function refreshChat(user){// Receiving messages

$.post(

    'php/chatInv.php',

    {

        user : user,
        token: lastMessage // this variable contains the token of the last red message

    },

    function(data){

        receivedMessages = jQuery.parseJSON(data);

        for(message in receivedMessages){

            message = receivedMessages[message].Message;

            date = receivedMessages[message].Day.split('-');
            time = receivedMessages[message].Time.split(':');

            newChatMessage = chatSnippet.replace('Date', ''+date[2]+' '+months[parseInt(date[1])-1]);

            newChatMessage = newChatMessage.replace('Time', ''+time[0]+':'+time[1]);

            newChatMessage = newChatMessage.replace('User', receivedMessages[message].Sender);

            newChatMessage = newChatMessage.replace('Message', message);

            $mess = $(newChatMessage);

            $mess.hide().prependTo('ul.chat').fadeIn(500);

            lastMessage = receivedMessages[messages].token;

        }

        nextRefresh = setTimeout("refreshChat('"+currentUser+"')",2000);

// When I'm done I set a timeout of 2 secs and then perform another refresh

    }

);

}

PHP代码:

收到一条新消息(我认为问题出在此处):

    mysql_connect("localhost", "root", "root") or die(mysql_error());
    mysql_select_db("chat") or die(mysql_error());

    $characters = array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');

    $token = $characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)].$characters[rand(0,61)];

    $all_Msgs = mysql_query("SELECT * FROM Messages ORDER BY ID");

    $prev_Msg = array('ID' => 1 , 'Sender' => $_POST['user'], 'Message' => $_POST['message'], 'Day' => date("Y-m-d"), 'Time' => date("H:i:s"), 'token' => $token);

    while($Msg = mysql_fetch_array($all_Msgs)){

        $update_success = mysql_query("UPDATE Messages SET Sender='".$prev_Msg['Sender']."', Message='".$prev_Msg['Message']."', Day='".$prev_Msg['Day']."', Time='".$prev_Msg['Time']."', token = '".$prev_Msg['token']."' WHERE ID=".$Msg['ID']);

        $prev_Msg = $Msg;

    }

基本上我在这里所做的是接收新的帖子消息,生成一个令牌和一个包含新输入数据的数组元素(它本身就是一个数组),这样做我在一个固定大小的 SQL 表上执行更新语句的序列,覆盖新的第一条记录上的数据,然后用前一条记录覆盖每个记录(这样最后一条记录将最终丢失)。

发送消息:

    mysql_connect("localhost", "root", "root") or die(mysql_error());
    mysql_select_db("chat") or die(mysql_error());

    $receiver = $_POST['user'];
    $token = $_POST['token'];

    $all_Msgs = mysql_query("SELECT * FROM Messages ORDER BY ID");

    $newMessages = array();

    while($Msg = mysql_fetch_array($all_Msgs)){

        if($Msg['token'] == $token) break;

        if($Msg['Sender'] != $receiver) array_unshift($newMessages,$Msg);

    }

    echo json_encode($newMessages);

因此,我将在最后一条已知消息之后插入的数据库中所有记录的数组的 JSON 编码发送给客户端,并且其作者不是查询客户端。


我的嫌疑人:

我得出的结论是,在执行消息接收(服务器端)时,从数据库中获取每条消息都有一个时间跨度,如果同时执行刷新,则找不到消息,并且如果该消息是我们正在寻找的最后一条红色消息,那么服务器将只选择表中的所有消息并将它们发送回来。

结果是你看到一堆你已经红色的消息,中间没有你的消息(因为它们被添加到视图客户端并且服务器脚本不会给你发回你自己的消息)

表示:

  • 我不在乎消息是否不完全按照实际的插入顺序:假设 A 和 B 正在聊天,实际的真实消息顺序是 BAB,但 A 可能会设置 ABB 的顺序,因为他的视图会在输入时立即更新(这有助于我保持“快速实时”的感觉)
  • 我不在乎是否丢失了某些消息(例如,如果它在有人可以阅读之前就落在了固定的 DB 表边缘)
  • 这个时候我不太关心实际的效率、速度和优化
  • 我知道我应该以不同的方式处理消息插入,添加新记录,然后仅更新 ID 并删除最后一条记录。但如果可能的话,我想保持这种仅更新的方式。

你认为我对这个问题的解释是正确的吗? 如果不是:那会是什么原因?/我该如何解决?如果是:我怎样才能轻松解决这个问题?

如果实际修复相当复杂:在 10 到 20 个用户聊天中,这种怪癖实际发生的可能性有多大?

谢谢

4

1 回答 1

1

我在处理聊天代码时也注意到了这一点,解决方案是将最后一个消息 ID(在 MySQL 中设置为自动增量字段)存储在会话中,并在数据库中搜索 ID 高于该 ID 的消息,而不是使用时间()函数。

if (!$_SESSION['message_id']]) {
// if there isn't a message_id, select the last seven entries in the message list
    $sql = "SELECT messages.message_id, messages.message, users.username FROM (SELECT * FROM messages, users user.user_id = messages.user_id ORDER BY message_id DESC LIMIT 7) as new_tbl ORDER BY message_id ASC";
} else {
// if there is a message_id, select the messages sent since the last entry
    $sql = sprintf("SELECT messages.message_id, messages.message, users.username FROM messages, users WHERE user.user_id = messages.user_id message_id > '%d'", $_SESSION['message_id']);
}

$data = array();
$query = mysql_query($sql);
while ($row = mysql_fetch_array($query)) {
// build the data array from the mysql result and set the message_id session to the id of the last message
    $data[$i] = array('user' => $row['username'], 'message' => $row['message']);
    $_SESSION['message_id'] = $row['message_id'] > $_SESSION['message_id'] ? $row['message_id'] : $_SESSION['message_id'];
    $i++;
}

显然你需要逃避会话!

如果没有 message_id 会话,它会从表中加载最后 7 条消息(按降序排列,然后按升序排列这些消息)。如果有 message_id 会话,它会加载新消息。

在 while 循环中,它构建一个数据数组(我将它作为 JSON 发送到我的脚本)并将 message_id 会话设置为 message_id 行,并进行故障安全检查以确保 message_id 会话最终不会被降低。

SQL 意味着您有一个包含 user_id 和 username 的用户表,以及一个包含 user_id、message_id 和 message 的消息表。

于 2012-05-13T07:57:58.227 回答