PHP 2016. Уровень 3.5: Сокеты и сетевые службы
Использование сетевых протоколов. конспект-памятка видеокурса 'Программирование на PHP 2016'. Лектор Борисов Игорь Олегович. Учебный Центр «Специалист» при МГТУ им. Н.Э.Баумана.
конспектировал Капустин Яков
оглавление
- 01 Сокеты
- 02 Использование сокетов
- 03 Сетевые функции
01Сокеты
При вызове функций файловой системы, таких как fopen(), copy(), file_exists() и filesize(), именованный ресурс, указанный в аргументе filename, закрепляется за потоком. Иными словами, устанавливается соединение с файлом (так как ВСЁ ЕСТЬ ФАЙЛ!). Локальным или удалённым.
Для установления соединения интерпретатору необходимо выяснить тип ресурса (различные типы требуют различных телодвижений).
Если filename передан в форме 'scheme://...', он считается URL'ом и PHP проведёт поиск обработчика протокола (также известного как 'wrappers' - обертка) для этой схемы.
- file:// — Доступ к локальной файловой системе
- http:// — Доступ к URL-адресам по протоколу HTTP(s)
- ftp:// — Доступ к URL-адресам по протоколу FTP(s)
- php:// — Доступ к различным потокам ввода-вывода
- zlib:// — Сжатые потоки
- data:// — Схема Data (RFC 2397)
- glob:// — Нахождение путей, соответствующих шаблону
- phar:// — PHP-архив
- ssh2:// — Secure Shell 2
- rar:// — RAR
- ogg:// — Аудиопотоки
- expect:// — Потоки для взаимодействия с процессами
В дополнение к этим оберткам, можно регистрировать собственные обертки, используя функцию stream_wrapper_register().
Если ни одна обёртка не закреплена за протоколом, PHP выдаст замечание, чтобы помочь вам отследить потенциальную проблему в вашем скрипте и затем продолжит выполнение, как если бы filename указывал на обыкновенный файл.
В труднопредставимых ситуациях, когда мы не можем (или не хотим) регистрировать собственные обертки возможно установить соединение практически вручную.
«Для этого есть сокет - непосредственно само соединение как таковое.» (с) ОлегБорисыч
Сокеты позволяют осуществить подключение к службе, для которой отсутствует соответствующая обёртка (file wrapper) и выполнение действий, невозможных при использовании потоков, но возможных при использовании сетевых протоколов.
Сокеты представляют собой чрезвычайно удобную, но в то же время плохо понятую технологию взаимодействия между двумя процессами в сети. Эти процессы могут существовать на одной и той же машине, общаясь друг с другом через локальный сокет, предназначенный для взаимодействия между процессами, либо на разных машинах через Internet.
Расширение socket реализует низкоуровневый интерфейс для функций связи сокетов, основанный на популярных сокетах BSD для обеспечения возможности взаимодействия сервера и клиента.
Для более общего клиентского интерфейса сокетов используются stream_socket_client(), stream_socket_server(), fsockopen() и pfsockopen(). При использовании этих функций, важно помнить, что хотя многие из них имеют имена, похожие на их аналоги в C, они часто имеют другой интерфейс использования.
При подключении посредством вебсокетов происходит обмен заголовками наподобие заголовков HTTP, так называемый handshake или по-нашему «рукопожатие». Любой код статуса, отличный от 101 будет означать что «рукопожатие» не завершено.
Функции сокета можно посмотреть здесь.
Хотя существует множество типов сокетов, все функции сокетов основаны на одном и том же базовом принципе — получении данных программой В от программы А. Эти программы могут работать на одной и той же машине с применением межпроцессного взаимодействия (Interprocess Communication — IPC), либо на удаленных машинах (таких как Web-сервер и браузеры).
Сокеты могут быть надежными, выполняющими все необходимое для обеспечения передачи данных из точки А в точку В (TCP), либо ненадежными, когда данные передаются без гарантии доставки (UDP).
Все сокеты двунаправлены, но существует разница между сокетами клиента и сервера. Независимо от типа создаваемого сокета (клиентский или серверный), все они инициализируются одинаковым способом — с помощью функции socket_create().
Для использования сокетов РНР потребуется расширения поддержки сокетов.
2
3
4
5
6
7
if(extension_loaded('sockets')){
echo "WebSockets AVAILABLE";
}else{
echo "WebSockets UNAVAILABLE";
}
02Использование сокетов
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Открытие соединения с интернет-сокетом или доменным сокетом Unix
// fsockopen(string $hostname [, int $port = -1 [, int &$errno [, string &$errstr [, float $timeout = ini_get("default_socket_timeout") ]]]] ) : resource
$socket = fsockopen("mysite.local", 80, $errno, $errmsg, 30);
if(!$socket){
echo "$errno : $errmsg";
}else{
// Поготовка запроса
$output = "HEAD /server.php HTTP/1.1\r\n";
$output .= "Host: mysite.local\r\n";
$output .= "Connection: close\r\n\r\n";
// Посылаем запрос
fwrite($socket, $output);
// Читаем ответ
while(!$feof($socket)){echo $fgets($socket);}
// Закрываем сокет
$fclose($socket);
}
Сокеты позволяют осуществить доступ к используемым сетевым протоколам. Одним из вариантов соединения с передачей параметров является рассмотренный ниже пример.
Создадим некоторый файл, ожидающий входящие данные, переданные методом POST.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$name=strip_tags($_POST["name"]);
$age=$_POST["age"]*1;
if($_SERVER["REQUEST_METHOD"]=="POST"){
if($name and $age){
$res="<h1>Привет, $name, мы не виделись $age лет.</h1>";
}
}else{$res="кто здесь?";}
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<?php
// выводим запрос
// echo "\$GLOBALS=<pre>".print_r($GLOBALS,true)."</pre><hr>";
echo $res;
?>
</body>
</html>
Выведем его во фрейме:
2
3
echo '<iframe src="https://yakoffka.ru/src/conspects/_php35/example_socket.php" width="100%" height="100"></iframe>';
Передадим требуемые параметры с использованием сокетов:
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
$errno=""; // в случае ошибки системного вызова функции connect()
// будет принимать номер этой ошибки.
$errstr=""; // сообщение об ошибке в виде строки.
$socket=fsockopen(
"yakoffka.ru",
80,
$errno,
$errstr,
30
);
if($socket){
// Создаём POST-строку
$str_query="name=Jakob&age=7";
// Собираем запрос
$out="POST /src/conspects/_php35/example_socket.php HTTP/1.1\r\n";
$out.="Host: yakoffka.ru\r\n";
$out.="Content-Type: application/x-www-form-urlencoded\r\n";
$out.="Content-lenght: ".strlen($str_query)."\r\n\r\n";
$out.=$str_query;
// Посылаем запрос
// fwrite($socket,$out);
fputs($socket,$out);
// выводим запрос
echo "\$out=<pre>$out;</pre><hr>";
// Получаем и выводим ответ
echo "<pre>";
while(!feof($socket)){
echo fgets($socket);
}
echo "</pre>";
}else{
echo "Произошла ошибка №$errno: $errstr.";
}
// Закрытие соединения
fclose($socket);
POST /src/conspects/_php35/example_socket.php HTTP/1.1 Host: yakoffka.ru Content-Type: application/x-www-form-urlencoded Content-lenght: 16 name=Jakob&age=7;
HTTP/1.1 301 Moved Permanently Server: openresty Date: Tue, 15 Jan 2019 03:23:05 GMT Content-Type: text/html; charset=iso-8859-1 Content-Length: 334 Connection: keep-alive Location: https://yakoffka.ru/src/conspects/_php35/example_socket.php301 Moved Permanently Moved Permanently
The document has moved here.
Apache/2.4.6 Server at yakoffka.ru Port 80 HTTP/1.1 400 Bad Request Server: openresty Date: Tue, 15 Jan 2019 03:23:05 GMT Content-Type: text/html Content-Length: 170 Connection: close400 Bad Request 400 Bad Request
openresty
Приведённый фрагмент является замороженным выводом, тк выполнение данного кода занимает слишком много времени.
03Сетевые функции
PHP предоставляет различные функции для работы с сетью. Для использования этих функций не требуется проведение установки, поскольку они являются частью ядра PHP. Поведение этих функций зависит от установок в php.ini.
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Получаем имя хоста по ip-адресу
$host_name = gethostbyaddr("127.0.0.0");
// Получаем ip-адрес по имени хоста
$ip_address = gethostbyname("mysite.local");
// Получаем массив ip-адресов по имени хоста
$ip_addresses = gethostbynamel("mysite.local");
// Получаем номер порта по имени службы
$port = getservbyname("http", "tcp");
// Получаем имя службы по номеру порта
$service = getservbyport(80, "tcp");
// Получаем DNS запись для указанного хоста
$dns_record = dns_get_record("mysite.local");
// Получаем MX запись для указанного хоста
$dns_record = getmxrr("mysite.local");
// Проверяем имя хоста на существование
$exists = checkdnsrr("mysite.local");
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
// Получаем ip-адрес по имени хоста
$ip_address = gethostbyname("yakoffka.ru");
// Получаем имя службы по номеру порта
$service = getservbyport(80, "tcp");
// Проверяем имя хоста на существование
$exists = checkdnsrr("yakoffka.ru");
echo "
ip_address=$ip_address;<br>
service=$service;<br>
exists=$exists;<br>
";
service=http;
exists=1;
Капустин Яков (2019.01.13 11:05)