php socket_read all data

Ragazzo

TDD interested
Что-то виснет сокет полностью когда пытаюсь прочитать все данные, тут я не новичок особо, но раньше либо по терминатору строки читал данные либо по определенному объему, т.к. раньше пытался читать все данные до того как будет прислана "" (данных нет), но php все время вис при этом, в комментах есть куча примеров но они работоспособностью не отличаются, кто-нибудь может проверить у себя также небольшой пример, как на SO привели, я не пойму это баг функции или что-то я делаю не так. Вот код со SO. Ну в качестве сокета можно подрубиться к какому-либо сайту например, меня просто интересует этот код рабочий все таки или нет.
http://stackoverflow.com/questions/493487/variable-length-packets-in-php
PHP:
while (($currentByte = socket_read($socket, 1)) != "") 
{
    // Do whatever you wish with the current byte
}
 

Вурдалак

Продвинутый новичок
Тут подразумевается случай, когда соединение разрывается. Если же нет, то понятия «прочитать все данные» на этом уровне в принципе нет, ибо формат может быть разный (в HTTP данные могут заканчиваться по прочтению Content-length байтов данных, могут заканчиваться при получении чанка нулевой длины и т.д.). Поэтому надо считывать данные согласно протоколу.

Для HTTP/1.0 такой код, например, правильный.
 

Ragazzo

TDD interested
Да нет, там фишка в том, что демон присылает строки, каждая строка заканчивается \n поэтому я использовал PHP_NORMAL_READ для получения 1ой строки, при нескольких чтобы не производить анализ строк на лету, не хочется делать т.к. гавнокод, но видимо придеться, фишка в том что я могу считывать например несколько строк через normal_read, но вот как понять где конец всего потока не понять, к сожалению в демоне не реализована ни строка-терминатор какая-то ни что-то еще, думал сделать еще через socket_select (проверить если в сокете данные а потом считывать по байту), но он почему то всегда возвращает 0 - не было изменений в сокетах, даже когда я знаю что в одном из входных сокетов(первый параметр), есть данные(
 

Активист

Активист
Команда форума
Для определения конца данных функция чтения из сокета ждет разрыва соединения, инициализированного на стороне сервера (ACK/FIN), если сервер (демон) не рвет соединение, то и функция будет ждать разрыва соединения вечно (ограничено временем жизни соединения на уровне ядра);

Т.е. любые TCP Данные для применения этой функции должны идти так:
- Коннект к демону;
- Демону отправляются команда (ну или этот шаг вообще пропускается);
- Демон посылает в сокет данные;
- Демон завершает TCP соединение;
- Цикл while завершается

Если демон не рвет соединения, то обрабатывать данные придется в любом случае, лучше использовать буфер отличный от одного байта, а то много системного IO использовать будет.

А на PHP скрипт сколько живет? Он случаем не читает данные постоянно?
 

Ragazzo

TDD interested
Активист
Да, Вурдалак как раз описал, что если сторона-сервер не рвет соединение, то читать(socket_read) будет бесконечно, ну т.е. тупо виснуть, демон не рвет соединение т.к. в этой же сессии(открытое соединение) осуществляется двустороннее общение, т.е. не обязательно посылка только одной команды и все, скрипт тут не один, задача одного - просто логин, другого - получение каких либо изменений, соотв. тут будет во втором просто set_time_limit(0), с переодическим слипом на 2-5сек, и посылкой новой команды на запрос изменений. Витоге я пришел к тому что придется считывать строки и анализировать на лету, а дальше потом реализовывать либо json сообщение которое будет говорить сколько байт демон передал, что-то вроде аналога content-length, либо просто какой-то терминатор строки предумать новый, но вариант с идущим вначале json-сообщением о кол-ве байт кажется наиболее приемлем.
 

fixxxer

К.О.
Партнер клуба
Ну да. Вариантов три - разрывать со стороны сервера соединение, передавать терминатор, сообщать длину пакета. Больше никак. В http/1.1 chunked transfer encoding не просто так появился :)
 

Ragazzo

TDD interested
fixxxer
Ага, у нас просто сегодня нефиговый факап случился из-за того, что я пытался объяснить, что по-другому больше никак, но мне хотели все таки сказать, что я не прав :D пойду почитаю про chunked transfer encoding ) что-то знакомое, давно правда читал про устройство 1.1
 

Активист

Активист
Команда форума
1. Лучше что бы демон посылал терминатор (т.е. БИНАРНЫЙ спец символ), например \x01 или два символа \x01\x02
2. Демон может слешевать спец символы (например новой строки).

Возмите протокол POP3, там, например, после комманды DATA терминатор <LF>.<LF>, в случае, если в DATA используется <LF>.<LF>, то последний слэшуется.
 

Ragazzo

TDD interested
Активист
Ну тут спорная на самом деле ситуация, т.к. все таки проще 1раз зная точный объем считать, чам по байту и сравнивать, т.к. могут быть накладки в том числе с совпадением спецпоследовательности и обычных данных как ты описал, плюс это значит что перед тем как отправить буфер сообщений, нужно его проверить на спец. последовательность и проекранировать где надо, хм, ну тут можно долго рассуждать, просто мне кажется что заранее зная сколько нужно считать будет проще, т.е. когда у тебя сообщения будут так
{'Action':'ContentLength','Value':12345} гораздо проще ведь.
 

fixxxer

К.О.
Партнер клуба
Ох, вот тут уж лучше просто фиксированную циферку слать. Парсить json из потока - это немножко адъ.
Если я правильно понимаю, что гоняется произвольное число json-ов, я бы сделал просто (по аналогии с теми же чанками):

<dword length><json...><dword length><json...> ... \x0\x0\x0\x0

т.е. pack('L', strlen($json)), $json, и так далее, длина 0 = конец связи.
 

Ragazzo

TDD interested
fixxxer
Да, гоняется произвольное кол-во json-строк, каждая из которых заканчивается \n (т.е. может быть 50 может быть 1 - например для авторизации). И тут мою идею про content-length завернули :D да что ж такое). Ок, посмотрим, в любом случае завтра между разрабами будем обсуждать и то и то наверное.
 

MiksIr

miksir@home:~$
Ну вооще если у него все пакеты - это json, то проще разделитель использовать, ибо json подразумевает эскейпинг.
 

MiksIr

miksir@home:~$
А много json строк как одно сообщение - выглядит немного странно, ибо в одну строку json можно запаковать сколько угодно сообщений.
Ну если уж очень надо, то да - \n как разделитель строк, \n\n как разделитель сообщений. И читается в снифере легко.
 

Ragazzo

TDD interested
fixxxer
Да я предлагал такое - отвергли :D
MiksIr
Ну там дело в том что каждая json-строка представляет собой информацию о каком-либо объекте, либо ответ на команду, т.е. они между собой никак не связаны, и параметры у них разные, так что сливать в одну строку не получиться.
 

MiksIr

miksir@home:~$
Ну и что, что не связаны
{"object1":{....}, "object2":{.....}} - вот и слили
Или
{данные 1}
{данные 2}
{данные 3}
сливаем в [{данные 1}, {данные 2}, {данные 3}]
Но это в общем мелочи.
 

Ragazzo

TDD interested
MiksIr
Ну да, тогда если
[{данные 1}, {данные 2}, {данные 3}]
то через PHP_NORMAL_READ 1 строкой и все. Нужно подумать над форматом данных в самом пакете да.
 
Сверху