简单的PHP聊天怪癖

时间:2012-05-12 23:41:43

标签: php jquery mysql chat

我一直致力于快速简单的jQuery / PHP聊天,放在我的网站上供访问者进行交流。我估计有200个同步网站用户(连接用户)的峰值,其中最多有10-20人实际聊天。

这是怪癖:

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

尽量保持聊天系统尽可能简单我想出了这段代码:


HTML CODE:

<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 CODE:

发送消息:

$(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表上执行UPDATE语句的seuqence覆盖第一条记录上的新数据,然后用前一条记录覆盖每条记录(以便最后一条记录最终丢失)。

发送消息:

    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编码,该编码包含在最后一条已知消息之后插入的数据库中的所有记录,并且其作者不是查询客户端。


我的嫌疑人:

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

结果是你看到一堆消息你已经红了没有你的消息(因为它们被添加到视图客户端而服务器脚本没有发回你自己的消息)

说明:

  • 我不在乎消息是否完全不符合实际的插入顺序:假设A和B正在聊天,实际的真实消息顺序是BAB,但是A可能会看到ABB为他的视图立即更新的顺序在输入时(这有助于我保持'快速实时'的感觉)
  • 我不在乎某些消息是否丢失(如果它丢失在固定的数据库表边缘,然后有人可以阅读它)
  • 目前我并不关心实际效率,速度和优化
  • 我知道我应该以不同的方式处理邮件插入添加新记录,然后仅更新ID并删除最后一条记录。但如果可能的话,我想保留这种仅限UPDATE的方式。

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

如果实际修复相当复杂:实际可能发生的情况是10-20个用户聊天中的这个怪癖?

由于

1 个答案:

答案 0 :(得分:1)

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

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的消息表。