Jump to content

[SOLVED] How do I log users out after they have closed the browser?


dpacmittal

Recommended Posts

I know this question has been asked many times and it really doesn't have a nice answer. All people advice is to use timeout. And if people are inactive for some period of time then, log them off. But this doesn't solve my problem.

 

Actually, I am trying to make a chat script in ajax for my website. My website will only be a chat site. I've got to a point where I can send the message, store it in database, then fetch the messages and display them in the browser. I've started to make a user's online list which will show people online in that chatroom. I've made another table for users online which will be used to store who's online in a particular room. When person clicks on logoff link, the row is deleted and he will not be shown on online user's list anymore.

 

But the problem is, if he doesnt click on logoff, and directly closes the browser windows, the row in table doesn't gets deleted and he will be shown online forever.

I can't use timeout because people can remain inactive in a chatroom for hours.

 

What I was thinking is to make ajax send a message to web server every 30 seconds, by which web server would know who's online. If it doesn't receive that message from certain user, it means he has probably closed his browser. The problem is I don't know how to implement this idea. Also there will be a need of a script which would continuously check if the messages are received and delete the appropriate users for the table.

 

My questions:

1) How to implement the idea above?

2) How do I make a script which would be continuously running on a server (do I have to put it in an endless loop. I just cant figure out how to make it run continuously)?

3) Is there any better idea for the job?

4) What method do publicly available chat scripts use for this purpose? I downloaded Blab! which is a well known chat script but couldn't understand how they implemented it?

 

Any help would be greatly appreciated :)

 

Link to comment
Share on other sites

What method do you use for getting new messages to the user?

 

I used POST method to transfer messages to the php file which then stores in a mysql table. Table also has line_id field which auto increments. When user logs in first, the latest line_id is fetched by using SQL MAX function. Ajax GET method is used to call another php file "retrieve.php" which shows latest messages from the given line number to latest line number. The messages are then appended using:

document.getElementById("chatwin")=document.getElementById("chatwin")+httpReq.responseText;

 

Link to comment
Share on other sites

k, so assuming the AJAX call for retrieve.php happens every N seconds...if you put a line in the retrieve.php file that updates a field in your table, to record the last time they were active. then, when other clients get their retrieve.php, only return data where the last active was within N seconds (maybe N+2 seconds just to be safe)

Link to comment
Share on other sites

k, so assuming the AJAX call for retrieve.php happens every N seconds...if you put a line in the retrieve.php file that updates a field in your table, to record the last time they were active. then, when other clients get their retrieve.php, only return data where the last active was within N seconds (maybe N+2 seconds just to be safe)

Thanks for your reply again. Is this what you are trying to say:

I put a code in retrieve.php which would update the timestamp in database table everytime retrieve.php is accessed. When users list is shown, we will only display those user which accessed retrieve.php within last 3-4 seconds.

 

I think this method would be very good. But I also want to delete the users, who have already closed their browser, from the table.. for which i need a script which runs continuously. Do you think it would be wise, if I put the deleting statement too in retrieve.php?

 

Would it create mysql server unstable if I put deleting procedure in retrieve.php. There would be like 1000s of queries per second. Assuming there are 100 users online(There can be many more than that), retrieve.php is called every 2 seconds, retrieve.php would have a message retrieve query(SELECT), timestamp query(UPDATE) and deleting users query(DELETE FROM) and a message sending query(INSERT INTO).

Link to comment
Share on other sites

While the easiest thing to do is to put the DELETE in the retrieve.php file, it would probably be better to put it in a scheduled job that runs every 5 to 10 minutes or so to reduce the number of queries.

 

Also, you may want to look into a couple other options to reduce the number of calls to retrieve.php. This would change everything we just discussed, but I think you would benefit in the long run.

 

Long Poll - Instead of doing an AJAX call every 2 seconds (which is hard on the client and the server) you can do an AJAX call, and have the server keep it open until new data is ready for the client. Then, once the data is received by the client, it immediately does another AJAX call. Sometimes the request will come back immediately, sometimes it will stay open for several seconds. This should average out to less AJAX calls, but the applications should seem more responsive.

 

Persistent Socket - There is a new application out there called Orbited http://www.orbited.org/ their webserver is not responding for me...I hope they still exist...but it's a server side application you install, and it does all the work for opening a persistent connection (like Gmail). I have not used it personally, but I hear good things.

Link to comment
Share on other sites

Thankyou for your reply, once again.

 

As I am a newbie to AJAX, I didn't know about these techniques. I researched about them and I read about long polling and server pushing (Comet) techniques. I think long polling would be better for a chat system and I would like to try it. But the problem is I couldn't find any tutorials for getting started with it.

 

I would be very grateful if you could post a short tutorial or point my to a website offering the same.

Link to comment
Share on other sites

Um, don't really have the time to write a full tutorial, but i'll try to cover the basics real quick.

 

Client Side - Write a JS function that does an AJAX request to the server. The callback should handle the data returned from the server. After processing the data, immediately call the original function again to start another AJAX request.

 

Server Side - The only change you should have to make server side, is putting all your code inside an infinite loop. Inside the loop, check for new data. If there is new data, print it and exit the script. As the last line inside the loop, put a usleep(500); and adjust the 500 to your desires. This will keep the infinite loop from eating up system resources. As another safety precaution, you may want to keep track of how long the loop has been running and return an empty data set after 20 seconds or so. This is to get around request timeouts.

 

Hope that helps. Give a crack at writing the code, and let me know if you have any problems.

Link to comment
Share on other sites

 

Server Side - The only change you should have to make server side, is putting all your code inside an infinite loop.

 

Thanks for your time and reply.

I also thought the same thing about the infinite loop. Thanks a ton. I will try it and let you know what happens.

Link to comment
Share on other sites

I modified my retrieve.php to be:

<?php
session_start();
$id=$_SESSION['id'];
include("connect.php");
$curr_time=time();
$updquery=mysql_query("Update users set lastactive='$curr_time' where user_id='$id'");
$delquery=mysql_query("delete from users where (lastactive+5)<'$curr_time'",$con);
$lastmsg=$_SESSION['lastmsg'];

for($i=0;$i<=20;$i++)
{
$q=mysql_query("Select Max(lineno) from abc");
$row2=mysql_fetch_row($q);
if($row2[0]==$lastmsg)
{
usleep(1000000);
}
else
{
$res1=mysql_query("Select * from abc where lineno>'$lastmsg' and lineno<='$row2[0]' order by lineno ASC");
while($row = mysql_fetch_array($res1))
{
echo $row['line']."<hr width=97% size=1 noshade>";
}
$_SESSION['lastmsg']=$row2[0];
exit();
}
}
?>

 

Firefox loads the page in 10 seconds, the statusbar shows waiting for localhost. IE crashes on loading the page. And most importantly, the script doesn't work. Tell me if you want all the files.

 

Help.

Link to comment
Share on other sites

try this:

 

<?php
session_start();
$id = $_SESSION['id'];
include ("connect.php");
$curr_time = time();
$updquery = mysql_query("Update users set lastactive='$curr_time' where user_id='$id'");
$delquery = mysql_query("delete from users where (lastactive+5)<'$curr_time'", $con);
$lastmsg = $_SESSION['lastmsg'];

while(1){ //Infinite loop
  $q = mysql_query("Select Max(lineno) from abc");
  $row2 = mysql_fetch_row($q);
  //Check for new message
  if ($row2[0] != $lastmsg) {
    $res1 = mysql_query("Select * from abc where lineno>'$lastmsg' and lineno<='$row2[0]' order by lineno ASC");
    while ($row = mysql_fetch_array($res1)) {
      echo $row['line'] . "<hr width=97% size=1 noshade>";
    }
    $_SESSION['lastmsg'] = $row2[0];
    exit;
  }
  //Timeout after 20 seconds
  if(time() - $curr_time >= 20){
    exit;
  }
  usleep(500); //Pause for a half second
}
?>

Link to comment
Share on other sites

Hey rhodesa. Thanks for you reply.

Had some problems with internet, thats why i am replying late.

I got my script to work. I had problems due to usleep(1000000). PHP manual mentioned usleep(microseconds) as syntax. I thought 1second=1000000. Anyways, thanks a ton for teaching me long polling. It works like a charm and yes, my application seems more responsive.

 

Just one another question. I want to make an admin section too. How do I get started with it?

 

Link to comment
Share on other sites

well...question is a little vague. there are lots of options (CMSs, etc) that already have a lot of the functionality of an admin built into them. but with such a custom app, you will probably need a custom admin. what kind of stuff are you looking for in the admin?

Link to comment
Share on other sites

Hey, sorry to bother you again but the long polling isn't working.

 

function retrieve()
{var xhr = new httprequest();
var url = "retrieve.php";
xhr.open("GET",url,true);
document.getElementById("transcript").innerHTML=document.getElementById("transcript").innerHTML+"<br>"+"sent";
xhr.onreadystatechange = function() {
if(xhr.readyState==4 && xhr.status==200)
{var prevtext=document.getElementById("chatwin").innerHTML;
document.getElementById("chatwin").innerHTML=document.getElementById("chatwin").innerHTML+xhr.responseText;
if(document.autoscr.scr.checked==true && document.getElementById("chatwin").innerHTML!=prevtext)
{
var objDiv = document.getElementById("chatwin");
objDiv.scrollTop = objDiv.scrollHeight;

}}
}
xhr.send(null);
}

function startRetrieve()
{
setInterval("retrieve()",100);
setInterval("user_fetch()",1000);
}

 

I've written

document.getElementById("transcript").innerHTML=document.getElementById("transcript").innerHTML+"<br>"+"sent";

just to check at what interval the request is being sent.

I notice that its being sent at just the interval I've mentioned ie. 0.1 second. It means that the long polling isn't working. How do I make another request as soon as the previous one is completed. I tried calling the function recursively but it crashes the browser(both firefox and ie).

 

Help needed.

Link to comment
Share on other sites

You don't want to use any setTimeouts or setIntervals now. you are going to turn it into a recursive function. So, you run the AJAX once, and when the AJAX is done, it calls itself again.

 

also, just to remind you...retrieve.php should only return new messages.

 

function retrieve()
{
  var xhr = new httprequest();
  var url = "retrieve.php";
  xhr.open("GET",url,true);
  document.getElementById("transcript").innerHTML += "<br>sending request";
  xhr.onreadystatechange = function()
  {
    if(xhr.readyState==4 && xhr.status==200)
    {
      document.getElementById("transcript").innerHTML += "<br>data received";
      var objDiv = document.getElementById("chatwin");
      objDiv.innerHTML += xhr.responseText;
      objDiv.scrollTop = objDiv.scrollHeight;
      retrieve(); //Send it again
    }
  }
  xhr.send(null);
}
//This will start it when the page loads
window.onload = function(){
  document.getElementById("transcript").innerHTML += "<br>page is loaded";
  retrieve();
}

Link to comment
Share on other sites

The above script works. Thanks. I was calling retrieve() outside the if condition.

 

Now, I've got a new problem. When I send a message, it gets inserted into the database only after the execution of retrieve.php is complete. Meaning that, it isn't executing two php files simultaneously. What is the problem here?

 

I checked that the function for sending message was being called but it got stuck on readystate=0. Only when execution of retrieve.php is complete, readystate changes and message gets sent.

If you need whole code and sql dump file, let me know. This is giving me a lot of headache.

Link to comment
Share on other sites

are you sharing the same xhr object for sending messages and getting messages? you will run into a problem there. my suggestion is to just use a library like jQuery to handle all the ajax work for you

no i am using separate objects for each request. Yeah.. will try my hand at jQuery.

Link to comment
Share on other sites

Hey I tried jQuery. Converted all my ajax request to use jQuery but I got same result.

 

My query which retrieves new messages is holding up other ajax queries. I mean that, other queries wait for the completion of retrieving query before they themself get executed.

Donno whats the problem.

Help.

Link to comment
Share on other sites

hum...one shouldn't block the other. run this as a test, it should keep getting updates every 3 seconds even if you click the "Send Message" button:

 

<?php
  if($_SERVER['REQUEST_METHOD'] == 'POST'){
    if($_POST['action'] == 'message'){
      print "Message received";
    }elseif($_POST['action'] == 'update'){
      sleep(3);
      print "Here are some updates";
    }
    exit;
  }
?>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"></script>
<script type="text/javascript">
  function doUpdate ( ) {
    $('#output').append("Sending Request for updates<br />");
    $.post('<?php echo $_SERVER['PHP_SELF']; ?>',{action:'update'},function(data){
      $('#output').append("Update Received: " + data + "<br />");
      doUpdate();
    });
  }

  function sendMessage ( ) {
    $('#output').append("Sending a message<br />");
    $.post('<?php echo $_SERVER['PHP_SELF']; ?>',{action:'message'},function(data){
      $('#output').append("Message sent: " + data + "<br />");
    });
  }
  $(function(){
    doUpdate();
  });
</script>
<input type="button" value="Send Message" onclick="sendMessage();" />
<div id="output"></div>

 

what OS/Browser are you testing in?

Link to comment
Share on other sites

Your script is working fine.

I have tested my script on Firefox 3, IE 7, Opera 9 and Google Chrome.

I tried to debug it. I found that the function for sending message is being called but its readystate is not changing to 4. It only changes when the execution of retrieve.php has completed. The problem, I think, is not at the client side, but at the server side. I am using xampp.

Do you think trying it on another server may help or maybe changing some php.ini settings?

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.