Php програмування сокетів

Сокети є надзвичайно зручну, але в той же час погано зрозумілу технологію взаємодії між двома процесами в мережі. Ці процеси можуть існувати на одній і тій же машині, спілкуючись один з одним через локальний сокет, призначений для взаємодії між процесами, або на різних машинах через Internet. Хоча тема сокетов дуже обширна, в даній статті представлені основи, які необхідні для використання розширень РНР, призначених для написання власних серверів і клієнтів сокетов.

Для використання сокетів РНР повинен бути скомпільовано з опцією ./configure --enable-sockets або ж буде потрібно завантажувати розширення підтримки сокетів динамічно.

Майте на увазі, що приклади, наведені далі в цій статті, розроблені для запуску безпосередньо з оточення оболонки з використанням версії РНР командного рядка. Хоча їх можна запустити в Web-браузері, робити це не рекомендується. У разі сценаріїв, які створюють сервери сокетов, їх застосування можна продемонструвати за допомогою будь-яких програм, здатних встановлювати з'єднання з мережею через сокети, наприклад, telnet (що, власне, і рекомендується).

основи сокетов

Хоча існує безліч типів сокетів, всі функції сокетів засновані на одному і тому ж базовому принципі - отриманні даних програмою В від програми А. Ці програми можуть працювати на одній і тій же машині із застосуванням взаємодії між процесами (Interprocess Communication - IPC), або на віддалених машинах (таких як Web-сервер і браузери).

Сокети можуть бути надійними, які виконують все необхідне для забезпечення передачі даних з точки А в точку В (TCP). або ненадійними, коли дані передаються без гарантії доставки (UDP).

Сокети також бувають "блокуючими" і "неблокірующіх". Блокують сокети змушують ваше додаток очікувати до тих пір, поки дані стануть доступні, в той час як неблокірующіх сокети цього не роблять. Хоча, як буде показано далі, все сокети двонаправлено, все ж існує різниця між сокетами клієнта і сервера.

Ми з вами розглянемо ТСР-сокети Internet, оскільки вони найбільш широко використовуються на сьогоднішній день. Проте, концепції і приклади коду, наведені тут, можуть бути застосовані до більшості операцій з сокетами.

Створення нового сокета

Незалежно від типу створюваного сокета (клієнтський або серверний), всі вони не започатковано однаковим способом - за допомогою функції socket_create (). Синтаксис цієї функції виглядає наступним чином:

socket_create ($ domain, $ type, $ protocol);

$ Domain - тип створюваного сокета і повинен приймати одне зі значень, перерахованих в таблиці констант доменів для сокетких з'єднань

$ Type - тип взаємодії, яке буде здійснюватися через сокет; допустимі значення наведені в таблиці констант типів сокетів

$ Protocol - протокол, який використовується даними сокетом. Цей параметр може бути будь-яким допустимим номером протоколу (див. Функцію getprotobyname ()) або константою SOL_UDP або SOL_TCP для з'єднань TCP / UDP.

В результаті виконання ця функція або повертає ресурс, який представляє створений сокет, або булевское значення false в разі помилки.

Функція socket_create () - це перший виклик при будь-якій взаємодії сокетів. який ініціалізує ресурс сокета, який використовується в подальших операціях. Отже, сокети можуть використовуватися як локально - для IPC, так і віддалено - в стилі клієнт / сервер. Контекст конкретного застосування сокета називається його доменом. Доступні в РНР домени, що передаються функції socket_create () в параметрі $ domain, задаються константами з таблиці:

Читати дані заданої довжини, або поки не зустрінеться символ нового рядка (\ r або \ n).

У лістингу нижче представлений приклад використання сокетів для вилучення індексного сторінки Web-сайту, в нього включено все, що розглядалося вище. Витяг індексного сторінки здійснюється відправкою простого GET-запиту HTTP 1.0 з подальшим читанням результату в змінну.

Розглянувши цей простий приклад сокета-клієнта, тепер давайте подивимося на іншу сторону з'єднання - простий сервер на базі сокетів.

Створення серверних сокетів

socket_bind ($ socket, $ address [, $ port]);

Другий крок: налаштувати сокет на прослуховування трафіку на предмет спроб підключення до нього. Це робиться за допомогою функції socket_listen ():

Де $ socket - прив'язаний раніше сокет, який повинен бути включений на прослушінаніе. Необов'язковий параметр $ backlog використовується для створення черги за допомогою вказівки максимально допустимого числа вхідних підключень, які розміщені в чергу. Якщо цей параметр не вказано, то сокет, який намагається підключитися, отримає відмову в обслуговуванні, поки серверний сокет недоступний. В результаті виконання ця функція повертає булевское значення, яке вказує на успішність налаштування серверного сокета на прослуховування.

Третій і останній крок у створенні серверного сокета - дати команду на прийом вхідних підключень. Це робиться функцією socket_accept ():

Де $ socket - прив'язаний сокет, включений на прослуховування, який повинен приймати з'єднання.

При виконанні ця функція не поверне управління до тих пір, поки не завершиться очікування вхідних підключень. Як тільки воно буде встановлено, функція поверне новий сокетних ресурс, який використовується для підключення. Якщо вказаний в параметрі $ socket сокет налаштований як неблокірующій, функція socket_accept () завжди негайно буде повертати false.

Сокетних ресурс, повернутий функцією socket_accept (), не може бути повторно використаний, оскільки він обслуговує тільки одне певне Ви зараз приєднані. Сокет, переданий їй в параметрі $ socket, однак, може бути використаний повторно.

У лістингу нижче створюється простий сокетних сервер, який приймає єдине підключення, максимум 1024 байти вхідного потоку і відображає цей потік користувачеві.

Створення простого сервера на основі сокета

У мене на компі цей скрипт лежить в папці Денвера по шляху: C: \ WebServers \ home \ app.loc \ www \ sockets \ test.php
Тепер, якщо запустити наш скрипт з командного рядка таким чином: C: \ WebServers \ home \ app.loc \ www \ sockets> php test.php У командному рядку ми побачимо наступне:

Сервер у відповідь пришле нам наш же запит + заголовки:

У командному рядку ми побачимо нові дані

А так же нове запрошення введення - свідоцтво про те, що процес отрубился. Це можна так само перевірити командою netstat -a і Убуд, що порт 4545 в списку не присутня.

Щоб створити сервер, сокети якого ведуть прослуховування на портах з номерами нижче 1000, даний користувач повинен мати в системі адміністративні права. Також слід зазначити, що наведений вище сценарій не завершить роботу до тих пір, поки не буде встановлено з'єднання, що може створити враження "зависання".

Одночасна робота з декількома сокетами

У лістингу, на попередній сторінці було представлено сервер на базі сокетів. Однак він не дуже зручний для реальних, цілей, оскільки до нього може виконуватися тільки одне підключення одночасно. Щоб створити більш зручний сервер сокетов, необхідно навчитися працювати одночасно з безліччю гнізд. Щоб зробити це, знадобиться функція socket_select (), синтаксис якої виглядає наступним чином:

socket_select ($ read, $ Write, $ Error, $ sec [, $ usec]);

Тут $ read, $ write і $ error - передані за посиланням змінні (точніше, масиви). Ці масиви повинні містити список всіх гнізд, за якими потрібно спостерігати на предмет читання, записи і перехоплення помилок відповідно. Наприклад, приміщення активного сокета в масив, який передається в параметрі $ read, змушує РНР перевіряти, чи є в цьому сокеті дані для читання. Останні два параметри - $ sec і необов'язковий $ usec - це значення тайм-ауту, керуючі тим, як довго чекатиме функція socket_select (), перш ніж повернути управління РНР.

В результаті виконання функція socket_select () повертає ціле число, яке вказує загальну кількість змінених сокетов (з переданого списку), і модифікує масиви $ read, $ write і $ error, видаляючи з них ті елементи, які не внесено жодних змін. В результаті кожен з цих масивів буде містити тільки список сокетов, які відповідають таким вимогам:

Сокети, перераховані в масиві $ read, містять дані, що підлягають читання з них, або що входять підключення до них.

Сокети, перераховані в масиві $ write, містять дані, що підлягають записи в них.

Сокети, перераховані в масиві $ error, містять помилки, які потрібно обробити.

У разі помилкового завершення socket_select () повертає булевское значення false.

Цей сокет буде також доданий в масив $ read і буде запущений керований нескінченний цикл. Потім за допомогою функції socket_select () буде організований моніторинг головного сокета на предмет нових підключень. Коли з'являється нове підключення, автоматично викликається функція socket_accept (), що призводить до створення нового серверного сокета, використовуваного для взаємодії з підключеним клієнтом.

Цей новий підключений сокет потім піддається моніторингу через той же виклик socket_select () (за рахунок додавання його до того ж масиву, куди вже доданий наш головний сокет) і реалізується логіка програми, що забезпечує функціональність нашого сервера. У лістингу нижче представлений працюючий приклад простого сервера, що приймає настроюється число підключень.

Створення многосортность сервера на РНР

У лістингу вище розкриті деякі обмеження сценарного механізму РНР, які вимагають кілька складнішого обхідного шляху в формі виклику socket_select ():

$ Num_changed = socket_select ($ read, $ NULL, $ NULL, 0, 10);

Зверніть увагу на застосування змінної з ім'ям $ NULL. У РНР для функцій, які приймають параметри по посиланню (як це і робить socket_select () в першому наближенні), NULL є неприпустимим значенням. Однак передача NULL в якості одного або більше параметрів-списків цілком коректна. Тому обхідний маневр полягає в привласненні змінній $ NULL значення NULL:

І наступною її передачі функції socket_select ().

Схожі статті