2011-08-24 68 views
6

我想用PHP和AJAX编写一个简单的网络聊天应用程序。保持在PHP登录用户的跟踪

我需要知道所有打开的会话,以便我可以显示可以与之通话的在线用户列表。我还需要注意注销,因为我使用“发送方和接收方都处于脱机状态”作为考虑聊天会话终止并删除消息的条件。

我跟踪数据库中的登录用户:在登录时添加条目并在注销时删除它可以正常工作,但它并不全面,因为用户可以通过其他两种方式进行日志记录out:

  • 服务器端会话在非活动状态后过期。
  • 客户端Cookie在浏览器关闭时被破坏。似乎使用某种onclose触发的AJAX是一个坏主意(如果浏览器崩溃或什么?)。

最简单的解决方案似乎是保留上次活动的时间戳。我看到,虽然一些问题是:

  • AFAIK服务器端过期是基于机会,所以这是不准确的(如果我得到的到期时间3分钟错了,那个的3个几分钟路程一些人可能是与离线用户交谈,想知道为什么没有人会回答)
  • 我不得不经常查询数据库以检查每个登录用户上次活动时间与当前时间相比。我没有看到我何时/何地有效地做到这一点。每当需要在线用户列表时,这样做似乎很愚蠢。

任何帮助表示赞赏。我自己编码,因为我不知道任何可以与我现有的用户数据库集成的网络聊天框架,如果我错了,请纠正我。

+0

OK,我意识到其实我并不需要尝试和推测将会时间戳。由于每个用户都在不断地查询新消息,我还可以在每次查询时更新时间戳,并考虑所有时间戳> 20秒的用户不活动。所以现在我只需要弄清楚数据库使用旧时间戳自动删除条目的最佳方式。 – Jonathan

+0

@bfavaretto非常感谢您的注意!程序员似乎是正确的地方要问,会做的。 – Jonathan

回答

0

我不知道,如果你需要两个概率除数但这里是我做自动退出的人:

ini_set('session.gc_maxlifetime',3600); // 1 hour 
ini_set('session.gc_probability',1); // it does garbage cleaning EVERY time 
ini_set('session.gc_divisor',1); // it does garbage cleaning EVERY time 
session_start(); 
+0

我意识到这实际上不是问题,请参阅我对OP的评论。需要弄清楚如何清理旧条目,而且我宁愿每次用户请求在线用户列表时都不会触发清理(看起来像是浪费,应该是集中式的,只是发生在服务器端)。 – Jonathan

1

我不认为你将能够做多大通过关闭浏览器,互联网连接问题等来缓解持续查询以确定用户是否已注销,但您可能可能让每个客户端每5秒向服务器发出一次AJAX请求以更新最后一次活动时间,并且如果您的应用程序错过了3-4次连续请求(即,它们的最后一次活动时间大于20秒),那么您的服务器上的应用程序会将该用户视为“已注销”。

在客户端,您可以在每次客户端向其他用户发送消息时检查最后一次活动时间,并在发生情况时回复他们已注销。如果他们试图与其他用户开通聊天,您也可以立即打电话查看他们的状态。然后你可以每隔30秒检查用户列表中所有用户的状态。这样,如果与他聊天的人意外脱机,您的客户会得到非常快速的反馈。

+0

我的第一部分同意,我觉得这是更有效的让数据库保持跟踪过期时间戳的内部,虽然,因为它是毫无意义的有10个用户同时具有数据库清理过期的时间戳。 我应该使用cron + mysql客户端来做到这一点,还是有更好的方法? – Jonathan

+1

Cron很好,但通过访问数据库的PHP脚本可以更好地运行它,如果您想稍后改变行为,这对您来说会更容易。 – Ravi

+0

我同意Ravi,cron +一个PHP脚本是去这里的路。我假设你已经有权访问crontab,但是如果由于某种原因你没有,你可以设置一个“穷人的cron”函数来跟踪上一次任务已经完成如果$ x秒已过,则执行并再次运行它,并让PHP脚本在每个请求中执行该函数。这是一件很痛苦的事情,并不是很准确,因为你不能确切地确定任务何时执行,但它在一个小小的情况下工作。我必须这样做之前,当我没有访问crontab ... –

0

您当前使用的大部分数据以及您需要的数据都存储在PHP会话中 - 如何从信息中派生用户标识并不明显。

如果您切换到使用数据库绑定会话处理程序,那么这一切都变得非常简单。我有一个快速谷歌 - 有很多例子。但是很多(例如,this one)在回读会话时不检查到期日。 OTOH我链接的示例确实显示将用户ID添加到sesion记录 - 稍后您将需要。所以读会话处理函数应该是这样的:

read: 
SELECT session_data, username 
FROM sessions 
WHERE session_id=' . session_id() . ' 
AND last_updated>= ' . date('YmdHis', time()-ini_get('session.gc_maxlifetime')) 

而且让所有的当前登录的用户:

SELECT username 
FROM sessions 
WHERE last_updated>= ' . date('YmdHis', time()-ini_get('session.gc_maxlifetime')) 

...,让(覆盖)会话的垃圾收集器将自动清除掉冗余数据。

HTH

0

首先区分你的担忧。 就'在线用户列表'而言,你可以使用数据库&,似乎你已经想通了。 (即使有人没有正确注销,显示一些额外的在线用户也不会造成太大的损害)

现在,用于检查用户是否仍在线的聊天应用程序,您将不得不使用ajax。 根本没有别的办法。当然,总是会有黑客攻击,我不知道。 看到图像image当你在这里回答(stackoverflow)。它经常检查时间是否已经过去(&你输入了什么新的)&保存副本。

1

您可以颠倒你的模式,用推送通知系统取代你的Ajax拉动行为。

通过这种方式,您可以实时通知聊天用户登录和注销新聊天成员。我从来没有在实践中做过这样的事情,但我已经阅读过关于这种技术的文章,而且对于像您这样的案例来说这似乎很有趣。

这比ajax pull方式稍微难一些,但是一旦实现了主结构,就可以轻松添加功能,性能会更好。

一些链接,我发现非常有用:

这是一个railscast插曲,其应对的javascript聊天中,实现在轨,但即使如果你不懂铁轨,你应该可以按照它来获得核心概念:http://railscasts.com/episodes/260-messaging-with-faye

+0

感谢这个答案 - 我没有意识到JS的推送通知框架的存在,所以现在我有一些我需要熟悉的新东西。 :-) –

+0

现在我的联盟不在了,但是我还没有意识到这个东西,我一直认为缺乏推动AJAX的一个很大的限制。很高兴知道。 – Jonathan

0

看起来您最关心的是性能,并且您已经了解了实现细节(主要是)。只要将处理会话的表的类型更改为'内存',这会将每个请求的db查询的性能成本降低到几乎没有,因为您是直接从RAM中获取数据。只要确保每次用户明确注销时删除会话,或者将用户标记为非活动状态。

但是不管你做什么事情都需要客户端和服务器之间的持续通信永远不能通过HTTP完成。但是如果你把合理的超时时间等,它将工作99%的时间。

0

我迟到了这次派对,但让我给我两分钱。

在这种特殊情况下,不需要针对不良通知的推送系统。也不需要cron。让我来解释:

说A,B和C是在一个聊天室& B的浏览器崩溃,所以他的会话过期。现在,服务器认为B仍然存在,但她不是。你如何更新A & C?当A & C ASK进行更新时。检查B的最后一个Keepalive时间戳,并确定他们的会话已过期。

如果所有A,B &Ç崩溃,那么这永远不会发生我听到你问。谁在乎?现在没有人看到我们的错误!唯一的缺点是让他们的聊天室保持活跃状态​​,这需要一些数据库空间。这可以在创建另一个聊天会话时清除。

的最后一个要点是:

  1. 保持时间戳的最后一次拍摄的动作。
  2. 通过时间戳使用用户事件循环,并采取了昨日黄花。

在用户的情况下,这将被记录出来。在聊天室的情况下,这将是那些已过期。