Правила     Закладки     Карма    Календарь    Журналы    Помощь    Поиск    PDA    Чат   
        СМС-ки
   
Пейджер выключен!
 
Фильтр авторов:    показать 
  скрыть
  Ответ в темуСоздание новой темыСоздание опроса

> Сокеты
apach  
 ۩  [x] Дата
Цитировать сообщение

Пользователя сейчас нет на форуме



Новичок
*

Профиль
Группа: Пользователь
Сообщений: 2
Пользователь №: 42998
На форуме: 6 месяцев, 14 дней
Карма:




Доброго времени суток. Стал недавно разбираться с сокетами и понял что я чего-то не понимаю. Начну я с пред истории.
Захотел я реализовать на сайте обмен сообщениями как ВКонтакте, чтобы было все мгновенно. Выбрал для этого сокеты, ведь все таки на дворе 2016 и HTML5 нам позволяет создавать WebSoket.
Начал реализовывать. Создал демона который обрабатывает все подключения (сервер). Задача его постоянно смотреть есть ли у него те пользователи которые должны получить сообщения если есть то он им отправляет. Создав демоно понял что он работает немного не так. Он отвечает только когда ему отправляют запрос на вход, выход, или отправки сообщения. Сам же он не может отвечать. Как сделать так чтобы если есть серверу что сказать то говорил?

Вот код демона:

include('config.php');

$path = SERVER_PATH."/../../class/sql.php";
if(file_exists($path))
require_once($path);
require_once(SERVER_PATH."/socket_server.php");
//pid-file
//Если в PID файле хранится PID процесса и он активен, то не запускаем копию

if (socket_server::getstatusfwithconsole($server['pidfile'])) {
exit;//1 уже запущен
}

//system config
error_reporting(E_ALL); //Выводим все ошибки и предупреждения
set_time_limit(0); //Время выполнения скрипта безгранично
ob_implicit_flush(); //Включаем вывод без буферизации
ignore_user_abort(true);//Выключаем зависимость от пользователя
//system config


ini_set('error_log', $server['wserrorslogfile']);


fclose(STDOUT);
fclose(STDERR);
fclose(STDIN);
$STDIN = @fopen('/dev/null', 'r');
$STDOUT = fopen($server['wsconsolelogfile'], 'ab');
$STDERR = fopen($server['wsconsoleerrfile'], 'ab');

$config = array(
'pidfile' => $server['pidfile'],
'offfile' => $server['offfile'],
'max_connects_from_ip' => $server['max_user'],
'host' => $server['ip_server'],
'port' => $server['port']
);

$websocketserver = new socket_server($config);
$websocketserver->start(function($t, $connect, $data){ #message

$uid = array_search($connect, $t->connects);
$timestart = microtime(true);

$f = $t->decode($data);
if($f['payload'] == "" || $f['payload'] == " ") return;
$cook = $t::getCookie($t->users[$uid]['Cookie']);
fwrite($connect,$t->encode($cook['PHPSESSID']));

});

Вот класс сокет сервер

class socket_server{

public $config;
public $starttime;
public $server;
public $connects;
public $users;
public $ips; //Массив IP адресов, запрещаем больше 3х подключений с одного IP
public $id; //счётчик id подключений
public $online;


public function __construct($config) {
$this->config = $config;
$this->connects = array();
$this->users = array();
$this->ips = array();
$this->id = 1; //Начинаем с 1го номера
$this->online = 0;
}

public function start($onMessage=null, $onOpen = null, $onClose = null, $onAll = null) {
$pidfile = $this->config['pidfile'];
$offfile = $this->config['offfile'];
$this->starttime = round(microtime(true),2);
file_put_contents($pidfile, getmypid());//СОХРАНЯЕМ PID в файле
$this->server = stream_socket_server("tcp://".$this->config['host'].":".$this->config['port'], $errno, $errstr);
if (!$this->server) { //Если сокеты не работают
unlink($pidfile);
die($errstr. "(" .$errno. ")\n");
}
$msgtosend = "";
while (true) {
//формируем массив прослушиваемых сокетов:
$read = $this->connects;
$read[]= $this->server;
$write = $except = null;
if (!stream_select($read, $write, $except, null)) {//ожидаем сокеты доступные для чтения (без таймаута)
break;
}
if (in_array($this->server, $read)) {//есть новое соединение то обязательно делаем handshake
//принимаем новое соединение и производим рукопожатие:

if (($connect = stream_socket_accept($this->server, -1)) && $info = $this->handshake($connect)) {
if(!isset($this->ips[$info['ip']])){
$this->ips[$info['ip']] = 1; // Одно подключение
}else{
$this->ips[$info['ip']]++;
if($this->ips[$info['ip']]>$this->config['max_connects_from_ip']){
continue;
}
}

$this->connects[] = $connect;//добавляем его в список необходимых для обработки
$info['id'] = $this->id++;
if($this->id > 10000) $this->id = 1;
$this->online ++;
$this->users[] = $info;
if(is_callable($onOpen)) $onOpen();
}
unset($read[ array_search($this->server, $read) ]);
}
foreach($read as $connect) {//обрабатываем все соединения
$data = fread($connect, 100000);
if (!$data) {
$uid = array_search($connect, $this->connects);//определяем uid закрытого соединения

if($this->ips[$this->users[$uid]['ip']]==1) unset($this->ips[$this->users[$uid]['ip']]); //Удаляем IP адрес из списка коннектов
else $this->ips[$this->users[$uid]['ip']]--;

$this->online --;

unset($this->users[ array_search($connect, $this->connects) ]); //Удаляем информацию о пользователе соединения
unset($this->connects[ array_search($connect, $this->connects) ]); //Удаляем ресурс подключения

fclose($connect);//Закрываем соединение
if(is_callable($onClose)) $onClose();
continue;
}
if(is_callable($onMessage)) $onMessage($this, $connect, $data);
}
if(is_callable($onAll)) $onAll($this);
}
}


private function handshake($connect) { //Функция рукопожатия
$info = array();

$line = fgets($connect);

$header = explode(' ', $line);
$info['method'] = $header[0];
$info['uri'] = $header[1];

//считываем заголовки из соединения
while ($line = rtrim(fgets($connect))) {
if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
$info[$matches[1]] = $matches[2];
} else {
break;
}
}


$address = explode(':', stream_socket_get_name($connect, true)); //получаем адрес клиента
$info['ip'] = $address[0];
$info['port'] = $address[1];

if (empty($info['Sec-WebSocket-Key'])) {
return false;
}

//отправляем заголовок согласно протоколу вебсокета
$SecWebSocketAccept = base64_encode(pack('H*', sha1($info['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"Sec-WebSocket-Accept:".$SecWebSocketAccept."\r\n\r\n";
fwrite($connect, $upgrade);

return $info;
}

public function encode($payload, $type = 'text', $masked = false) {
$frameHead = array();
$payloadLength = strlen($payload);

switch ($type) {
case 'text':
// first byte indicates FIN, Text-Frame (10000001):
$frameHead[0] = 129;
break;

case 'close':
// first byte indicates FIN, Close Frame(10001000):
$frameHead[0] = 136;
break;

case 'ping':
// first byte indicates FIN, Ping frame (10001001):
$frameHead[0] = 137;
break;

case 'pong':
// first byte indicates FIN, Pong frame (10001010):
$frameHead[0] = 138;
break;
}

// set mask and payload length (using 1, 3 or 9 bytes)
if ($payloadLength > 65535) {
$payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 255 : 127;
for ($i = 0; $i < 8; $i++) {
$frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
}
// most significant bit MUST be 0
if ($frameHead[2] > 127) {
return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)');
}
}
elseif ($payloadLength > 125) {
$payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
$frameHead[1] = ($masked === true) ? 254 : 126;
$frameHead[2] = bindec($payloadLengthBin[0]);
$frameHead[3] = bindec($payloadLengthBin[1]);
} else {
$frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
}

// convert frame-head to string:
foreach (array_keys($frameHead) as $i) {
$frameHead[$i] = chr($frameHead[$i]);
}
if ($masked === true) {
// generate a random mask:
$mask = array();
for ($i = 0; $i < 4; $i++) {
$mask[$i] = chr(rand(0, 255));
}

$frameHead = array_merge($frameHead, $mask);
}
$frame = implode('', $frameHead);

// append payload to frame:
for ($i = 0; $i < $payloadLength; $i++) {
$frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
}

return $frame;
}

//-------------------------------------------------------------------------------------------------- ------------

public function decode($data){
$unmaskedPayload = '';
$decodedData = array();

// estimate frame type:
$firstByteBinary = sprintf('%08b', ord($data[0]));
$secondByteBinary = sprintf('%08b', ord($data[1]));
$opcode = bindec(substr($firstByteBinary, 4, 4));
$isMasked = ($secondByteBinary[0] == '1') ? true : false;
$payloadLength = ord($data[1]) & 127;

// unmasked frame is received:
if (!$isMasked) {
return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)');
}

switch ($opcode) {
// text frame:
case 1:
$decodedData['type'] = 'text';
break;

case 2:
$decodedData['type'] = 'binary';
break;

// connection close frame:
case 8:
$decodedData['type'] = 'close';
break;

// ping frame:
case 9:
$decodedData['type'] = 'ping';
break;

// pong frame:
case 10:
$decodedData['type'] = 'pong';
break;

default:
return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)');
}

if ($payloadLength === 126) {
$mask = substr($data, 4, 4);
$payloadOffset = 8;
$dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;
} elseif ($payloadLength === 127) {
$mask = substr($data, 10, 4);
$payloadOffset = 14;
$tmp = '';
for ($i = 0; $i < 8; $i++) {
$tmp .= sprintf('%08b', ord($data[$i + 2]));
}
$dataLength = bindec($tmp) + $payloadOffset;
unset($tmp);
} else {
$mask = substr($data, 2, 4);
$payloadOffset = 6;
$dataLength = $payloadLength + $payloadOffset;
}

if (strlen($data) < $dataLength) {
return false;
}

if ($isMasked) {
for ($i = $payloadOffset; $i < $dataLength; $i++) {
$j = $i - $payloadOffset;
if (isset($data[$i])) {
$unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
}
}

$decodedData['payload'] = $unmaskedPayload;
} else {
$payloadOffset = $payloadOffset - 4;
$decodedData['payload'] = substr($data, $payloadOffset);
}

return $decodedData;
}


//-----------------------static function-----------------------
public static function getstatusfwithconsole($pidfile) {
if( file_exists($pidfile) ) {
$pid = file_get_contents($pidfile);
//получаем статус процесса
$status = self::getstatusp($pid);
if($status['run']) {
//демон уже запущен
return true;
}else{
//pid-файл есть, но процесса нет
if(!unlink($pidfile)) {
//не могу уничтожить pid-файл. ошибка
exit(-1);
}
}
}

return false;
}
public static function getstatusp($pid){
$result = array ('run'=>false);
$output = null;

if (strtoupper(substr(PHP_OS,0,3)) === 'WIN'){
exec("tasklist /fi \"pid eq ".$pid."\"", $output);

if(count($output)>3){//Если в результате выполнения больше одной строки то процесс есть! т.к. первая строка это заголовок, а третья уже процесс
$result['run'] = true;
$result['info'] = $output[3];//строка с информацией о процессе
}
}
else {//Если *nix
exec("ps -aux -p ".$pid, $output);

if(count($output)>1){//Если в результате выполнения больше одной строки то процесс есть! т.к. первая строка это заголовок, а вторая уже процесс
$result['run'] = true;
$result['info'] = $output[1];//строка с информацией о процессе
}
}


return $result;
}
public static function getstatusf($pidfile) {

if( file_exists($pidfile) ) {

$pid = file_get_contents($pidfile);
$output = null;

if (strtoupper(substr(PHP_OS,0,3)) === 'WIN'){
exec("tasklist /fi \"pid eq ".$pid."\"", $output);

if(count($output)>3){//Если в результате выполнения больше одной строки то процесс есть! т.к. первая строка это заголовок, а третья уже процесс
return $pid;
} else {
//pid-файл есть, но процесса нет
return -2;
}
}
else {//Если *nix
exec("ps -aux -p ".$pid, $output);

if(count($output)>1){//Если в результате выполнения больше одной строки то процесс есть! т.к. первая строка это заголовок, а вторая уже процесс
return $pid;
} else {
//pid-файл есть, но процесса нет
return -2;
}
}
}

return -1;//файла и процесса нет

}
public static function getCookie($cookie){
$result = array();
$cookie = explode("; ",$cookie);
if(count($cookie)){
foreach($cookie as $v){
$d = explode("=",$v);
if(count($d)){
$result[$d[0]] = $d[1];
}
}
}

return $result;
}
}

PMПисьмо на e-mail пользователю
    0   Для быстрого поиска похожих сообщений выделите 1-2 слова в тексте и нажмите сюда Для быстрой цитаты из этого сообщения выделите текст и нажмите сюда
Invis1ble  
Дата
Цитировать сообщение

Пользователя сейчас нет на форуме




******

Профиль
Группа: Эксперт
Группа переписки
Сообщений: 11788
Пользователь №: 23195
На форуме: 6 лет, 4 месяца, 15 дней
Карма: 429

Трезвый :
7 лет, 3 месяца, 15 дней


Цитата (apach @ 6.06.2016 - 18:03)
Начал реализовывать.


--------------------
PMПисьмо на e-mail пользователюСайт пользователя
    0   Для быстрого поиска похожих сообщений выделите 1-2 слова в тексте и нажмите сюда Для быстрой цитаты из этого сообщения выделите текст и нажмите сюда
apach  
 ۩  Дата
Цитировать сообщение

Пользователя сейчас нет на форуме



Новичок
*

Профиль
Группа: Пользователь
Сообщений: 2
Пользователь №: 42998
На форуме: 6 месяцев, 14 дней
Карма:




Да что то вроде этого.
Пробовал в бесконечном цикле перебрать все подключения и просто выдавать им любую информацию но пользователям нечего не приходит пока кто то не отправит из них запрос. Почему так происходит?
PMПисьмо на e-mail пользователю
    0   Для быстрого поиска похожих сообщений выделите 1-2 слова в тексте и нажмите сюда Для быстрой цитаты из этого сообщения выделите текст и нажмите сюда
  Быстрый ответ
Информация о Госте
Введите Ваше имя
Кнопки кодов
Для вставки цитаты, выделите нужный текст и
НАЖМИТЕ СЮДА
Введите сообщение
Смайлики
:huh:  :o  ;) 
:P  :D  :lol: 
B)  :rolleyes:  <_< 
:)  :angry:  :( 
:unsure:  :blink:  :ph34r: 
     
Показать всё

Опции сообщения  Включить смайлики?
 Включить подпись?
 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:

Опции темы Ответ в темуСоздание новой темыСоздание опроса