2

我有一个流水账。(cPanel 或 DirectAdmin)所以我没有 root 访问权限,我不能使用 exec() 或 shell_exec() 函数(被服务器管理员阻止)。

我知道聊天的最佳方式是套接字编程,但它需要终端命令,例如:

PHP ./server.php

但我无法访问终端。

我进行了太多搜索并找到了一些方法,但我不确定性能,因为它们使用 Javascript 命令setTimeout()连接到聊天数据库或文件。

我想如果我使用setTimeout('refresh()',1000)了一种更好的方法,而不是每 1 秒连接一次聊天数据库。我的方法:

refresh=function()
{
//there is flag in session, true means there is new message from sender (session_set_save_handler)
//check flag value in the session, if its true then refresh chat database
}
setTimeout('refresh()',1000)

我使用了一个标志,因为我不想每 1 秒连接一次数据库,也许没有来自发件人的新消息,但我们强制服务器每 1 秒刷新一次。

我的方法好还是不好?有没有更好的方法来构建没有服务器根访问的聊天脚本?

非常感谢。对不起英语不好

4

3 回答 3

1

当我还在学习Web开发时,我曾经做过一个简单的聊天脚本。您可以使用以下 javascript 函数来制作您的小聊天信使:

每 1 秒后通过 ajax 从 db 获取消息

function fetchMessage()
{
var xmlhttp;
if (window.XMLHttpRequest)
  {
  xmlhttp=new XMLHttpRequest();

  }
else
  {
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    flag_loading=1;
    document.getElementById("chat").innerHTML=xmlhttp.responseText;
    var objDiv = document.getElementById("chat");
    objDiv.scrollTop = objDiv.scrollHeight;
    }
  /*else if (flag_loading=0)
    {
        document.getElementById("chat").innerHTML="loading..."; 
    }*/
  }
xmlhttp.open("GET", "fetchmessage.php", true);
xmlhttp.send();
}

setInterval("fetchMessage()",1000);

服务器端脚本 fetchmessage.php 放在这里

<?php

    session_start();
    include('connect.php');

    $user_id=@$_GET['user_id'];

    $sql="select m.user_name,m.message,m.post_time from message m where m.user_id='".$_SESSION['current_user']['user_id']."' or m.reciever_id='".$_SESSION['current_user']['user_id']."' ";
    $result = mysql_query($sql);
    while($row = mysql_fetch_array($result)) {

        echo "<br><font size=2 color='brown'><b>".$row['user_name'].":</b></font> ".$row['message']."</br>";
        echo "<font size=1 color='blue'>(".$row['post_time'].")</font>";
    }

?>

此功能将您的消息发送给其他用户(与您聊天)

    function addMessage(reciever_id, user_id, message) {

    if (message=='') {

        window.alert("Enter some message.");

    } else if (reciever_id=='none') {

        window.alert("Select any friend.");

    } else {

        var xmlhttp;
        if (window.XMLHttpRequest)
        {
            xmlhttp=new XMLHttpRequest();
        }
        else
        {
            xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }

        xmlhttp.onreadystatechange=function()
      {
      if (xmlhttp.readyState==4)
        {
            document.getElementById('message').value="";
        }
      }

      xmlhttp.open("GET", "addmessage.php?user_id="+user_id+"&message="+message+"&reciever_id="+reciever_id, true);
      xmlhttp.send();

    }
}

服务器端脚本 addmessage.php 放在这里

    <?php
session_start();
include('connect.php');

if (!isset($_SESSION['current_user'])) {
    header("location:index.php?error=Please Login First!");
}

else {
    $user_id=$_GET['user_id'];
    $message=$_GET['message'];
    $reciever_id=$_GET['reciever_id'];

    mysql_query("INSERT INTO message VALUES (NULL, '".$_SESSION['current_user']['user_id']."', '".$reciever_id."', '".$_SESSION['current_user']['user_name']."', '".$message."', NOW())") or die(mysql_error());
}

?>

您还可以使用此脚本检查在线用户/朋友

    function checkOnline() {
    var xmlhttp;
    if (window.XMLHttpRequest)
    {
        xmlhttp=new XMLHttpRequest();
    }
    else
    {
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }

    xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        document.getElementById('online_friends').innerHTML=xmlhttp.responseText;
        //window.alert(xmlhttp.responseText);
    }
  }

  xmlhttp.open("GET", "onlinefriends.php", true);
  xmlhttp.send();
}

setInterval("checkOnline()", 1000);

onlinefriends.php 的服务器端脚本放在这里

    <?php 
    session_start();
    include('connect.php');

    $query_online=mysql_query("SELECT user_name FROM user WHERE user_status=1 AND user_name!='".$_SESSION['current_user']['user_name']."' ") or die(mysql_error());

    while ($row=mysql_fetch_array($query_online)) {
    echo "<font size=4 style='font-style:italic; font-family:Georgia, 'Times New Roman', Times, serif'>".$row[0]."</font><br>"; 
    }
?>

并问我您是否觉得难以实施,我会进一步帮助您:)

于 2013-11-09T14:56:46.700 回答
1

该解决方案肯定会起作用,setTimeout用于每隔一秒左右检查一次新消息。如问题所述,还有其他技术,例如彗星,尽管这些在 PHP 中是不可能的。

下面是一个使用 PHP 并将聊天历史存储在 SQL 数据库中的示例,使用 ajax 函数来获取新的聊天消息:

//Updates the chat
function updateChat(){
  $.ajax({

    type: "GET",
    url: "update.php",
    data: {  
        'state': state,
        'file' : file
        },
    dataType: "json",
    cache: false,
    success: function(data) {

        if (data.text != null) {
            for (var i = 0; i < data.text.length; i++) {  
            $('#chat-area').append($("<p>"+ data.text[i] +"</p>"));
        }

        document.getElementById('chat-area').scrollTop = document.getElementById('chat-area').scrollHeight;

    }  

    instanse = false;
    state = data.state;
    setTimeout('updateChat()', 1);

    },
  });
}

如您所见,最后一行用于setTimeout每 1 秒调用一次函数。

消息由不同的函数单独发送:

//send the message
function sendChat(message, nickname) {       
  $.ajax({
       type: "POST",
       url: "process.php",
       data: {  
                'function': 'send',
                'message': message,
                'nickname': nickname,
                'file': file
                },
       dataType: "json",
       success: function(data){

       },
    });
}

正如我在上面的评论中提到的,使用 PHP 以外的服务器技术有一些优势。大多数 PHP 解决方案使用数据库在对服务器的请求之间保留聊天消息,这会产生比实际需要的更多的服务器开销,node.js 解决方案可以将消息存储在一个数组中,该数组保留在服务器的内存中,并且另外使用套接字参见此处的示例。

编辑 - 在内存中缓存 sql 查询

如果您使用的是 MySQL,则可以在查询前添加注释以暗示查询应缓存在内存中,例如:

$res   = $mysqli->query("/*" . MYSQLND_QC_ENABLE_SWITCH . "*/" . "SELECT message FROM chatroom WHERE id = $roomId");

有关更多信息,请参见示例 1,此处。该示例对查询进行计时,并且可以用作服务器上的基准。

即使代码没有明确要求缓存也可以在服务器上进行,毕竟存储聊天室内容所需的内存量非常小——只有几行文本!我从上面的 Javascript 代码中获取的示例将文本存储在一个文件中,该文件几乎肯定会存储在 Web 服务器的内存中。如果数据库服务器与网站在同一主机上运行,​​那么请求很可能不会导致任何磁盘活动,从而使开销也非常小。webserver / db 连接也可能在内存中而不是套接字中。

最好的解决方案在很大程度上取决于您的服务器设置,最好让某些东西正常工作,然后对其进行优化。我确实从经验中知道node.js解决方案非常快。

编辑 - 回答标记问题

在客户端 Javascript 中设置标志将不起作用,因为其他客户端可以在没有重置当前客户端的标志的情况下提交消息。在服务器上的 PHP 中设置一个标志是很棘手的,不特定于客户端(来自会话或 cookie)的持久变量,并且不存储在数据库中,必须保存在文件中:PHP:持久变量值。该static关键字与 C 或类似语言中的关键字并不完全相同。这是使用的主要优点之一Node.js,持久数组非常容易创建。

一种解决方案是将消息保存为json文件,在收到每条新消息时附加它,然后每秒一次将整个文件返回给用户。该文件可能被限制在 100 行左右,这将确保它保存在某个地方的缓存内存中(ramdisk、OS 磁盘缓存,或者更糟糕的是硬盘本身的硬件缓存)。

于 2013-11-09T14:58:03.360 回答
1

如果您不(或不能)使用套接字,则在特定时间内将每个客户端中的低权重响应 php 页面与自动刷新脚本与 jquery ajax 混合是最好的解决方案。但它的性能很大程度上取决于您的活动聊天页面的总数!

为了获得更好的性能和减少服务器的处理时间,我认为制作自定义会话处理程序以访问所有活动会话是检查是否已收到任何新消息供读者使用的好方法,而不是每次都检查数据库。

并且为了节省带宽,尤其是在高流量时间,您可以使用从 ajax 接收到的响应来更改 JavaScript next setTimeout 延迟。

另一种解决方案是为数据库中的每条消息设置读取标志,以便仅使用 json 发送新消息,而不是在每次更新时重新加载所有消息。

于 2013-11-14T01:04:56.793 回答