Модераторы: skyboy, MoLeX, Aliance, ksnk
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Сокеты в PHP. Пособие для начинающих. статья 
:(
    Опции темы
AztEK
Дата 13.7.2006, 15:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 723
Регистрация: 4.1.2005

Репутация: 6
Всего: 18



Сокеты в PHP. Пособие для начинающих.

Сокеты как таковые (не только реализованные в PHP) – мощнейший инструмент сетевого программирования. Они позволяют передавать и получать данные на прикладном уровне. Наиболее частое использование сокетов в контексте web-программирования - это работа со страницами, расположенными на разных хостах и взаимодействие с различными типами протоколов, используемых в Интернете (HTTP, FTP, SMTP, IMAP и проч.).

Для взаимодействия с сокетами в PHP чаще всего используется функция fsockopen, синтаксис которой приведён ниже.

resource fsockopen ( string $host [, int $post [, int &$errno [, string &$errstr [, float $timeout]]]] )
$host - хост, к которому будет производиться подключение
$port - порт, по которому будет производиться подключение
$errno - переменная, в которую запишется номер ошибки если таковая произойдёт
$errstr - переменная, в которую запишется текст ошибки если таковая произойдёт
$timeout - время в течении которого мы будем будет производиться попытка соединения с хостом

Мы разберём подключение к серверу по 80-му порту и использование протокола HTTP.
Поэтому хотя бы основы HTTP надо знать (в общем то любому web-программисту надо их знать). Спецификацию HTTP можно скачать по ссылке, расположенной в конце статьи.

Больше не будет теоретизировать, сразу перейдём к практике.

Распространённые примеры использования сокетов.

Пример 1. Передаём данные и получаем страницу.

Создайте php-скрипт следующего содержания:

Код

<?php
echo 'Browser: ' . $_SERVER['HTTP_USER_AGENT'] . "\r\n";
echo 'Referer: ' . $_SERVER['HTTP_REFERER'] . "\r\n";
echo 'Cookie: ' . $_COOKIE['test'] . "\r\n";
?>


Он, как нетрудно догадаться, выводит переменные, которые мы передадим в теле HTTP-запроса: строку User-Agent(где клиент говорит о том, какой у него браузер и ОС), HTTP-Referer(откуда он пришёл) и тестовую cookie (они ведь тоже передаются в HTTP).

Теперь напишем скрипт обращения к странице и передачи переменных.
Код

<?php
$fp = fsockopen('localhost', 80); //Подсоединяемся с хосту "localhost" на 80-й порт
if($fp)
{
    //Соединение создано, всё в порядке
    fputs($fp, "GET /test_sock.php HTTP/1.0\nUser-Agent: У меня Firefox 1.5 и Windows XP\nReferer: Я пришёл с microsoft.com\nCookie: test=test_cookie\n\n");
    
    echo '<pre>'; //Что бы нам было лучше видно заголовки, которые отдаст сервер
    while(!feof($fp)) echo fgets($fp); //Выводим ответ сервера
    echo '</pre>';
    
    fclose($fp); //Закрываем сокет
}
?>


Разберём построчно этот пример.
$fp = fsockopen('localhost', 80);
Открываем сокет с идентификатором $fp и присоединяемся к хосту localhost (напомню, что получить имя хоста из URL можно функцией gethostbyname). Обратите внимание – перед названием хоста НЕ НАДО писать http:// - мы вполне бы могли работать и не с этим протоколом.

Теперь, когда создан сокет, мы может получать и передавать информацию по нему с помощью функций, используемых для работы с файлами (fread(), fwrite(), fgets(), feof()).
Этим и займёмся.

fputs($fp, "GET /test_sock.php HTTP/1.0\nUser-Agent: У меня Firefox 1.5 и Windows XP\nReferer: Я пришёл с microsoft.com\nCookie: test=test_cookie\n\n");
С помощью функции fputs передаём данные, используя формат HTTP. Давайте разберём, что же там написано.

Код

GET /test_sock.php HTTP/1.0
User-Agent: У меня Firefox 1.5 и Windows XP
Referer: Я пришёл с php.net
Cookie: test=test_cookie;
-- два переноса строки --


В первой строке мы указываем, какую страницу на сервере мы хотим получить (/test_sock.php). Соответственно, если бы нам была нужна страница, расположенная в директории test на сервере, мы бы написали так: /test/test_sock.php, а если бы была нужна главная страница сервера, напишем просто /. HTTP/1.0 показывает, какую версию протокола HTTP мы будем использовать, обращаясь к серверу.
В следующих строках мы будем передавать серверу информацию в виде
Код

Header: value

Это называется заголовок HTTP-запроса.
Вот распространённые значения (все возможные значения можно прочитать в спецификации HTTP):

User-Agent
Пример: User-Agent: Mozilla/4.5 [en] (Win95; I)
Строка, описывающая браузер и ОС.
На основе этого заголовка создается переменная окружения  HTTP_USER_AGENT.

Referer
Пример: Referer: php.net
URL страницы, с которой клиент перешел на данную страницу.
На основе этого заголовка создается переменная окружения  HTTP_REFERER.
Как видите,  не стоит сильно доверять этой переменной при написании систем авторизации, поскольку клиент (или хакер) может передать в этом заголовке всё что угодно.

Accept
Пример: Accept: text/html, text/plain, image/gif, image/jpeg
Эта строка используется браузером, что сказать серверу, какие типы данных он понимает
На основе этого заголовка создается переменная окружения  HTTP_ACCEPT_ENCODING.

Cookie
Пример: Cookie: a=1;b=2;c=3;
Передаём cookie.
На основе этого заголовка создается глобальный массив $_COOKIE.

После того, как мы передали все необходимые нам переменные, идут подряд два символа перевода строки(\n\n). Это означает, что всё, что клиент хотел сказать серверу, он сказал и теперь ожидает ответа.

while(!feof($fp)) echo fgets($fp);
Используя функции feof() и fgets(), выводим ответ сервера на наш запрос. У моего сервера ответ получился такой:
Код

HTTP/1.1 200 OK
Date: Thu, 13 Jul 2006 05:52:49 GMT
Server: Apache/1.3.24 (Win32)
X-Powered-By: PHP/4.3.10
Connection: close
Content-Type: text/html

Browser: У меня Firefox 1.5 и Windows XP
Referer: Я пришёл с microsoft.com
Cookie: test_cookie


Не буду сдаваться в подробности того, какие заголовки вывел сервер, всё это вы можете прочитать в документации.

Как видите, послать серверу можно что угодно, поэтому дважды подумайте, прежде чем доверять переменным окружения. В умелых руках это серьёзное оружие взлома.

P.S. Довольно часто получать  содержание страницы в Интернете пытаются функциями наподобие file() и file_get_contents(). Почему этого не стоит делать, читайте здесь.

Пример 2. Определить, существует ли страница в Интернете.

Довольно популярный вопрос, который обычно пробуют решать примерно так:
Код

<?php
    // Это неправильный способ!
    if (@!implode (file($link))) {
        //значит нет такого
    } 
?>

Сейчас разберёмся, как сделать это грамотнее. Немного отвлечёмся от программирования. Вот вы бродите по Интернету, открываете страницы и вдруг ошиблись при наборе адреса и попали на несуществующую страницу. Что делает ваш браузер? Конечно он хороший браузер и говорит, что такой страницы не существует. В тексте сообщения об ошибке наверняка попадётся число 404. Это даёт нам идею для реализации нашей задачи. Давайте вернёмся немного назад, к предыдущему примеру. Сервер первой строкой отдал нам следующее: HTTP/1.1 200 OK
Что это значит? HTTP/1.1 – это ясно, версия протокола, на котором отдан ответ клиенту. Следующая часть уже интереснее, 200 – это так называемый код состояния, число, используя которое, сервер говорит, что он сделал с нашим запросом и чего нам следует теперь ожидать. В очередной раз отправляю вас к документации по HTTP, где написано, что код 200 означает, что «Запрос был удачно выполнен». Это хорошо. А что возвратит сервер, если страница не найдена?  Правильно, уже знакомое нам 404.
Итак, определить наличие страницы в Интернете можно так:
Код

<?php
$fp = fsockopen('localhost', 80); //Подсоединяемся с хосту "localhost" на 80-й порт
if($fp)
{
    //Соединение создано, всё в порядке
    fputs($fp, "GET /non_existed_page.html HTTP/1.0\n\n");
    $request = fgets($fp); //Нас интересует только первая строчка
    fclose($fp); //Закрываем сокет
    //Теперь будем смотреть, что нам отдал сервер
    sscanf($request, '%s %s %s', $protocol, $status_code, $description);
    if($status_code == '404') echo 'Страница не найдена!';
}
?>


Справедливости ради, следует отметить, что сервер, не найдя страницу может и не возвращать 404. Вполне возможно, что он редиректнет нас, причем возвратит код 200.
Здесь приведён простейший пример, в реальных условиях, возможно, потребуются дополнительные проверки.

Пример 3. Передаём данные на сервер методом POST, не используя HTML-форму.

Самый животрепещущий вопрос на тему сокетов  smile .
Добавьте в скрипт, к которому мы обращались в прошлом примере строчку

echo 'POST: ' . $_POST['test'] . "\n";

Сейчас будем ловить POST-данные.

Теперь наш запрос к серверу будет выглядеть таким образом:
Код

<?php
$fp = fsockopen('localhost', 80); //Подсоединяемся с хосту "localhost" на 80-й порт
if($fp)
{
    //Соединение создано, всё в порядке
    fputs($fp, "POST /test_sock.php HTTP/1.0\nUser-Agent: У меня Firefox 1.5 и Windows XP\nReferer: Я пришёл с microsoft.com\nCookie: test=test_cookie;\nContent-Type: application/x-www-form-urlencoded\nContent-length: 14\n\ntest=test_post");
    
    echo '<pre>'; //Что бы нам было лучше видно заголовки, которые отдаст сервер
    while(!feof($fp)) echo fgets($fp); //Выводим ответ сервера
    echo '</pre>';
    
    fclose($fp); //Закрываем сокет
}
?>


Разберём этот запрос. Вот он:

Код

POST /test_sock.php HTTP/1.0
User-Agent: У меня Firefox 1.5 и Windows XP
Referer: Я пришёл с microsoft.com
Cookie: test=test_cookie;
Content-Type: application/x-www-form-urlencoded
Content-length: 14
-- два переноса строки --
test=test_post


Рассмотрим, чем этот запрос отличается от запроса из предыдущего примера.
Прежде всего,  в первой строке используется "POST", а не "GET". Это значит, что после двух переносов строки будут идти данные, которые сервер, возможно, передаст PHP-интерпретатору. Кроме того, среди заголовков есть два новых для нас.

Content-length
Пример: Content-length: 2452
Длина данных, которые, грубо говоря, будут переданы после двух переносов строки. Используется, когда сервер отвечает на запрос или, как в нашем случае, если передаются POST-данные. Размер переданных данных должен быть обязательно равен этому значению.
На основе этого заголовка создается переменная окружения  CONTENT_LENGTH.

Content-Type
Пример: Content-Type: text/html
Задаёт тип документа и его кодировку. Для передачи данных методом POST мы использовали application/x-www-form-urlencoded.

Чтобы передать несколько переменных методом POST, перечислите их через символ «&» (совсем как в GET), например: 

Код

POST /test_sock.php HTTP/1.0
User-Agent: У меня Firefox 1.5 и Windows XP
Referer: Я пришёл с microsoft.com
Cookie: test=test_cookie;
Content-Type: application/x-www-form-urlencoded
Content-length: 14
-- два переноса строки --
test=test_post&test2=tes2&test3=test3


Полезные ссылки

Википедия
Спецификация HTTP1.0
Спецификация HTTP1.1
Описание функции fsockopen (php.net)
Сокеты в PHP (php.net)
Почему нельзя использовать fopen, file_get_contents при работе с HTTP?

Заключение
Следует отметить, что подключаться к хосту можно совсем не обязательно на 80-й порт и работать не с протоколом HTTP. Использовать сокеты можно и для работы с FTP, почтой, ICQ и проч. Главное знать, какой порт и протокол использовать  в том ли ином случае.

Мы разобрали взаимодействие сокетов с HTTP-протоколом, самый простой и очевидный.
Удачи вам в программировании!

С уважением, AztEK.
   

Это сообщение отредактировал(а) AztEK - 13.7.2006, 17:15


--------------------
Linux is like wigwam -- no windows, no gates, apache inside.
PM MAIL Jabber   Вверх
GiV
Дата 13.7.2006, 16:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 137
Регистрация: 13.7.2006
Где: rus|53

Репутация: 4
Всего: 9



Небольшое уточнение:
Формат функции
Цитата(AztEK @  13.7.2006,  15:22 Найти цитируемый пост)
int fsockopen(string $host, int $port [,int &$errno] [,string &$errstr])

несколько не верен.

resource fsockopen ( string target [, int port [, int &errno [, string &errstr [, float timeout]]]] )

Во-первых, функция возвращает ресурс. Т.е. идентификатор соединения.
И опущен параметр timeout, он устанвливает время в течении которого мы будем пытаться соединится с ресурсом. Если в течении указанного времени соединение установить не удалось, идентификатор соединения не будет создан. 
PM MAIL WWW ICQ   Вверх
AztEK
Дата 13.7.2006, 17:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 723
Регистрация: 4.1.2005

Репутация: 6
Всего: 18



GiV, да, спасибо. Действительно, это ошибка. Зачем то взял BNF функции из Котерова, а не c php.net.
Исправил. 


--------------------
Linux is like wigwam -- no windows, no gates, apache inside.
PM MAIL Jabber   Вверх
AztEK
Дата 15.7.2006, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 723
Регистрация: 4.1.2005

Репутация: 6
Всего: 18



Что, больше никто ничего не может сказать? smile  


--------------------
Linux is like wigwam -- no windows, no gates, apache inside.
PM MAIL Jabber   Вверх
nerezus
Дата 15.7.2006, 16:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вселенский отказник
****


Профиль
Группа: Участник
Сообщений: 3330
Регистрация: 15.6.2005

Репутация: 12
Всего: 43



Если честно, то тема абсолютно не раскрыта =(

А я то уж было хотел неблок. сокеты почитать, открываю тему... и облом... 


--------------------
Сообщество художников Artsociety.ru
PM MAIL WWW   Вверх
MoLeX
Дата 26.1.2008, 10:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Местный пингвин
****


Профиль
Группа: Модератор
Сообщений: 4076
Регистрация: 17.5.2007

Репутация: 46
Всего: 140



Неплохая статья на мой взгляд. держи +1

Это сообщение отредактировал(а) MoLeX - 26.1.2008, 10:33


--------------------
Amazing  smile 
PM MAIL WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "PHP"
Aliance
IZ@TOP
skyboy
SamDark
MoLeX

Новичкам:

  • PHP редакторы собираются и обсуждаются здесь
  • Электронные книги по PHP, документацию можно найти здесь
  • Интерпретатор PHP, полную документацию можно скачать на PHP.NET

Важно:

  • Не брезгуйте пользоваться тегами [code=php]КОД[/code] для повышения читабельности текста/кода.
  • Перед созданием новой темы воспользуйтесь поиском и загляните в FAQ
  • Действия модераторов можно обсудить здесь

Внимание:

  • Темы "ищу скрипт", "подскажите скрипт" и т.п. будут переноситься в форум "Web-технологии"
  • Темы с именами: "Срочно", "помогите", "не знаю как делать" будут УДАЛЯТЬСЯ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, IZ@TOP, skyboy, SamDark, MoLeX, awers.

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


 




[ Время генерации скрипта: 0.1225 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.