В этой статье мы напишем простенький демон на php, но для начала нам надо ознакомиться с сокетами и базовыми знаниями IP-протокола. Итак что же представляет собой сокет? Сокет - это программный интерфейс, предназначенный для передачи данных между приложениями на сетевом уровне. В PHP есть функции работающие с сокетами на уровне IP-протокола. Это гораздо более низкий уровень по сранению с уровнем, на котором работают функция fsockopen и потоки. В этой статье я научу тех, кто пока ещё не умеет работать с этими функциями. Работать будем в режиме CLI (command-line interface) т.е в коммандной строке, а не через браузер.

Что для этого нам понадобиться:

PHP интерпретатор - ну куда ж без него :). Кстати вебсервер apache или какой другой нам не понадобиться!
php_sockets.dll - библиотека (должна быть именно под вашу версию PHP)

Подключение модуля sockets
Для начала нам нужно проверить подключена ли у нас либа php_sockets.dll. Смотрим в php.ini (eg. %WINDIR%\php.ini) и смотрим в раздел "Dynamic Extensions". Там должна быть раскоментирована строчка "extension=php_sockets.dll". Далее смотрим директиву "extension_dir" она должна указывать на путь где лежат все динамически подключаемые модули. Лезем туда и смотрим присутсвует ли библиотека там. Если да то читаем дальше, если нет то скачайте модуль под вашу версию php и положите в эту директорию. Теперь проверим работает ли она. Существует несколько способов проверить это.
Из командной строки запустить php.exe с ключом -m. (php.exe -m);
Скрипт запустить и посмотреть существует ли раздел "sockets";
На хостинге (где у вас нету доступа к php.ini и ssh) можно проверить скриптом:

HTML
Текст

  1.    print "php_sockets.dll - ";
  2.    if(extension_loaded('sockets'))
  3.    {
  4.        print "loaded";
  5.    } else {
  6.        print "not loaded";
  7.    }

Если у вас не подключена php_sockets.dll то смысла читать ниже - нету.
Пишем простой демон
Итак напишем простой демон который будет при подключении выводить какую нибудь рандомную надпись из файла. Принцип сервера-скрипта будет простой:

Создаём TCP сокет;
Привязываем сокет к определённому адресу и порту;
Слушаем сокет;
Клиент конектится, выводим рандомную фразу. Ждём до того пока не он не пошлёт комманду "bye".
Приступим к кодингу, думаю вопросов лишний не возникнет так-как код хорошо комментирован.

HTML
Текст


  1. // Файл содержащий рандомные фразы разделённых "rn" (каждая фраза на новой строке)
  2. $FileName = "file.txt";

  3. $FHandle = file($FileName);

  4. // Работаем вечно (выдаёт ошибку при safe_mode=1, @ для подавления)
  5. @set_time_limit(0);

  6. // Создание сокета TCP: resource socket_create(1, 2, 3);
  7. // 1) AF_INET - семейство протокола или домен. Для соединений
  8. //              осуществляемых через интернет используется AF_INET,
  9. //              для UNIX используется AF_Unix (но об этом позже)
  10. // 2) SOCK_STREAM - обычно используется для TCP (SOCK_DGRAM - UDP)
  11. // 3) Протокол для TCP - SOL_TCP, UDP - SOL_UDP
  12. // возвращает дескриптор сокета
  13. if(($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0)
  14. {
  15.    print("Невозможно создать сокет: " .
  16.        socket_strerror(socket_last_error()) . "n");
  17. }

  18. // Биндим сокет на определённый адрес и порт: boolean socket_bind(1, 2, 3);
  19. // 1) Дескриптор сокета
  20. // 2) IP адрес, или путь до сокета в Unix
  21. // 3) Порт (в нашем случае порт = 666)
  22. if(($error = socket_bind($socket, "127.0.0.1", 666)) < 0)
  23. {
  24.    print("Невозможно привязать сокет :" .
  25.        socket_strerror(socket_last_error()). "n");
  26. }

  27. // Прослушиваем сокет: boolean socket_listen(1, 2);
  28. // 1) дескриптор сокета
  29. // backlog размер очереди запрросов ожидающих соединения
  30. if(($error = socket_listen($socket, 5)) < 0)
  31. {
  32.    print("Невозможно прослушать сокет: " .
  33.        socket_strerror(socket_last_error()) . "n");
  34. }

  35. // Слушаем вечно ;)
  36. while(TRUE)
  37. {
  38.    // ожидаем соединение
  39.    // socket_accept(дескрипток сокета) - принимает входящие соединение и делает на скрипт сервером.
  40.    if(($accept = socket_accept($socket)) < 0)
  41.    {
  42.        print("Ошибка при чтении: " .
  43.            socket_strerror($message) . "n");
  44.        break;
  45.    }

  46.    // выводим рандомную строку из файла
  47.    socket_write($accept, getRandMessage($FHandle));
  48.    print(date("Y-m-d H:i:s", time())." STATUS: client connected.n");
  49.    ob_flush();
  50.    while(TRUE)
  51.    {
  52.        // Считываем заданное количество байт из указанного сокета
  53.        if(FALSE === ($line = @socket_read($accept, 2048)))
  54.        {
  55.            print("Невозможно прослушать сокет: " .
  56.                socket_strerror(socket_last_error()) . "n");
  57.            break 2;
  58.        }

  59.        switch(strtolower(trim($line)))
  60.        {
  61.            case "bye"  :
  62.                print(date("Y-m-d H:i:s", time())." STATUS: client close connection.n");
  63.                break 2;
  64.            break;
  65.            case "more" :
  66.                // записываем данные из буфера в сокет
  67.                if(!@socket_write($accept, getRandMessage($FHandle)."rn"))
  68.                {
  69.                    print(date("Y-m-d H:i:s", time())." STATUS: client close connection.n");
  70.                    break 2;
  71.                }
  72.            break;
  73.            default :
  74.                // записываем данные из буфера в сокет
  75.                if(!@socket_write($accept, "Unknown command, 'bye' to exit.rn"))
  76.                {
  77.                    print(date("Y-m-d H:i:s", time())." STATUS: client close connection.n");
  78.                    break 2;
  79.                }
  80.            break;
  81.        }
  82.        print(date("Y-m-d H:i:s", time()). " READ: ".$line."n");
  83.        ob_flush();
  84.    }
  85.    // закрываем соединение
  86.    socket_close($accept);
  87. }
  88. // Закрываем сокет
  89. socket_close($socket);

  90. function getRandMessage(&#038;$Array)
  91. {
  92.    return ($Array[rand(0, count($Array)-1)]);
  93. }

Думаю многое понятно. Назовём его например socket.php. Теперь запускаем наш скрипт-сервер командой: php socket.php в командной строке.
Тестируем приложение
Для начала выведем все активные TCP соединения

C:\Documents and Settings\t3rr4n>netstat -o -a -p TCP

Активные подключения

Имя Локальный адрес Внешний адрес Состояние PID
TCP work-012f823131:http work-012f823131:0 LISTENING 2112
TCP work-012f823131:epmap work-012f823131:0 LISTENING 1188
TCP work-012f823131:microsoft-ds work-012f823131:0 LISTENING 4
TCP work-012f823131:doom work-012f823131:0 LISTENING 1552 < -- это мы висим
TCP work-012f823131:1029 work-012f823131:0 LISTENING 884
TCP work-012f823131:1048 work-012f823131:0 LISTENING 2032
TCP work-012f823131:3306 work-012f823131:0 LISTENING 2444
TCP work-012f823131:1046 205.188.8.253:https ESTABLISHED 2032
TCP work-012f823131:netbios-ssn work-012f823131:0 LISTENING 4

DOOM это ассоциация с портом "666" (в игре "DooM" используется именно этот порт ;) ). Т.е наше приложение ожидает подключение. Проверим что это за приложение запущено по PID 1552:
C:\Documents and Settings\t3rr4n>tasklist | find "1552"
php.exe 1552 Console 0 4а064 КБ

C:\Documents and Settings\t3rr4n>tasklist | find "php.exe"
php.exe 2716 Console 0 812 КБ
php.exe 1552 Console 0 4а064 КБ

Теперь попробуем подключиться и посылать комманды (на рис. всё понятно). Я использовал известную программу NetCat, вы можете Telnet но лучше NC:
C:\Documents and Settings\t3rr4n>nc 127.0.0.1 666
Хай!

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



Постоянные ссылки

При копировании ссылка на TeaM RSN обязательна!

URI

Html (ЖЖ)

BB-код (Для форумов)

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.