Выход пользователя из веб-сайта, когда он уложил компьютер - PullRequest
11 голосов
/ 10 марта 2020

Это странно. У нас есть Laravel веб-сайт, и на указанном сайте у нас есть таймер на пользователя, где они получают 15 минут неактивности перед загрузкой.

Мы делаем это с помощью таймера, который находится на странице в реагировать компонент, он работает так, как мы хотим, но теперь у нас есть новая проблема: если пользователь вошел в систему и закрыл крышку своего ноутбука, веб-сайт должен загрузить их. Это делают банки, школы и университеты, правительственные сайты тоже. Так что это возможно, просто не знаю, как.

Мы используем веб-сокеты, используя библиотеку laravel -websockets и Echo. Я хотел бы, чтобы это произошло:

  • После того, как вы закроете свой ноутбук, загрузитесь к экрану входа в систему. Поэтому в следующий раз, когда вы откроете ноутбук и войдете в систему, вы увидите браузер на экране входа в систему. Это не должно происходить так быстро, но нам нужен способ отправить что-то внешнему интерфейсу, в основном, сказать им, чтобы они обновили sh страницу, как только сеанс завершен, мы устанавливаем время жизни сеанса на laravel из 15 минут.

Некоторые люди предлагали в других подобных вопросах:

Наиболее популярным из них является использование веб-сокетов, прослушивание для пользователя, чтобы отключить и затем загрузить их, что нормально и все, но как тогда вы отправляете запрос в браузер, который приостановлен, чтобы затем загрузить их?

Я нашел requestIdleCallback () Но опять же, я не думаю, что это то, чего я хочу, если у меня уже есть таймер сердцебиения на сайте. Это также не работает во всех браузерах.

Я очень растерялся здесь о том, как выполнить sh это, пример, который я могу привести:

Войдите в свой банк, введите ваш компьютер, чтобы спать, подождите 15-20 минут, разбудите компьютер, войдите в систему и увидите, что ваш банк теперь показывает вас на экране входа в систему. Вот чего я хочу . Но я не знаю, как это сделать sh.

Вы не можете отправлять события в "спящий" браузер с бэкэнда, и хотя да, это должно быть бэкэнд-решение, как это сделать? затем вы обновляете интерфейс, чтобы они появлялись на экране выхода из системы, когда они пробуждают ноутбук или компьютер?

Ответы [ 6 ]

0 голосов
/ 20 марта 2020

Я выполнил точно такое же требование, используя AWS Cognito, с Lambda Authorizers & Redis, я не могу поделиться кодом на этом этапе, но я могу рассказать вам все, как он реализован с этими компонентами, те же концепции может использоваться с другими компонентами, не относящимися к AWS.

Во-первых, при реализации выхода из неактивности вам придется делать это на стороне сервера, как если бы кто-то просто выключил свой компьютер, интерфейсный веб-сайт не регистрировал бы журнал. их. Я использовал концепцию ACTIVE пользователей. Когда пользователи успешно проходят аутентификацию, я сохраняю в Redis с TTL 15 минут запись с ключом их username и значением ACTIVE (это может быть имя пользователя + sessionid, если вы хотите разрешить несколько сеансов для данного пользователя в то же время).

В моих пользовательских Авторизаторах, когда пользователь ACTIVE и у него есть действующий токен, я даю им доступ к защищенному ресурсу И, что наиболее важно, я делаю еще один вклад в Redis с помощью username & ACTIVE.

Всякий раз, когда пользователь выходит из системы, я выхожу из него в своем решении для управления идентификацией (Cognito) и помечаю их как INACTIVE. Обратите внимание, что если пользователь не нажмет API в течение 15 минут, у него больше не будет записи ACTIVE против его имени пользователя, и он больше не сможет получить доступ к API, и ему придется снова войти в систему, для чего он будет перенаправлен. сделать.

При таком подходе нужно учитывать много вещей, во-первых, Авторизаторы часто кешируют результаты в течение некоторого времени, и если, скажем, вы кешируете результат в течение 5 минут в качестве примера, то вы, пользователь, можете выйдите из системы через 10 минут, так как ваш пользователь может использовать кеш вместо Авторизатора, который не обновит sh запись ACTIVE.

Также важно, чтобы вы сохранили все, что используете для хранения, если данный пользователь имеет ACTIVE высокую доступность и быстро восстановится в случае сбоя.

Подход с использованием хранилища кеша таким способом аналогичен тому, как аннулирование токена модифицируется для протоколов авторизации без сохранения состояния, таких как OAuth2 .

Мы использовали этот подход уже несколько месяцев, кажется чтобы работать нормально для нас, это может быть чем-то вроде раздражающего требования, я ожидал в мире AWS, что для этого будет готовое к использованию готовое решение, но не о чем говорить .

0 голосов
/ 20 марта 2020

Я думаю, у меня есть идея, вы много обсуждали о том, как работает система входа / выхода из банка.

Случай 1: Доступ пользователя к веб-странице в течение неограниченного времени, если пользователь активен

Каждый раз, когда пользователь входит в систему, запускайте таймер на своем бэкэнде (устанавливайте ограничение по времени, как хотите), скажем, 15 минут. Теперь, что это значит? Это означает, что если пользователь не выполняет каких-либо действий на веб-странице, то мы его выполним.

Теперь спереди вы можете отправить активность пользователя на свой сервер (можно отправить с помощью сокета или длительный опрос), который в основном сбрасывает таймер, и пользователь может активно использовать веб-страницу в любое время.

Если пользователь переведет свой P C в спящий режим, таймер не будет сброшен, и вы можете аннулировать Сеанс таймера заканчивается.

Если вы хотите сделать недействительным сеанс пользователя, как только он переведет свой p c в спящий режим, вы можете установить ограничение времени проверки сеанса. Например, когда пользователь входит в систему, мы создадим сеанс, который будет действителен только в течение 10 секунд, и как только мы получим запрос активности пользователя, мы можем сбросить таймер и предоставить новый ключ сеанса.

Я надеюсь, это поможет вам. Дайте мне знать, если у вас есть какие-либо вопросы.

0 голосов
/ 19 марта 2020

Я написал скрипт, чтобы определить, перешел ли компьютер в спящий режим. Идея в том, что когда машина находится в спящем настроении, все сценарии будут остановлены. Поэтому, если мы будем отслеживать текущее время в течение timeInterval. Каждый раз, когда timeInterval запускает текущее время минус (-), новое время должно быть достаточно близко к timeInterval. Поэтому, если мы хотим проверить, не работал ли таймер в течение X времени, мы можем проверить, больше ли разница во времени, чем X.

Пример проверки удара, если компьютер был переведен в спящий режим более чем на 15 секунд. Обратите внимание, что, когда вы переводите компьютер в спящий режим, потребуется еще 15 секунд, чтобы представить все процессоры. (При тестировании на MY P C).

(function() {
    this.SleepTimer = function() {
        // console.log('sleep timer initiated');
        // Create global element references
        this.sleepTimer = null;
        this.maxTime = null;
        this.curDate = null;
        this.newDate = null;
        this.timer = null;
        this.timeInterval = 1000;

        this.sleepTimer = new CustomEvent("sleepTimer", {
		    "detail": {
		    	"maxTime":this.maxTime,
				"idelFor": this.newDate - this.curDate,
				"timer": this.timer
			}
		});

        // Define option defaults
        var defaults = {
            maxTime: 10000,
            timeInterval: 1000,
            autoStart: true,
            console: false,
            onStart: null,
            onIdel: null
        }
        // Create options by extending defaults with the passed in arugments
        if (arguments[0] && typeof arguments[0] === "object") {
            this.options = extendDefaults(defaults, arguments[0]);
        }
        if (this.options.timeInterval) {
            this.timeInterval = Math.max(1000, this.options.timeInterval);
            this.maxTime = Math.max(this.options.maxTime, 10000);
        } else {
        	this.options = defaults;
        }

        if(this.options.autoStart === true) this.start()
        // Utility method to extend defaults with user options
        
    }
    function extendDefaults(source, properties) {
        var property;
        for (property in properties) {
            if (properties.hasOwnProperty(property)) {
                source[property] = properties[property];
            }
        }
        return source;
    }
    SleepTimer.prototype.start = function(){
        var _ = this;
    	this.options.onStart()
        this.curDate = Date.now();

        this.timer = setInterval(function() {
            _.newDate = Date.now();
            var diff = _.newDate - _.curDate;

            // for debugging
            if(_.options.console && diff > _.timeInterval){
            	console.log('Your PC was idel for ' + diff / 1000 + 's of ' + _.maxTime /1000 + 's. TimeInterval is set to ' + _.timeInterval / 1000 + 's');
            }
            
            if (diff < _.maxTime) {
                _.curDate = _.newDate;
            } else {
            	_.options.onIdel();
                // alert('You have been idle for ' + diff / 1000 + 's');
                clearTimeout(_.timer);
            }
        }, this.timeInterval); // seconds
    }
}());

var sleepTimer = new SleepTimer({
	maxTime: 15000,
	console: true,
	onStart: function(){
		console.log('sleepTimer started.');
	},
	onIdel: function(){
		alert('Your session expired! Please login again.');
	}
});
0 голосов
/ 18 марта 2020

Во-первых, давайте подробнее рассмотрим, почему банковские сайты выходят из системы через 15 минут без активности Это требование PCI для обеспечения безопасности.

Требование PCI-DSS 8.1.8 :

8.1.8 Если сеанс простаивал более 15 минут потребуйте от пользователя повторной аутентификации для повторной активации терминала или сеанса.

Для достижения этого решение на самом деле гораздо более примитивно, чем вы думаете оно должно быть , Он не требует использования веб-сокетов и не знает ничего о состоянии компьютера клиента (в спящем режиме, в режиме бодрствования или иным образом). Все, что требуется, это знать время между текущим запросом, использующим этот сеанс, и последним запросом, использующим тот же сеанс, и убедиться, что они не превышают 15 минут. Если они - пользователь, должен быть повторно аутентифицирован. Если это не так, вы можете продолжить работу с запросом.

Сообщение "время ожидания истекло"

Возможно, вы задаетесь вопросом (если это так просто), как сообщение о времени ожидания истекло появляются, когда вы переводите компьютер в спящий режим и снова включаете его. Эта часть обманчиво проста.

Когда компьютер переводится в спящий режим, браузер фактически отключает все соединения TCP / IP, что, в свою очередь, отключает событие l oop в движке javascript. Так что таймеры не работают. Но когда браузер снова просыпается, он пытается обновить sh некоторые вещи, включая саму страницу. Поэтому, когда страница обновляется, запрос возвращается на сервер, вызывая сервер, чтобы потребовать повторной аутентификации пользователя.

Однако это не будет учитывать модальное сообщение javascript (если это то, что вы Вы имеете в виду), что делают некоторые банковские сайты. Также не все браузеры делают жесткую ссылку sh на странице во всех сценариях ios. Таким образом, можно использовать другой подход. Вместо того чтобы иметь таймер в браузере, который истекает через 15 минут, вы можете просто сохранить время загрузки страницы в javascript в качестве временной метки и иметь интервал времени в 1 секунду, который сравнивает эту временную метку с текущей временной меткой компьютера. Если они находятся на расстоянии более 15 минут, сеанс должен быть прерван.

window.onload = function() {

    sessionStart = Date.now();
    timer = setInterval(function() {
        if (Date.now() - sessionStart > 15 * 60 * 1000) {
            clearTimeout(timer);
            alert("Session Timed out!");
            window.location = "http://www.example.com/login";
        }
    }, 1000);


};

Даже если компьютер переходит в спящий режим и таймер останавливается, сеанс в конечном итоге истекает на стороне сервера ( подробности см. в разделе ниже ), и когда компьютер снова включается, таймер с интервалом в 1 секунду, в конечном счете, снова запускается, вызывая сообщение (как будто время ожидания пользователя истекло, пока компьютер спал). Время, потерянное между временем, в течение которого компьютер перешел в спящий режим, и временем, в течение которого компьютер просыпается, не будет иметь значения, поскольку отметка времени останется в памяти. Разрыв между клиентом и сервером не важен, потому что им не нужно передавать эту информацию для правильного завершения сеанса на стороне сервера. Сервер может выполнить свою собственную сборку мусора и завершить сеанс без связи с клиентом (т. Е. асинхронно ).

Хотите верьте, хотите нет Банки не заботятся об активности внутри клиента. Они заботятся только об активности запросов к серверу. Поэтому, если вам интересно, как они поддерживают сеанс в течение более 15 минут, когда пользователь находится на одной и той же странице в течение такого длительного времени, он просто отправляет запрос AJAX в фоновом режиме, чтобы обновить sh сеанс после запроса пользователю, если он все еще хочет продолжить.

Это можно сделать в том же onload обратном вызове события, который мы использовали ранее, например:

window.onload = function() {

    sessionStart = Date.now();
    timer = setInterval(function() {
        if (Date.now() - sessionStart > 10 * 60 * 1000) {
           if (confirm("Your session is about to timeout. Do you wish to continue?")) {
                // send ajax request to refresh session TTL here
                // reset the timer
                sessionStart = Date.now();
            }
        } else if (Date.now() - sessionStart > 15 * 60 * 1000) {
            clearTimeout(timer);
            alert("Session Timed out!");
            window.location = "http://www.example.com/login";
        }
    }, 1000);


};

Обработка завершения сеанса на стороне сервера

Для обработки завершения сеанса на стороне сервера существует несколько подходов. В зависимости от того, какой из них вы используете, вам понадобятся разные тактики. Один использует обработчик сеанса по умолчанию PHP и устанавливает срок действия session.max_lifetime через 15 минут (это полностью удаляет данные сеанса на стороне сервера, тем самым аннулируя готовку клиента ie).

Если вы позволите механизму обработчика сеанса по умолчанию делать это, вы можете столкнуться с проблемами в зависимости от того, какой обработчик используется (файлы, memcached, redis, custom и т. Д. c).

С файлами (по умолчанию) обработчик) сборка мусора происходит одним из двух способов:

  • Большинство систем на основе Debian выполняют свою собственную G C через задание cron (что отлично подходит для вашего сценария)
  • Другие дистрибутивы позволяют PHP стандартному механизму G C обрабатывать его, что основано на вероятностном c результате каждого входящего запроса к PHP, который проверяет файлы mtime на файлах сеансов и удаляет их после session.max_lifetime. Проблема с этим подходом состоит в том, что на узлах с низким трафиком c сеанс может потенциально находиться там на сервере в течение длительного времени, пока не поступит достаточное количество запросов (в зависимости от оценки session.gc_probability), чтобы вызвать G C для очистите файлы сеанса.

С обработчиками на основе memcached и redis у вас нет этой проблемы. Они будут обрабатывать очистку памяти автоматически. Сессии могут все еще оставаться в физической памяти некоторое время после их жизни, но демон не сможет получить к ним доступ. Если вы беспокоитесь об этом бите для безопасности, вы можете зашифровать свои сеансы в покое или найти хранилище ключей / значений с более строгим механизмом очистки памяти G C.

С пользовательским обработчиком сеансов вам придется создайте свой собственный механизм G C. Через SessionHandlerInterface вы реализуете метод gc, который передает вам максимальный интервал времени жизни сеанса, и вы будете нести ответственность за проверку того, прошел ли сеанс свое время жизни на основе этого интервала, и выполнять оттуда сборку мусора.

Вы также можете установить отдельную конечную точку, которая проверяет TTL сеанса (с помощью асинхронного AJAX запроса на стороне клиента) и отправляет ответ, если сеанс истек (принудительно javascript для повторной аутентификации). пользователь).

0 голосов
/ 13 марта 2020

ОБНОВЛЕНИЕ

Что касается запроса WebSocket, я предполагаю, что вы используете Laravel WebSockets с pusher. Pusher.io не поддерживает тайм-аут , вы можете прочитать эту статью поддержки "Планируете ли вы добавить функцию тайм-аута соединения в клиентскую библиотеку Channels pusher- js? «. Вы можете проверить это, если включите Laravel режим отладки (APP_DEBUG=true внутри .env) и начнете laravel-websockets с терминала (php artisan websockets:serve), чтобы вы могли видеть события выходного журнала. Если вы попытаетесь закрыть крышку ноутбука или перевести компьютер в режим гибернации ( sleep ), вы не увидите никаких сообщений об этом событии. Вы не можете сделать это с протоколом pusher. Существует member_removed Событие присутствия , но оно срабатывает только при закрытии вкладки или выходе из системы. Конечно, вы можете инициировать пользовательское событие клиента на канале присутствия, но для этого вам также понадобится установить таймер на стороне клиента, и вам нужно будет создать поставщика услуг для сервера laravel-websockets, как эта проблема github «Существуют ли способы реализации веб-хуков?» .

Некоторые люди предлагали другие похожие вопросы:

...

  • Чтобы таймер работал на внешнем интерфейсе ( у нас, он просто останавливается, когда вы закрываете крышку ноутбука )

Это происходит потому, что таймеры клиента останавливают выполнение в спящем режиме Таким образом, они продолжают с того места, где они были раньше. Но если вы используете переменную даты для экономии времени , эта переменная не будет обновляться, когда компьютер перейдет в режим гибернации, поэтому вы узнаете, когда он выйдет из спящего режима, проверив эту переменную даты , который по сравнению с текущим временем будет иметь существенную разницу и будет больше, чем интервал таймера.

Реализация логики времени c в клиенте

Вы также можете увидеть это реализация этой связанной Q / A : Могут ли какие-либо браузеры настольных компьютеров определять, когда компьютер выходит из спящего режима?

Вы можете настроить таймер в клиенте для запуска каждую минуту. Мы не будем полагаться на интервал таймера , но вместо этого этот таймер проверит переменную даты внешней области действия , если промежуток времени с последнего таймера больше 15 минут; если это так, то это означает, что браузер / JS по какой-то причине приостановил выполнение , возможно, спящий режим устройства ( sleep ), а затем вы перенаправили пользователя на маршрут выхода из системы. .

Пример JS код клиента:

// Set a variable to check previous time
let clientSession = new Date;

// Setup the client session checking timer
let clientSessionTimer = setInterval(() => {
  const now = new Date;
  // Get how many seconds have passed since last check
  const secondsSpan = (now - clientSession) / 1000;

  // If the 1 minute timer has exceeded 15 minutes trigger logout and clear timer
  if (secondsSpan > (60 * 15)) {
    // For some reason JS halted execution, so we'll proceed with logging out
    clearInterval(clientSessionTimer);
    window.location.href = '/logout/session'
  } else {
    // The timer runs as it should, update the clientSession time
    clientSession = now;
  }

}, 1000 * 60);

Вы можете проверить этот простой пример, но используя 1 второй таймер с 15 секундами выхода здесь . Лучше всего протестировать его на ноутбуке с закрытой крышкой, а затем открыть снова через 15 секунд минуту или две, потому что если у вас много запущенных программ, компьютеру потребуется некоторое время для сохранения состояния памяти, чтобы завершить Режим гибернации и остановка выполнения.

Пример Web Workers

Вы даже можете использовать API Web Workers , чтобы настроить веб-работника на более безопасный уровень:

Страница JS код:

const logoutWorker = new Worker('logoutWorker.js');
logoutWorker.onmessage = function (ev) {

  if (ev && ev.data === 'wakeup') {
    logoutWorker.terminate();
    // window.location.href = '/logout/session'
  } else {
    // The timer runs as it should, nothing to do
  }
}

веб-работник logoutWorker.js код:

let clientSession = new Date();

let clientSessionTimer = setInterval(() => {
  const now = new Date;
  const secondsSpan = (now - clientSession) / 1000;

  if (secondsSpan > 15) {
    postMessage('wakeup'); // Send a message wakeup to the worker page
    clearInterval(clientSessionTimer); // Clear the timer
  } else {
    clientSession = now; // Update the clientSession timer variable
    postMessage('update'); // And post a message to the page ONLY IF needed
  }
}, 1000);

Вы также можете проверить пример веб-рабочего с тем же таймером 15 секунд здесь .

0 голосов
/ 11 марта 2020

Итак, Idea стоит за setInterval и Sockets, setInterval поддерживается в большинстве браузеров, а javascript WbsocketApi поддерживается почти во всех браузерах.

Краткий обзор: setInterval () - это поведение функции следующее, когда ваш компьютер в спящем / приостановленном / спящем режиме он приостанавливается, а когда вы находитесь в режиме пробуждения, он возобновляет себя.

Следующий код сначала выполняет следующее (может быть одновременно, но), но запускается php server_socket прослушивая соединения,

чем javascript websocket api отправляет текущую временную метку в Unix временной метке в миллисекундах каждые 2 секунды, вы можете иметь 1 секунду, это ваше дело.

после этого php серверный сокет получает это время и проверяет, имеет ли он что-то похожее на предыдущий раз для сравнения, когда код создается впервые php не имеет ничего похожего на предыдущий раз, чтобы сравнить его с время, которое было отправлено из javascript websocket, поэтому php ничего не делает, но сохраняет это время в сеансе с именем 'prev_time' и ожидает получения данных другого времени из сокета javascript, поэтому здесь начинается второй цикл. когда php серверный сокет новых временных данных из javascript WebsocketApi проверяет, имеет ли он что-то похожее на предыдущее время для сравнения с этими вновь полученными временными данными, это означает, что php проверяет, существует ли сессия с именем «prev_time», так как мы находимся в второй цикл php обнаруживает, что он существует, получает его значение и выполняет следующие действия: $diff = $new_time - $prev_time, $ diff будет составлять 2 секунды или 2000 миллисекунд, поскольку помните, что наш цикл setInterval происходит каждые 2 секунды, а формат времени, который мы отправляем, - в миллисекундах,

, чем php проверяет if($diff<3000), если разница меньше 3000, если она знает, что пользователь активен, снова вы можете манипулировать этими секундами, как вы sh, я выбираю 3000, потому что возможная задержка в сеть, которая почти невозможна, но вы знаете, я всегда осторожен, когда речь идет о сетях, поэтому давайте продолжим, когда php определит, что пользователь активен php просто сбрасывает сеанс 'prev_time' со значением $new_time, которое было только что полученный и только для целей тестирования, он отправляет сообщение обратно javascript socket,

, но если $diff больше 3000, это означает, что что-то приостановило наш setInterval, и есть только один способ, которым это может произойти, и я думаю, что вы уже знаете, что я говорю, поэтому в else logi c из (if($diff<3000)) вы можете выйти из системы, уничтожив указанный сеанс c, и, если вы хотите перенаправить, вы можете отправить некоторый текст в сокет javacript и создать logi c, который будет выполнять window.location = "/login" в зависимости от текста, вот код:

Сначала это индекс. html файл просто для загрузки javascript:

<html>
    <body>
        <div id="printer"></div>
        <script src="javascript_client_socket.js"></script>
    </body>
</html>

затем javascript это не очень красиво написано, но вы можете понять, ЧИТАЙТЕ КОММЕНТАРИИ, ОНИ ВАЖНЫ:

var socket = new WebSocket('ws://localhost:34237'); // connecting to socket
    // Open the socket
socket.onopen = function(event) { // detecting when connection is established
        setInterval(function(){ //seting interval for 2 seconds
            var date = new Date(); //grabing current date
            var nowtime = Date.parse(date); // parisng it in miliseconds
            var msg = 'I am the client.'; //jsut testing message


            // Send an initial message
            socket.send(nowtime); //sending the time to php socket
    },2000);

};


// Listen for messages
socket.onmessage = function(event) { //print text which will be sent by php socket 
    console.log('php: ' + event.data);
};

// Listen for socket closes
socket.onclose = function(event) {
    console.log('Client notified socket has closed', event);
};

теперь здесь есть часть кода php, не волнуйтесь, там тоже есть полный код, но эта часть на самом деле Что касается вышеупомянутых заданий, вы встретите и другие функции, но они предназначены для декодирования и работы с javascript сокетами, так что это актуальная вещь прямо здесь. ПРОЧИТАЙТЕ КОММЕНТАРИИ, ОНИ ВАЖНЫ:

<?php 
            $decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
            print("< ".$decoded_data."\n");
            $response = strrev($decoded_data);
            $jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT  */
            if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
               $prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
               $diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
               print("$jsTime - $prev_time = $diff"); /** printing the difference */
               if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
                               *** you can manipulate and have for example 1 second = 1000ms */
                    socket_write($client,encode("You are active! your pc is awakend"));
                    $_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
                }else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds 
                            ** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
                    socket_write($client,encode("You are not active! your pc is at sleep"));
                    $_SESSION['prev_time'] = $jsTime;
                }
            }else { /** if we have not saved the previous time in session save it  */
                $_SESSION['prev_time'] = $jsTime;
            }

            print_r($_SESSION);

?>

И вот полный код php:

<?php
//Code by: Nabi KAZ <www.nabi.ir>
session_abort();
// set some variables
$host = "127.0.0.1";
$port = 34237;
date_default_timezone_set("UTC");


// don't timeout!
set_time_limit(0);

// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0)or die("Could not create socket\n");

// bind socket to port
$result = socket_bind($socket, $host, $port)or die("Could not bind to socket\n");

// start listening for connections
$result = socket_listen($socket, 20)or die("Could not set up socket listener\n");

$flag_handshake = false;
$client = null;
do {
    if (!$client) {
        // accept incoming connections
        // client another socket to handle communication
        $client = socket_accept($socket)or die("Could not accept incoming connection\n");
    }

    $bytes =  @socket_recv($client, $data, 2048, 0);
    if ($flag_handshake == false) {
        if ((int)$bytes == 0)
            continue;
        //print("Handshaking headers from client: ".$data."\n");
        if (handshake($client, $data, $socket)) {
            $flag_handshake = true;
        }
    }
    elseif($flag_handshake == true) {

        /*
        **** Main section for detectin sleep or not **
        */
        if ($data != "") {
            $decoded_data = unmask($data /* $data is actual data received from javascript socket */); //grabbing data and unmasking it | unmasking is for javascript sockets don't mind this
            print("< ".$decoded_data."\n");
            $response = strrev($decoded_data);
            $jsTime = (int) $decoded_data; /* time sent by javascript in MILISECONDS IN UNIX FORMAT  */
            if (isset($_SESSION['prev_time'])) { /** check if we have stored previous time in the session */
               $prev_time = (int) $_SESSION['prev_time']; /** grabbing the previous time from session */
               $diff = $jsTime-$prev_time; /** getting the difference newly sent time and previous time by subtracting */
               print("$jsTime - $prev_time = $diff"); /** printing the difference */
               if($diff<3000){ /** checking if difference is less than 3 second if it is it means pc was not at sleep
                               *** you can manipulate and have for example 1 second = 1000ms */
                    socket_write($client,encode("You are active! your pc is awakend"));
                    $_SESSION['prev_time'] = $jsTime; /** saving newly sent time as previous time for future testing whcih will happen in two seconds in our case*/
                }else { /** if it is more than 3 seconds it means that javascript setInterval function was paused and resumed after 3 seconds 
                            ** So it means that it was at sleep because when your PC is at sleep/suspended/hibernate mode setINterval gets pauesd */
                    socket_write($client,encode("You are not active! your pc is at sleep"));
                    $_SESSION['prev_time'] = $jsTime;
                }
            }else { /** if we have not saved the previous time in session save it  */
                $_SESSION['prev_time'] = $jsTime;
            }

            print_r($_SESSION);

           /*
        **** end of Main section for detectin sleep or not **
        */ 


        }
    }
} while (true);

// close sockets
socket_close($client);
socket_close($socket);
$client = null;
$flag_handshake = false;

function handshake($client, $headers, $socket) {

    if (preg_match("/Sec-WebSocket-Version: (.*)\r\n/", $headers, $match))
        $version = $match[1];
    else {
        print("The client doesn't support WebSocket");
        return false;
    }

    if ($version == 13) {
        // Extract header variables
        if (preg_match("/GET (.*) HTTP/", $headers, $match))
            $root = $match[1];
        if (preg_match("/Host: (.*)\r\n/", $headers, $match))
            $host = $match[1];
        if (preg_match("/Origin: (.*)\r\n/", $headers, $match))
            $origin = $match[1];
        if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $match))
            $key = $match[1];

        $acceptKey = $key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
        $acceptKey = base64_encode(sha1($acceptKey, true));

        $upgrade = "HTTP/1.1 101 Switching Protocols\r\n".
            "Upgrade: websocket\r\n".
            "Connection: Upgrade\r\n".
            "Sec-WebSocket-Accept: $acceptKey".
            "\r\n\r\n";

        socket_write($client, $upgrade);
        return true;
    } else {
        print("WebSocket version 13 required (the client supports version {$version})");
        return false;
    }
}

function unmask($payload) {
    $length = ord($payload[1]) & 127;

    if ($length == 126) {
        $masks = substr($payload, 4, 4);
        $data = substr($payload, 8);
    }
    elseif($length == 127) {
        $masks = substr($payload, 10, 4);
        $data = substr($payload, 14);
    }
    else {
        $masks = substr($payload, 2, 4);
        $data = substr($payload, 6);
    }

    $text = '';
    for ($i = 0; $i < strlen($data); ++$i) {
        $text .= $data[$i] ^ $masks[$i % 4];
    }
    return $text;
}

function encode($text) {
    // 0x1 text frame (FIN + opcode)
    $b1 = 0x80 | (0x1 & 0x0f);
    $length = strlen($text);

    if ($length <= 125)
        $header = pack('CC', $b1, $length);
    elseif($length > 125 && $length < 65536)$header = pack('CCS', $b1, 126, $length);
    elseif($length >= 65536)
    $header = pack('CCN', $b1, 127, $length);

    return $header.$text;
}

ПРИМЕЧАНИЕ ЧИТАЙТЕ ЭТО: $new_time переменная $jsTime в коде

создайте папку и просто скопируйте и вставьте ее в файлы, запускаемые php socket с помощью команды: php -f server_socket. php go на локальном хосте и протестируйте его, откройте консоль, чтобы увидеть сообщения, в которых будет сказано "вы активны" или "вы не активны" (когда вы выходите из спящего режима) ; ваше выполнение будет происходить, когда пользователь выйдет из спящего режима, а не когда он спит, потому что в этот момент все кэшируется в файл подкачки (windows) или в swap (linux)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...