Xpoint
   [напомнить пароль]

Неблокирующие сокеты - как с ними работать?

2006-07-19 14:51:19 [обр] Михаил Харитонов [досье]

Здравствуйте.
Скажите, пожалуйста, какие способы работы с неблокирующими сокетами существуют в php (т.е. как можно в многопоточном режиме работать).
Я сейчас пишу скрипт для определения google PR и яндекс тИЦ сайтов. Есть список сайтов и для каждого идёт обращение к яндексу и гуглю для определения тИЦ и PR. Если просто последовательно для каждого сайта обращаться к серверу яндекса через fsockopen(), затем к серверу гугля, то выходит очень долго. Соответственно необходимо сделать параллельное обращение к различным серверам с различными запросами.
Слышал, что в php5 что-то добавилось для работы с сокетами, но ничего конкретного не нашёл.

P.S.: английский я знаю плохо, поэтому источники ограничиваются только русскоязычными ресурсами :(

спустя 1 час 54 минуты [обр] Андрей Гора [досье]
тебе может помочь curl, проверь, чтобы PHP был с ним скомпилен
пробуй функции семейства curl_multi_*
спустя 5 минут [обр] Михаил Харитонов [досье]

тебе может помочь curl, проверь, чтобы PHP был с ним скомпилен

пробуй функции семейства curl_multi_*

Я курл нелюблю :)
Насколько я знаю, курл в своей работе использует теже пхпшные функции и ничего нового не добавляет, просто через него удобнее работать. Вы, случайно, не знаете, как в курле реализована данная возможность?
спустя 1 час 56 минут [обр] Михаил Харитонов [досье]
Скажите, может стоит копать в сторону функций socket_*?
спустя 1 час 30 минут [обр] Дмитрий Донцов [досье]

Михаил Харитонов[досье]
Если Вам нужно при каждом обращении к скрипту тянуть с яндекса и гугеля информацию, то посоветовать Вам что-то сложно...
Если же не нужно, то напишите 2 скрипта... один - раз (два, три ии т.д.) в сутки достает информацию и сохраняет, а другой ее только отображает...

простите, если был бесполезен :)

спустя 1 час 35 минут [обр] Василий Свиридов [досье]
Дмитрий Донцов[досье]
Можно и не делать n-раз в день. Один скрипт пусть будет консольным, там можно пользовать pcntl_fork. Нафоркал процессов, а они с разных сайтов инфу-то и тянут.
спустя 6 часов [обр] Андрей Гора [досье]

Насчет нелюбви к CURL - это Вы, батенька, напрасно, для Вашего случая самое оно.
PHP-функции CURL не использует, поскольку это отдельная библиотека, писаная на C.
По моему опыту, у multiCURL появляются тормоза, если запрашивать одновременно более 100 урлов, но это можно учитывать и делить массив запросов на пакеты.

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

Дмитрий Донцов[досье], Василий Свиридов[досье] - google PR и яндекс тИЦ, возможно, обновляются не чаще раза в день, точно не знаю, но сильно много даже им работенки несколько раз в день пересчитывать такие вещи.

спустя 4 часа 5 минут [обр] Дмитрий Кононов [досье]

Михаил Харитонов[досье], пишите в личку, скину библиотеку, которую я сделал для работы с неблокирующими сокетами.
Можно писать как клиенты, так и сервера.
Используется PHP5 и socket streams.

Можно использовать и обычные socket_*, но преимущество streams в том, что вы можете прозрачно работать с HTTPS.

спустя 3 часа 21 минуту [обр] Михаил Харитонов [досье]
google PR и яндекс тИЦ, возможно, обновляются не чаще раза в день, точно не знаю, но сильно много даже им работенки несколько раз в день пересчитывать такие вещи.

На самом деле тИЦ пересчитывается раз в 2 недели, а PR - раз в 3 месяца. Вот только точных дат никто не знает, поэтому и приходится каждый день проверять. Но у меня немного другая задача. Я пишу скрипт для определения конкурентности запроса. Т.е. скрипту скармливается запрос, по этому запросу выдирается, скажем, первые 10 сайтов из результатов поиска яндекса и для каждого из найденных сайтов определяется тИЦ и PR, потом для каждого из этих сайтов определяются (опять через запрос к яндексу и парсинка результатов поиска) сколько существует ссылок с других сайтов на этот сайт (или страницу) с текстом запроса в ссылке и т.д.

Т.е. нет единого списка сайтов. Поэтому и нужно максимально ускорить скорость работы при обращении через сокеты.

спустя 2 часа 7 минут [обр] Дмитрий Шер aka sherd [досье]

Михаил Харитонов[досье], Ваша задача, как мне кажется, состоит вовсе не в том, чтобы "используя сокеты, как можно быстрее скачивать данные с сайтов".

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

спустя 21 час [обр] Дмитрий Кононов [досье]

Дмитрий Шер aka sherd[досье], уважаемый, позвольте с вами не согласиться.
В этой задаче как раз нет нужды смотреть в сторону многопоточности и всяких форков.
Нужно: послать множество запросов, получить ответ и сделать несложные операции (записать в БД, например).

Форканье процессов приводит к ненужному потреблению памяти.
Собственно, большую часть времени скрипт будет ожидать ответа сервера, соотвественно при использовании форка n процессов будут висеть в памяти и ждать.
Зачем же запускать много процессов, если это можно сделать из одного?

Для этого и существуют асинхронные операции с сокетами.
Вы коннектитесь или посылаете данные и не ждете завершения системного вызова.
Записались данные во время системного вызова - хорошо, нет - можно подождать, пока они не запишутся, а в это время обработать другие данные.
Для этого есть волшебная функция select.
Кстати, тот же squid и еще множество других приложений используют неблокирующие сокеты.

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

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

Как и везде свои плюсы, свои минусы.

И, конечно, работа с сокетами доставит вам массу впечатлений, можете поверить. :)
Однако, оно того стоит.

спустя 3 часа 10 минут [обр] Михаил Харитонов [досье]
А никто не знает, где есть информация на русском языке по работе с socket_* и stream_* функциями в php и в целом по работе с сокетами и потоками. А то я тут понял, что я в этой теме полный профан. Даже не знаю чем socket_* ф-и отличиются от stream_socket_* ф-й. :(
спустя 5 минут [обр] Михаил Харитонов [досье]
Скажите ещё, а multiCURL добавляет какие-либо кардинально новые возможности по сравнению со стандартными функциями php5. Т.е. есть ли в мультикурле что-либо, что нельзя реализовать без него?
спустя 1 день 3 часа [обр] Андрей Гора [досье]
В multiCURL, можно все, что доступно в самом CURL, это вообще условное деление.
А в CURL да, есть то, что в PHP не реализуется (функции stream_* в расчет не беру, не работал, но по-любому их применение посложнее CURL-а будет).
Это соединение через прокси, установка USERAGENT и REFERER, информация о ошибках соединения и т.д.....
спустя 1 день 16 часов [обр] Михаил Харитонов [досье]
Это соединение через прокси, установка USERAGENT и REFERER, информация о ошибках соединения и т.д.....
Но соединение через прокси, установка UserAgent'а и т.п. - это всё можно вообще при помощи fsockopen() и HTTP запроса реазизовать. Подключение через прокси к удалённому HTTP серверу я сам много раз реализовывал...
спустя 2 часа 11 минут [обр] Дмитрий Шер aka sherd [досье]
Михаил Харитонов[досье], а оно вам надо, что-то реализовывать? Да, Дмитрий Кононов[досье] все правильно сказал - вам просто нужна асинхронная работа. Не в скорости скачивания или работы с самим сокетом дело, а с алгоритмом.. либо нитки, либо асинхронные сокеты, либо потоки. Выбирайте )
спустя 4 часа 26 минут [обр] Михаил Харитонов [досье]
Да, Дмитрий Кононов[досье] все правильно сказал - вам просто нужна асинхронная работа. Не в скорости скачивания или работы с самим сокетом дело, а с алгоритмом.. либо нитки, либо асинхронные сокеты, либо потоки. Выбирайте )
Да я, вообще то, перед созданием темы ещё определился :)
Мне нужны асинхронные сокета. Но как с ними работать в php (и вообще) я не знаю. Поэтому и прошу подкинуть ссылки (не php мануал), где моно почитать об них или примеры работы с ними. И скажите, пожалуйста, неграмотному человеку (т.е. мне), чем socket_* ф-и отличиются от stream_socket_* ф-й в php?
спустя 15 часов [обр] Дмитрий Кононов [досье]

Михаил Харитонов[досье], ну что ж вы никак не успокоитесь. :)
Я вам скинул библиотеку, используйте ее, ничего писать не нужно.
Просто посмотрите пример использования в examples/get.php и удалите лишние обработчики.

Что касается описаний на русском, то я их встречал крайне мало.
Основная масса на английском.
Если хотите научиться работать с сокетами, то все рекомендуют книги Стивенса:

  1. W. Richard Stevens Unix Network programming, volume 1.
  2. W. Richard Stevens TCP/IP Illustrated, volume 1.
  3. W. Richard Stevens, TCP/IP Illustrated, volume 2.

Правда, я их не читал. :)

Вторая книга у меня есть в pdf (на английском). Если надо - пишите.
Вот переводное издание:
http://www.piter.com/book/978594723991/
Один товарищ пишет про неблокирующий коннект по-русски:
http://www.kalinin.ru/programming/network/01_12_00.shtml

Теперь про отличия функций socket_* от stream_socket_*.

Как пишут в документации, "The socket extension implements a low-level interface to the socket communication functions", то есть функции socket_* реализуют низкоуровневый интерфейс к функциям сокетов. Если посмотреть исходный код этого расширения, то можно увидеть, что данные функции являются просто оберткой для Си-шных функций:
socket_connect -> connect
socket_bind -> bind
socket_read -> recv
и т.д., которые в свою очередь делают системные вызовы ядра.
Используя это расширения, вы пишете так же, как и на обычном Си.
Здесь есть очень полезная функция socket_get_option (в Си getsockopt), которая позволяет получить значения многих параметров.

Функции же stream_socket_* - это часть интерфейса streams.
Streams, или потоки, являются обощенным интерфейсом для доступа к различным ресурсам: локальным файлам, протоколам HTTP/HTTPS, FTP/FTPS, даже SSH и т.д. Вы даже можете делать свои собственные потоки.
Делая обычный fopen, вы сами того не подозревая открываете поток.
Например, вот такой вызов

$fd = fopen("/path/to/file", "r");

открывает локальный файл.
Вот такой:

$fd = fopen("http://www.example.com/", "r");

уже открывает страничку на удаленном сервере.

Работа с потоками унифицирована: для чтения/записи вы используете обычные fread/fwrite.
Можно определить свой префикс, например, "mydb://" и извлекать из базы данных какой-нибудь блоб.
Функции stream_socket_* предназначены для работы с сокетами в рамках streams.
Они в какой-то мере упрощают работу программиста, не нужно делать лишние вызовы.
Например, чтобы создать сервер с использованием sockets, нужно было делать несколько вызовов:

// creating, binding and listeninig socket
if (!($s_listen = socket_create(AF_INET, SOCK_STREAM, 0)) ||
    !socket_set_option($s_listen, SOL_SOCKET, SO_REUSEADDR, 1) ||
    !socket_set_option($s_listen, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>5, "usec"=>0)) ||
    !socket_set_option($s_listen, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>5, "usec"=>0)) ||
    !socket_set_nonblock($s_listen) ||
    !socket_bind($s_listen, $config["listen_addr"], $config["listen_port"]) ||
    !socket_listen($s_listen)
   )
     sock_err(null, true);

В streams кода поменьше:

// creating, binding and listeninig socket
if (!($s_listen = stream_socket_server("tcp://{$config['listen_addr']}:{$config['listen_port']}", $errno, $errstr)))
     sock_err(null, true);

То есть bind и listen делаются одним вызовом.
Также можно легко и непринужденно забирать странички по протоколу HTTPS.

Правда, здесь уже нет аналога функций socket_get_option/socket_set_option, поэтому вы не сможете управлять сокетом столь тонко, как это делается в sockets.
(Я не совсем понимаю, почему разработчики не предусмотрели эту возможность.)
Кроме того, streams появились позднее, в баг-репортах было много ошибок.
Возможно, они не все еще пофиксены. Например, я одну так и не смог побороть, не понимаю, бага это или фича. :) Тем не менее, обошел ее.

socket_* и stream_socket_* по сути делают одно и то же, но никак не соотносятся между собой.
То есть вы не сможете использовать функции socket_* в streams и наоборот.

А вообще не стоит сильно углубляться в работу с сетью, если вам это не сильно нужно.
Потеряете много времени. Используйте готовые решения.

спустя 3 месяца 21 день [обр] Cook [досье]
Дмитрий Кононов[досье]
я сейчас также ищу интсрументы для асинхронной работы, для многопоточного(канального,сессионного или еще как:) ) вебклиента, мне нужно
одноврменно от 1000 сессий открывать, попробовал POE на перле, жрет много памяти и непонятно куда она уходит и как освобождать...
не делали ли вы часом тестов сколько памяти кущается при параллельном получении содержания 1000 урлов, и освобождается ли она после завершения коннекта ?
и вообще, нет ли каких то глюков при открытии более 1000 ас. сокетов на пхп ?
спустя 7 дней [обр] Дмитрий Кононов [досье]

Cook[досье]
На таких объемах не тестировал. :)
Да и зачем столько много?
Сделайте пакетную обработку по 20-30-40 соединений.
А 1000 одновременных коннектов хостер может не одобрить, приравнять это к вредоносной деятельности...

Вы получаете одновременно 1000 страничек и храните их в памяти, каждая, скажем, килобайт по 20 — вот вам и получается 20 метров памяти минимум. :)
Если полученные блоки сразу же сохранять на диск, тогда надо держать открытыми порядка 1000 дескрипторов. Тоже определенные накладные расходы.

Вообще, по освобождению памяти в PHP5 сделали много улучшений.
По моим тестам она освобождается достаточно быстро, не растет лавинообразно.

Рискну разместить тут ссылку на свою библиотеку (да простят меня модераторы):
http://inetserv.sourceforge.net/
Попробуйте ваши 1000 коннектов, отпишитесь потом, как работает.

Powered by POEM™ Engine Copyright © 2002-2005