За последние 24 часа нас посетили 8452 программиста и 518 роботов. Сейчас ищут 216 программистов ...

Счетчик онлайна на php с глюком

Тема в разделе "Прочие вопросы по PHP", создана пользователем Dr.And, 20 дек 2012.

  1. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Помогите пожалуйста, кто силен в php, подскажите, что в коде не так.
    Вот нашел один вариант (код не мой), работает с сессиями. Но проблема в том, что в файле "base_sessions.dat"
    появляются пустые строки и подсчет идентификаторов сессий идет с ошибками.
    Вернее не пустые строки а куча пробелов в одной строке перед идентификатором.
    Код (Text):
    1. s0k34bjf6vsu13jddse8im8vo2|1356014950
    2. nkogriq3tcue2htkcmo19i3ou2|1356015110
    3. (здесь куча пробелов) k0qnpari2vk22053hua63glhr0|1356015110
    4. jpcpm1pil0jtot0b28f8m83vu6|1356015110
    5. tmo9ua7utvlg4ialegu27tapd6|1356015110
    6. hg69qmhghf3hmvjfmfnfp19cg1|1356015110
    А поскольку показатели каждые 10 сек. обновляются аяксом, то на странице показатели
    счетчика "прыгают".
    Может кто-то поможет найти причину появления этих пробелов?
    И собственно код счетчика:
    Код (Text):
    1. <?php
    2. session_start();
    3. //выделяем уникальный идентификатор сессии
    4. $id = session_id();
    5. if ($id!="")
    6. {
    7. //текущее время
    8. $CurrentTime = time();
    9. //через какое время сессии удаляются
    10. $LastTime = time() - 60;
    11. //файл, в котором храним идентификаторы и время
    12. $base = "base_sessions.dat";
    13.  
    14. $file = file($base);
    15. $k = 0;
    16. for ($i = 0; $i < sizeof($file); $i++) {
    17. $line = explode("|", $file[$i]);
    18. if ($line[1] > $LastTime) {
    19. $ResFile[$k] = $file[$i];
    20. $k++;
    21. }
    22. }
    23.  
    24. for ($i = 0; $i<sizeof($ResFile); $i++) {
    25. $line = explode("|", $ResFile[$i]);
    26. if ($line[0]==$id) {
    27. $line[1] = trim($CurrentTime)."\n";
    28. $is_sid_in_file = 1;
    29. }
    30. $line = implode("|", $line); $ResFile[$i] = $line;
    31. }
    32.  
    33. $fp = fopen($base, "w") or die ("Нет доступа к базе данных");
    34. for ($i = 0; $i<sizeof($ResFile); $i++) { fputs($fp, $ResFile[$i]); }
    35. fclose($fp);
    36.  
    37. if (!$is_sid_in_file) {
    38. $fp = fopen($base, "a");
    39. $line = $id."|".$CurrentTime."\n";
    40. fputs($fp, $line);
    41. fclose($fp);
    42. }
    43. }
    44. echo "Сейчас на сайте: ".sizeof(file($base))." чел.";
    45. ?>
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    гибельная технология ) каждые 10 сек читать/писать в файл каждым клиентом это не тру вэй. да еще без блокировки.

    если разобраться, раз уж ты открываешь сессию, то у тебя УЖЕ есть вся информация: в папке из настройки session.save_path лежат сессионные файлы. по одному на каждое подкючение. смотри время последнего изменения каждого файла и делай выводы.

    справка по php подскажет тебе все недостающие детали.
     
  3. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Блокировку добавить можно (в закоментированном виде добавил)... Но это не решает проблемы...
    Код (Text):
    1. session_start();
    2. //выделяем уникальный идентификатор сессии
    3. $id = session_id();
    4. if ($id!="")
    5. {
    6. //текущее время
    7. $CurrentTime = time();
    8. //через какое время сессии удаляются
    9. $LastTime = time() - 60;
    10. //файл, в котором храним идентификаторы и время
    11. $base = "base_sessions.dat";
    12.  
    13. $file = file($base);
    14. $k = 0;
    15. for ($i = 0; $i < sizeof($file); $i++) {
    16. $line = explode("|", $file[$i]);
    17. if ($line[1] > $LastTime) {
    18. $ResFile[$k] = $file[$i];
    19. $k++;
    20. }
    21. }
    22.  
    23. for ($i = 0; $i<sizeof($ResFile); $i++) {
    24. $line = explode("|", $ResFile[$i]);
    25. if ($line[0]==$id) {
    26. $line[1] = trim($CurrentTime)."\n";
    27. $is_sid_in_file = 1;
    28. }
    29. $line = implode("|", $line); $ResFile[$i] = $line;
    30. }
    31.  
    32. $fp = fopen($base, "w") or die ("Нет доступа к базе данных");/* flock($fp,2); */
    33. for ($i = 0; $i<sizeof($ResFile); $i++) { fputs($fp, $ResFile[$i]); }
    34. fclose($fp);
    35.  
    36. if (!$is_sid_in_file) {
    37. $fp = fopen($base, "a");
    38. $line = $id."|".$CurrentTime."\n";
    39. fputs($fp, $line);/* flock($fp,3); */
    40. fclose($fp);
    41. }
    42. }
    43. echo "Сейчас на сайте: ".sizeof(file($base))." чел.";
     
  4. Dmitriy A. Arteshuk

    Dmitriy A. Arteshuk Активный пользователь

    С нами с:
    19 янв 2012
    Сообщения:
    2.441
    Симпатии:
    62
    Адрес:
    Зеленоград
    да вообще, хранить эти вещи в файлах....жесть какая-то ((((

    заведи простую табличку в БД и считай.
     
  5. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    так и в базе данных тоже скорее всего возникнет та же проблема пробелов...
     
  6. Dmitriy A. Arteshuk

    Dmitriy A. Arteshuk Активный пользователь

    С нами с:
    19 янв 2012
    Сообщения:
    2.441
    Симпатии:
    62
    Адрес:
    Зеленоград
    откуда ей взяться то?

    у тебя будет 3 поля:

    id - session_id - date_time

    тут пробелам взяться неоткуда...
     
  7. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    этот скрипт в принципе не стоит отлаживать. дичь просто. скачай любой движок форума и поищи там слово online — технология отлажена отцами и дедами )))

    Добавлено спустя 2 минуты 8 секунд:
    суть сводится к одному запросу: SELECT count(*) FROM `online` WHERE `last_activity` > :t
    базы данных такие базы данных!
     
  8. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    У меня сайт с онлайн радио, люди долго сидят на страницах, поэтому `last_activity`в пролете.
    Именно поэтому и обновляю сам скрипт аяксом, что бы имитировать активность пользователя.
     
  9. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    ок, твой аякс будет "трогать" запись в базе. устраивает?
     
  10. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    А с базами данных я никогда не работал, надо еще изучить матчасть :(
    А если бы умел сделать, как вы предлагаете - то бесспорно устроило бы...
     
  11. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    когда-то надо начинать. сам понимаешь, без баз никуда! пробуй, а мы поможем. я бы посоветовал отложить строительство велосипедов ненадолго. таки возьми движок форума и покопайся во внутренностях. найдешь много полезного.
     
  12. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Ок, движок посмотрю.
     
  13. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    fluxbb.org рекомендую. в нём мало кода ))) простой и эффективный.
     
  14. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Скачал, лезу ковырять код...

    Добавлено спустя 21 минуту 49 секунд:
    Блин, нифига наковырять не могу. Наверно проще на free-lance.ru обратиться...
     
  15. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.560
    Симпатии:
    142
    рефакторинг
    Код (PHP):
    1. $base = "base_sessions.dat"; //файл, в котором храним идентификаторы и время
    2. $LastTime = time() - 60; //через какое время сессии удаляются
    3. touch($base);
    4. $file = file($base);
    5. $id = session_id(); //выделяем уникальный идентификатор сессии
    6. if ($id!='') {
    7.   $ResFile = array();
    8.   foreach($file as $line) {
    9.     list($sid, $utime) = explode('|', $line);
    10.     if ($utime > $LastTime) {
    11.       $ResFile[$sid] = trim($sid).'|'.$utime.PHP_EOL;
    12.     }
    13.   }
    14.   $ResFile[$id] = trim($id).'|'.time().PHP_EOL;
    15.   file_put_contents($base, $ResFile, LOCK_EX);
    16.   $file=$ResFile;
    17. }
    18. echo "Сейчас на сайте: ".sizeof($file)." чел."; 
     
  16. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Спасибо. Код работает, записывает идентификаторы сессий в файл через строчку.
    Но случается, что между идентификаторами пустой строчки нет и опять подсчет не корректный.
    Как следствие - те же "скачки" показателей счетчика...
    Пример записей в файле .dat
    k5kosmssqce1g9um2ihc08gqv6|1356080571

    q3df1lnscdgltbka9jt1au25o7|1356080571

    7p1ufgaia5i1vuqsoa3tv825h7|1356080571

    t98hg3cs4cihtcq8j8ffj7g7q2|1356080571

    443127n9gbnncvb9hlg444ff87|1356080571

    7vtn09a2mk7u5q9lid9ntb77g5|1356080578
    naf7cbpajou2fg2a6ftvqgkio0|1356080571

    j4g66hf4i9fakrookrvjv1fn96|1356080571

    elpvcvf4vi4cg0fdg2b2r747p2|1356080572

    ibdmdkqbl4dfs1d0gud8cfd9m7|1356080572

    p59fgemlih4ep7vnv6hvh7lah1|1356080572
     
  17. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.560
    Симпатии:
    142
    замените
    $ResFile[$sid] = trim($sid).'|'.$utime.PHP_EOL;
    на
    $ResFile[$sid] = trim($sid).'|'.trim($utime).PHP_EOL;

    Добавлено спустя 1 минуту 12 секунд:
    писал на коленке. интерпретировал в голове) похоже упустил и пролезают лишние переводы строк
     
  18. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Идентификаторы стали писаться в файл уже по порядку, пустых строк нет.
    Но показатели счетчика по-прежнему "прыгают". Возможно это происходит из-за не корректной
    блокировки во время одновременного доступа к файлу .dat
    При онлайне 30-40 чел. - все нормально, но толко онлайн доходит до 100 и дальше - начинаются "прыжки".
     
  19. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    OK, раз вы такие упертые, вот вам моя версия БЕЗ использования базы. Технология как я выше описал.

    Это ajax-скрипт, который должен вернуть "онлайн 2 пользователя". ajax.php
    Код (PHP):
    1. <?php
    2.  
    3.  
    4. $dir     = ini_get('session.save_path');
    5. $now     = time();
    6. $timeout = 60;
    7.  
    8. $_SESSION['var'] = $now; // just write simething to THIS session file
    9.  
    10. $count = 0;
    11. foreach (glob($dir . '/sess_*') as $filename) {
    12.     if (filemtime($filename) > ($now - $timeout)) {
    13.         ++$count;
    14.     }
    15. }
    16. echo "Online: {$count} user(s)"; 
    Это произвольная страничка, из которой есть обращение к нашему скрипту, index.html
    Код (Text):
    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4.   <meta charset="utf-8">
    5.   <title>Simplified example of &quot;Users online&quot;</title>
    6.   <script src="/js/jquery.js"></script>
    7.  
    8.   <script>
    9.     $(document).ready(function () {
    10.       var info = $('#info');
    11.  
    12.       function getAndShowCount() {
    13.         $.get('/ajax.php', function(data) { info.html(data) });
    14.       }
    15.  
    16.       setInterval(getAndShowCount, 10000);
    17.     });
    18.   </script>
    19.  
    20. </head>
    21. <body>
    22.   <h1>Hello!</h1>
    23.   <p id="info">Wait...</p>
    24.   <p id="lorem">
    25.     Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    26.     Mauris nec nisi leo, at rutrum nulla.
    27.   </p>
    28. </body>
    29. </html>
     
  20. Your

    Your Старожил

    С нами с:
    2 июл 2011
    Сообщения:
    4.075
    Симпатии:
    6
    Гиблое дело... =)
    Пора включать мозги)
     
  21. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Спасибо. Ваш способ для показа онлайна на всем сайте - работает на ура.
    Только вот похоже данные он берет с папки /var/www/user/data/mod-tmp, где лежат идентификаторы всех пользователей сайта. Мне же надо показывать онлайн на определенных страницах сайта по отдельности. Каким образом я смогу узнавать на какой странице сидит пользователь определенной сессии ?...
    Не удивляйтесь, если я с вашей точки зрения пишу ересь, я самоучка и далеко не все понимаю...
    Опыт и умения приходят со временем.
     
  22. Dmitriy A. Arteshuk

    Dmitriy A. Arteshuk Активный пользователь

    С нами с:
    19 янв 2012
    Сообщения:
    2.441
    Симпатии:
    62
    Адрес:
    Зеленоград
    пытаюсь забукмарить вариант artoodetoo

    viewtopic.php?f=2&t=43048&sid=f32db7dd6df0b797cbb4f9542f8daa72#p344345

    прикольно, как мне показалось, попробовал локально, все гуд, но на серваке...

    Код (Text):
    1. $dir     = ini_get('session.save_path'); // /var/lib/php5
    при этом
    Код (Text):
    1. var_dump (glob($dir . '/sess_*')); // bool(false)
    устанавливаю папке принудительно
    Код (Text):
    1. session_save_path("/tmp");
    имеем 54 элемента массива, но они все с одним временем 1355349548

    где косячу?
     
  23. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.560
    Симпатии:
    142
    чтение каталога с файлами сессии имеет ряд недостатков.
    1. там может лежать очень много файлов, так как сборщик удаляет файлы с определенной вероятностью. и скрипт будет каждый раз лопатить эти сотни или даже тысячи файлов. очень неоптимально
    2. настройки сессии могут разниться от сервера к серверу. например есть понятие "глубина размещения файлов" , тогда надо будет лопатить рекурсивно еще и поддиректории.. для каждого запроса
    3. посчитать просто кол-во онлайн юзеров только полбеды. зачастую нужно отображать их список(логинов) да еще посчитать гостей(незалогиненных). тут это вообще нереализуемо

    в общем хлипкое решение.
    тогда уж лучше в БД или в всеже в файле
     
  24. Dr.And

    Dr.And Активный пользователь

    С нами с:
    6 июн 2012
    Сообщения:
    23
    Симпатии:
    0
    Самым правильным решением будет удалить к черту счетчик с сайта и не парить мозг ни себе, ни форумчанам. :)
     
  25. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    9.291
    Симпатии:
    713
    Адрес:
    из России с любовью
    я просто разминался ;)
    пример не претендует ни на что. разве что он получше онанизма с впиливанием строчек в единый файл.
    однозначно БД подойдет лучше.

    1. это ты от меня узнал? ;) заодно можно собрать мусор. +файловая система кешируется операционкой (поэтому сессии не так сильно тормозят как должны по идее).
    2.это какая-то совсем гипотетическая возможность, стандартный сборщик мусора кажется не умеет с этим работать. я не видел ни разу.
    3. согласен. но не будем бежать впереди топикстартера.