Как правильно работать с сокетами?

GRIG

Новичок
Привет всем. Помогите разобраться с такой проблемой.
Мне нужно:
1) Открыть сокет
2) Выплюнуть в него некоторое сообщение
3) Дождаться ответа на него
4) Выплюнуть в сокет второе сообщение
5) Дождаться ответа на него
6) Закрыть сокет.
Пункты 1+2+4+6 проблем не вызывают:
PHP:
if( $sock = fsockopen( $ip_adress, $port, $errno, $errstr )
{
  fwrite( $sock, $message1 );
  fwrite( $sock, $message2 );
  fclose( $sock);
}
Проблемы возникают в п.3 и 5. Непонятно как организовать ожидание ответа. А организовать его нужно обязательно - если я закрою связь раньше, чем на том конце закончится обработка моего сообщения, то сообщение вообще не будет обработано. (Да и плюс к тому в будущем может понадобиться обработка этого ответа).
Вставить между двумя записями в сокет вызов функции fgets() - не вариант. Во-первых, потому что в ответе где-то в середине может совершенно законно встретиться символ перевода строки. Во-вторых, потому, что в конце ответа обязательно стоит байт с другим кодом.
Вставить туда вызов fread() - тоже не вариант. Размер ответа заранее неизвестен, и к тому же вместе с ответом не передается. Так что функция будет ждать либо заполнения блока до запрошенного размера, либо закрытия связи.
Проверять условие feof() - еще хуже. Пока связь не закрыта эта функция не вернет true и может даже зависнуть.
В общем, чего-то я тут застрял и ничего путного придумать не могу.
 

Вурдалак

Продвинутый новичок
Если размер ответа неизвестен и в получаемом ответе нет никаких признаков того, когда заканчивается ответ, то задача нереализуема.
 

GRIG

Новичок
Размер ответа заранее неизвестен. Но признаки того, что ответ пришел полностью, есть. Это факт прихода байта с определенным кодом.
 

jrip

Новичок
Тогда видимо нужно читать пока не придет этот байт.
 

GRIG

Новичок
То есть как? В цикле fread( куда-то там, 1 байт ) - и так до тех пор пока не придет тот самый, который должен быть последним?
 

jrip

Новичок
Ну да, к примеру так.
Но по идее не обязательно читать именно по байту.
И лучше в цикл поставить какой-нибудь маленький sleep()
 

GRIG

Новичок

jrip

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

GRIG

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

jrip

Новичок
Вы бы документацию то почитали, чтоли, для начала. К примеру про блокирующие-неблокирующие сокеты.
 

Вурдалак

Продвинутый новичок
jrip, ну, справедливости ради, ответ лежит не на поверхности, чтобы отвечать в подобном духе.
 

jrip

Новичок
Вурдалак
Ну человек утверждает, что нельзя. Я удивился, пошел почитал документацию, убедился что можно, поэтому посоветовал и ему :)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
если это HTTP - я бы посоветовал CURL,
он позволяет подключить коллбэки - функции, которые вызываются при получении данных
я тут когда-то выкладывал свою библиотеку с примерами
 

Farsh

~ on ~ high ~ wave ~
Читать крупными кусками нельзя - fread будет ждать до тех пор, пока кусок не будет заполнен.
неверно

И вообще, мне не понятно, где проблема ?
Записал - так сиди и читай в цикле fread'ом до тех пор, пока последний байт и не будет ожидаемым разделителем.
И слипов тут никаких не надо, ибо по уполчанию это блокирующее чтение.

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

GRIG

Новичок
Вы бы документацию то почитали, чтоли, для начала. К примеру про блокирующие-неблокирующие сокеты.
А поконкретнее можно? Хотя бы в виде ссылки на php.net
Нет, это не он. И вообще речь идет о закрытом протоколе старой разработки, который не совместим ни с чем из современного, но при этом применяется из соображений обратной совместимости. А отказаться от использования этого протокола - это развлекуха на такие миллионы рублей и долларов, что даже подумать страшно.
>> И если тот самый байт будет в середине - то дальше ждать придется очень долго.
Тут я вообще не понимаю, а что у вас этот самый байт разделяет ?
Я имел в виду следующую ситуацию. Предположим, что ответное сообщение читается через fread() блоками по 100 байтов. Предположим, что пришедший ответ имеет длину 150 байтов и правильно заканчивается тем самым байтом. Согласно доке, fread() завершается в одном из трех случаев: 1) получено заданное количество байтов; 2) закрыта связь; 3) истекает тайм-аут. В этих условиях получается, что первый вызов fread() вернет первые 100 байтов ответа, а второй вызов скопирует в буфер остаток ответа размером в половину требуемого блока, а потом будет ждать пока заполнится весь блок целиком. И ждать он будет очень долго, поскольку с той стороны больше ничего не поступит до тех пор, пока я не выплюну туда второе сообщение, а связь (согласно протоколу) должна разорваться именно по моей инициативе. Какой там тайм-аут по умолчанию стоит - хрен его знает. Могу только сказать, что неприемлемо большой (увы, проверено).
 

Farsh

~ on ~ high ~ wave ~
Согласно доке, fread() завершается в одном из трех случаев: 1) получено заданное количество байтов; 2) закрыта связь; 3) истекает тайм-аут.
и четвертый случай: если доступен хотя бы 1 байт во входящем буфере tcp ( как должно быть )

Но действительно, сам только что проверил, fread при каждом вызове ждет как минимум 1 байт с другого конца соединения и отдает результат вместе с тем, что уже есть во входящем буфере.

То есть логика такая:
-> запрос на чтение fread($fp, 100)
-> в tcp стек приходит 150 байт
-> php забирает все 150 байт к себе
-> php отдает от себя 100 байт, 50 байт остается
-> запрос на чтение fread($fp, 100)
-> тут уже php смотрит, что запросилось больше данных, чем у него пямяти, после чего лезет в сетевой стек. А там у нас пусто, но соединение не закрыто, и делает очередной запрос на чтение с другого конца.

P.s. юзайте socket_read; там работает все так, как должно
 

GRIG

Новичок
В итоге остановился вот на каком варианте:
PHP:
it( $sock = fsockopen( $IP, $PORT, $errno, $errstr ) )
{
  socket_set_timeout( $sock, 1 );
  fwrite( $sock, $message1 );
  fread( $sock, 2000 );
  fwrite( $sock, $message2 );
  fread( $sock, 2000 );
  fclose( $sock );
}
В таких условиях все работает так, как я хотел, и на ожидании нигде не тормозит.
Правда теперь есть другая непонятка. Перед работой с сокетом я устанавливаю тайм-аут в 1 секунду. Первым вызовом fread() я запрашиваю количество байтов, которое заведомо превышает все то, что может мне прийти. По идее, fread() должен выждать эту секунду прежде чем вернуть то, что пришло и сказать "извини, больше нету". Но на другой стороне оба моих сообщения фиксируются с одинаковым (с точностью до секунды) временем. Т.е. получается, что этого ожидания на самом деле нет. Как это может быть?
 

GRIG

Новичок
Переделать скрипты на socket_* конечно можно. Только для этого нужно потратить время на изучение доков и практику. А fsockopen уже изучен. А мне сейчас важнее результат побыстрее получить.
 
Сверху