Когда говорят сокет, то часто представляют себе что-то абстрактное, и связанное с сетевыми передачами. Но если выразится конкретнее, то получится что сокет, например, как протокол IP, это совокупность адреса (хоста) и порта. Адрес (хост) на текущий момент представляет из себя 32-x битный адрес. Наиболее часто его представляют в символьной форме mmm.nnn.ppp.qqq (адрес, разбитый на четыре октета по одному байту в октете и разделенный точками). Порт - это номер порта в диапазоне от нуля до 65535. Так вот, эта пара и есть сокет (гнездо в в котором расположены адрес и порт). В процессе обмена, как правило используются два сокета - сокет отправителя и сокет получателя.
PHP позволяет скрипту соединиться с помощью сокетов к любому хосту и порту с помощью функции fsockopen. Функция fsockopen предназначена для соединения клиента с сервером. Она очень удобна, поскольку является универсальной и объединяет в себе те функции, которые есть в PHP для работы с сокетами.
Что же делают сокеты? С помощью сетевых протоколов они обмениваются между собой запросами. Существуют такие виды сетевых протоколов:
TCP - протокол управления передачей (Transmission Control Protocol).
TCP/IP - (Transmission Control Protocol/Internet Protocol) - набор сетевых протоколов, позволяющих компьютерам (различных аппаратных платформ и с разными операционными системами) взаимодействовать по соединенным сетям и через Интернет. Любой компьютер в Интернете поддерживает tcp/ip. Это базовый протокол, используемый Internet.
UDP - дополнительный компонент протокола tcp, поддерживающий выполняющуюся без подключений службу датаграмм, не гарантирующую ни доставку, ни правильную последовательность доставленных пакетов (аналогично протоколу ip).
FTP - один из протоколов tcp/ip, используемый для копирования файлов с одного компьютера на другой через Интернет. При этом оба компьютера должны поддерживать соответствующие роли ftp: один должен быть клиентом ftp, а другой - сервером ftp.
SSL - протокол, обеспечивающий защиту передаваемых данных с помощью шифров.
TLS - стандартный протокол, используемый для обеспечения безопасности веб коммуникаций в Интернете и интрасетях. Позволяет клиентам проверять подлинность серверов или (необязательно) серверам проверять подлинность клиентов. Также обеспечивает безопасный канал за счет шифрования соединений. Протокол tls является самой последней и более безопасной версией протокола ssl.
SMTP (Simple Mail Transfer Protocol) - что означает простой протокол передачи электронной почты;
POP (Post Office Protocol) - почтовый протокол для приема электронной почты.
В свою очередь в семействе TCP/IP существуют следующие протоколы:
HTTP - (Hypertext Transfer Protocol) протокол прикладного уровня передачи данных в первую очередь в виде текстовых сообщений. Основой http является технология «клиент-сервер», то есть клиенты посылают запрос, а серверы, которые ожидают соединения для получения запроса, производят необходимые действия и возвращают обратно сообщение с результатом. Порт сервера по умолчанию 80 или 8080.
HTTPS - расширение протокола http, поддерживающее шифрование. Https не является отдельным протоколом. По сути это обычный http, работающий через шифрованные транспортные механизмы ssl и tls. Порт сервера по умолчанию 443.
Давайте теперь еще раз определим, что такое сокеты с точки зрения интернета. Распространенным протоколом передачи информации между клиентами (веб браузеры, например Internet Explorer, Mozilla Firefox, Opera, Google Chrome и др.) и серверами (удаленные сайты) является HTTP.
Принцип работы протокола HTTP - обмен текстовыми запросами (сообщениями). Таким образом, сокеты позволяют отправить на определенный хост (адрес) и номер порта текстовое сообщение, а в ответ получить другое текстовое сообщение.
Теперь необходимо разобраться, что это за текстовое сообщение и из чего оно состоит. Я не буду описывать всю
------------------------------------------
Запрос клиента:
GET /index.php HTTP/1.1
Host: master-web.com.ua
User-Agent: Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9b5) Gecko/2008050509 Firefox/3.0b5
Accept: text/html
Connection: close
------------------------------------------
Ответ сервера:
HTTP/1.0 200 OK
Date: Wed, 11 Feb 2009 11:20:59 GMT
Server: Apache
X-Powered-By: PHP/5.2.4-2ubuntu5wm1
Last-Modified: Wed, 11 Feb 2009 11:20:59 GMT
Content-Language: ru
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Connection: close
(далее следует код запрошенной страница в HTML)
------------------------------------------
Запрос клиента начинается с команды GET. Каждый сервер должен поддерживать как минимум две команды GET и HEAD. Посмотрите какие еще существуют основные команды:
GET - используется для запроса содержимого указанного ресурса (страницы). В результате этой команды сервер должен выполнить запрос и возвратить ответ. Как правило, все клиенты (браузеры) для получения страницы сайта по url используют get.
HEAD - аналогичен методу GET, за исключением того, что в ответе сервера отсутствует тело. Другими словами обратно Вы получите только заголовок ответа сервера, но не код страницы.
POST - применяется для передачи пользовательских данных заданному ресурсу. Эти данные могут быть в виде файла или просто текста. Как правило, название переменной и ее значение.
TRACE - возвращает полученный запрос так, что клиент может увидеть, что промежуточные сервера добавляют или изменяют в запросе.
Разбираем запрос клиента дальше.
После команды серверу идет часть URI запроса относительно хоста. Например, в данном случае мы запрашиваем содержимое страницы index.php, которая находится в корневом каталоге хоста. Но мы также, можем запросить и такой URI: /info/price.php?type=1 и он будет прекрасно работать.
Далее, в первой строке запроса клиента следует указание вида протокола, в данном случае он указан как HTTP/1.1 (может быть и HTTP/1.0 и HTTP/0.9), но обычно используют HTTP/1.0.
В строке (Host:) указывается название хоста к которому мы отправляем http-запрос.
В строке (User-Agent:) указывается название клиента (в данном случае одна из версий браузера Mozilla).
В строке (Accept:) указываем в каком формате мы хотим получить данные.
В строке (Connection:) мы закрываем соединение. Это является правилом хорошего тона, так как сервер при обращении к нему создает отдельное соединение, которое обслуживает все дальнейшие Ваши запросы. Однако, если его не закрыть ничего не случится, сервер закроет его сам, но через определенный промежуток времени.
Кроме вышеперечисленных строк, существует еще множество других параметров которые можно указать в запросе, но перечислять все нет смысла, Вы можете сами посмотреть вживую на обмен http-запросами между клиент-сервером, например, с помощью дополнительной утилиты (надстройки) к Mozilla Firefox "Tamper Data". Более детально запрос клиента мы еще разберем ниже.
Теперь рассмотрим ответ сервера. Первая строка ответа начинается с указания типа http-протокола, в данном случае это HTTP/1.0. Далее идет статус (код) ответа сервера. Посмотрите на таблицу с диапазонами ответов сервера:
Диапазон кодов | Значение ответа |
100–199 | Информационный |
200–299 | Запрос клиента успешен |
300–399 | Запрос клиента переадресован, необходимы дальнейшие действия |
400–499 | Запрос клиента является неполным |
500–599 | Ошибки сервера |
Список популярных ответов сервера:
Код | Значение ответа |
200 | Запрошенный документ найден и сейчас будет выслан. |
301, 302 | Запрошенный документ навсегда перенесен в другое расположение. При этом в поле Location будет указано новое расположение документа. |
401 | Запрошенный документ присутствует, однако для его получения требуется авторизация на сервере. |
402 | Запрошенный документ (страница) не найден на сервере. |
500 | Произошла внутренняя ошибка сервера. Как правило, такие ошибки возникают при коллизиях между CGI-программой и сервером. |
В нашем примере код ответа сервера имеет значение 200 OK. Это означает, что указанная в клиент-запросе страница найдена и ответ успешно возвращается. Последующие строчки, я думаю, Вам должны быть понятны интуитивно.
Что ж, мы сделали краткое вступление к понятию сокетов в php и теперь можем смело приступить к написанию нашего сокета в php.
Как я уже выше писал, для создания сокетов в php используется функция fsockopen. Посмотрите на этот код:
<?php
$fp = fsockopen("master-web.com.ua", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)
";
} else {
$query = "GET / HTTP/1.1
";
$query .= "Host: master-web.com.ua
";
$query .= "Connection: Close
";
fwrite($fp, $query);
$page = '';
while (!feof($fp)) {
$page .= fgets($fp, 4096);
}
fclose($fp);
if (!empty($page)) echo '<pre>
'.$page.'</pre>
';
?>
Выполните этот код на своем web сервере и Вы получите корневую страницу с хоста master-web.com.ua, а также заголовок ответа сервера.
Функция fsockopen имеет следующие параметры:
"master-web.com.ua" - название домена, к которому осуществляется подключение, можно, также, указать ip-адрес (например, "62.149.26.133");
80 - адрес порта удаленного сервера, к которому подключаемся;
$errno - в случае ошибки соединения в эту переменную заносится номер ошибки;
$errstr - в случае ошибки соединения в эту переменную заносится текстовое объяснение ошибки;
30 - время в миллисекундах на протяжении которого будут осуществляется попытки соединится или timeout по истечении которого попытки соединения будут прекращены.
Функция fwrite($fp, $query) - отправляет запрос $query и записывает ответ в файловый поток $fp.
Функция fgets($fp, 4096) - считывает построчно содержимое файловый поток $fp.
В результате удачного соединения (открытия сокета) переменная $fp обретает значение и мы отправляем текстовый запрос к серверу. Составить правильный текстовый запрос к серверу очень важно, поэтому я детально постараюсь его описать.
Запрос клиента может состоять из пары строк, а может и из десятка строк. Все зависит специфики запроса и того, что Вы сами хотите сообщить серверу. Каждая строка (кроме первой) состоит из параметра и его значения разделенные двоеточием :, например, Host: master-web.com.ua
, Referer: http://google.com/
, Cookie: income=1
и т.д.
В конце строки в обязательном порядке должен быть код переноса строки "
". Часто пишут "
", где "
" - перенос строки, а "
" - перевод каретки в начало строки. Весь http-заголовок заканчивается двойным переносом строки, например Connection: Close
или просто User-Agent: Mozilla/5.0 (compatible; MSIE 6.0; Windows 98)
.
Спросите: зачем необходим двойной перенос строки? Во-первых, согласно спецификации HTTP, двойной перенос означает окончание http-запроса, а во-вторых, вспомните, что POST передача подразумевает передачу все данных не в строке запроса (в виде ..index.php?var1=1&var2=2), а внутри http-запроса:
$query = "POST /e/19 HTTP/1.1
";
$query .= "Host: master-web.com.ua
";
$query .= "Accept-Language: ru
";
$query .= "Expires: Thu, 01 Jan 1970 00:00:01 GMT
";
$query .= "Connection: Close
";
$query .= "var1=1&var2=2";
Как двойной перенос строки "
"
отделяет http-запрос от переменных, так он и отделяет http-ответ сервера от содержимого страницы. Благодаря этому клиенты (браузера) выводят на экран страницу без заголовка ответа.
Перечень параметров, которые могут передаваться серверу:
Accept
Пример: Accept: text/html, text/plain, image/gif, image/jpeg
Эта строка используется клиентом, что сказать серверу, какие типы данных он воспринимает. На основе этого заголовка создается переменная окружения HTTP_ACCEPT_ENCODING.
Accept-Encoding
Пример: Accept-Encoding: bzip2, gzip
Определяет типы сжатия, с которыми может работать наш браузер/скрипт, и которые желательно использовать при передаче документа (если сервер поддерживает данные форматы сжатия).
Accept-Language
Пример: Accept-Language: ru,en-us;q=0.7,en;q=0.3
Означает, какая раскладка клавиатуры у нас установлена и на каком языке должен возвращаться запрошенный документ.
Accept-Charset
Пример: Accept-Charset: koi8-r;q=1.0, iso-8859-5;q=0.9, iso-8859-1;q=0.1
Определяет кодировки, с которыми может работать наш браузер/скрипт, и которые желательно использовать при передаче документа.
Cookie
Пример: Cookie: var1=1;var2=2;type=blob;
Передача cookie, на основе этого заголовка создается глобальный массив $_COOKIE.
Content-length
Пример: Content-length: 1001
Длина данных в байтах, переданных после двух переносов строки. Актуально при передачи данных методом POST или когда сервер отвечает на запрос. Размер переданных данных должен быть обязательно равен этому значению, иначе сервер ничего не будет выполнять, так как посчитает, что получил неполный пакет данных. На основе этого заголовка создается переменная окружения $ _SERVER["CONTENT_LENGTH"].
Content-Type
Пример: Content-Type: text/html;charset=ISO-8859-4
Тип данных, которые передаются в теле запроса. Поле Content-Type не имеет значения по умолчанию.
Keep-Alive
Пример: Keep-Alive: 300
Определяет время в секундах, в течение которого данное соединение с сервером должно удерживаться (не должно разрываться).
Referer
Пример: Referer: info-pages.com.ua
URL страницы, с которой клиент перешел на данную страницу. На основе этого параметра создается переменная окружения $_SERVER["HTTP_REFERER"].
User-Agent
Пример: User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Строка, описывающая браузер и ОС. На основе этого заголовка создается переменная окружения $_SERVER["HTTP_USER_AGENT"].
Теперь Вы можете полноценно обмениваться http-сообщениями с любым сервером, используя сокеты. При этом Вы можете послать ему любое значение одного из заголовков. Например, если Вы включаете в запрос http-заголовок "User-Agent: Opera/7.23 (Windows 98; U) [en]", то сервер будет думать что у Вас установлена Windows 98 и работаете Вы под Operо'й. А если вы укажите такой заголовок "Referer: google.com", то сервер решит, что Вы пришли из Гугл страницы.
Меняя значение заголовков в http-запросах, Вы можете указывать серверу любые параметры. И тогда единственным способом Вашей идентификации в интернете, будет Ваш ip-адрес. Однако в интернете, также существуют и анонимные прокси-серверы, которые могут скрывать реальный ip-адрес. Благодаря этим небольшим хитростям Вы можете посылать http-запросы на прокси-сервер, предварительно конечно проверив его анонимность, и на 99% быть уверенными, что идентифицировать Вас будет задачей довольно сложной.
Но, я думаю, Вы найдете более полезное применение сокетов на практике и создадите, например какой-нибудь полезный скрипт для авторизации на форумах, или почтовых сервисах передавая серверу необходимые данные.