Как написать сервер на php
Перейти к содержимому

Как написать сервер на php

  • автор:

Сокеты: Сервер на PHP

Сокеты: Сервер на PHP

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

Сразу привожу код сервера на PHP с подробными комментариями:

<?php
header(‘Content-Type: text/plain;’); //Мы будем выводить простой текст
set_time_limit(0); //Скрипт должен работать постоянно
ob_implicit_flush(); //Все echo должны сразу же отправляться клиенту
$address = ‘localhost’; //Адрес работы сервера
$port = 1985; //Порт работы сервера (лучше какой-нибудь редкоиспользуемый)
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) <
//AF_INET — семейство протоколов
//SOCK_STREAM — тип сокета
//SOL_TCP — протокол
echo «Ошибка создания сокета»;
>
else <
echo «Сокет создан\n»;
>
//Связываем дескриптор сокета с указанным адресом и портом
if (($ret = socket_bind($sock, $address, $port)) < 0) <
echo «Ошибка связи сокета с адресом и портом»;
>
else <
echo «Сокет успешно связан с адресом и портом\n»;
>
//Начинаем прослушивание сокета (максимум 5 одновременных соединений)
if (($ret = socket_listen($sock, 5)) < 0) <
echo «Ошибка при попытке прослушивания сокета»;
>
else <
echo «Ждём подключение клиента\n»;
>
do <
//Принимаем соединение с сокетом
if (($msgsock = socket_accept($sock)) < 0) <
echo «Ошибка при старте соединений с сокетом»;
> else <
echo «Сокет готов к приёму сообщений\n»;
>
$msg = «Hello!»; //Сообщение клиенту
echo «Сообщение от сервера: $msg»;
socket_write($msgsock, $msg, strlen($msg)); //Запись в сокет
//Бесконечный цикл ожидания клиентов
do <
echo ‘Сообщение от клиента: ‘;
if (false === ($buf = socket_read($msgsock, 1024))) <
echo «Ошибка при чтении сообщения от клиента»; >
else <
echo $buf.»\n»; //Сообщение от клиента
>
//Если клиент передал exit, то отключаем соединение
if ($buf == ‘exit’) <
socket_close($msgsock);
break 2;
>
if (!is_numeric($buf)) echo «Сообщение от сервера: передано НЕ число\n»;
else <
$buf = $buf * $buf;
echo «Сообщение от сервера: ($buf)\n»;
>
socket_write($msgsock, $buf, strlen($buf));
> while (true);
> while (true);
//Останавливаем работу с сокетом
if (isset($sock)) <
socket_close($sock);
echo «Сокет успешно закрыт»;
>
?>

Данный код я постарался тщательно прокомментировать, поэтому, надеюсь, Вам в нём всё понятно. Главное понять следующее: сервер — это непрерывно выполняющийся скрипт, который просто ждёт подключения клиентов. Затем принимающий запрос и на основании этого запроса, возвращающий клиенту ответ. Это самое главное, что Вам необходимо понять.

А в следующей статье мы с Вами напишем клиента на PHP, который и будет подключаться к данному серверу и отправлять различные запросы.

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Она выглядит вот так:

Комментарии ( 14 ):

У меня ошибка, пишет что функции socket_create нет.

Надо включить эту библиотеку. Для этого в php.ini расскоментируйте строчку: extension=php_sockets.dll, затем перезапустите сервер.

Надо включить эту библиотеку. Для этого в php.ini расскоментируйте строчку: extension=php_sockets.dll, затем перезапустите сервер. — Вот я всё по инструкции сделал,а оно мне: <!—error—><br /> <b>Fatal error</b>: Call to undefined function socket_create() in <b>Z:\home\localhost\www\server.php</b> on line <b>7</b><br /> <script language=JavaScript src=’/denwer/errors/phperror_js.php’></script>

Дополнительные модули к Денверу установите (скачайте их с http://denwer.ru).

А что нужно именно скачать,можна ссылку ))

Дополнительные модули нужно скачать: http://www.denwer.ru/packages/php5.html

Ваш совет помог мне включить поддержку сокетов на денвере,спасибо!

После установки дополнительных модулей ошибка сохранилась.

Все отлично, но вот есть вопрос: здесь если мы получили коннект клиента, то пока он не перестанет слать что либо серверу(здесь пока не пошлет exit), другие клиенты будут как бы в очереди и не будут обрабатываться. Есть варианты одновременной обработки нескольких клиентов?

Скажите хоть. Надо ли запускать самостоятельно "server.php" или его просто надо разместить и он сам будет непрерывно работать! Опишите пожалуйста, Может из за денвера моего не х..на не работает!?

Надо запускать и сам он непрерывно работать не будет.

"Главное понять следующее: сервер — это непрерывно выполняющийся скрипт" Зачем тогда вообще эти сокеты нужны? Где они используются?

А как он запускается?

Скажите пожалуйста, а как его запускать. этот сокет сервер на Денвере?

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.

Делаем вебсокеты на PHP с нуля

Некоторое время назад я выбирал библиотеку для работы с вебсокетами. На просторах интернета я натыкался на статьи по интеграции node.js с yii, а почти все статьи о вебсокетах на хабре ограничивались инструкциями к тому, как использовать phpdaemon.

Я изучал библиотеки phpdaemon и ratchet, они достаточно монструозны (причём используя ratchet для отправки сообщения конкретному пользователю рекомендовано дополнительно использовать wamp). Мне не совсем было понятно для чего использовать таких монстров, которые требуют установку других монстров. Почитав исходники этих, а также других библиотек, я разобрался как всё устроено и мне захотелось написать простой вебсокет-сервер на php самостоятельно. Это помогло мне закрепить изученный материал и наткнуться на некоторые подводные камни, о которых я не имел представления.

Так я решил написать необходимый для меня функционал с нуля.

Получившийся код и ссылка на демонстрационный чат в конце статьи.

Поставленные цели:

1) разобраться с серверными сокетами в php
2) разобраться с протоколом вебсокетов
3) написать с нуля простой сервер вебсокетов

1) Серверные сокеты в php

До этого момента я имел смутные представления о серверных сокетах. Почитав исходники нескольких библиотек для работы с вебсокетами я столкнулся с двумя схемами их реализаций:

используя расширение php «socket»:

или используя расширение php «stream»:

Я предпочёл второй вариант ввиду его краткости.

Итак, мы создали серверный сокет и теперь хотим обрабатывать новые соединения к нему, для этого опять же есть два варианта

или с использованием stream_select

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

2) Протокол вебсокетов
«Рукопожатие» или handshake:

Считываем значение Sec-WebSocket-Key из пришедшего заголовка от клиента, рассчитываем на его основе Sec-WebSocket-Accept и отправляем итоговый ответ:

обмен сообщениями
Простой сервер вебсокетов

Итак, у нас есть вся необходимая информация.
Используя код простого http сервера из первой части, а также функции handshake, decode и encode из второй мы можем собрать простой сервер вебсокетов.

В приведённом примере можно менять пользовательские сценарии onOpen, onClose и onMessage для реализации необходимого функционала.

Поставленные цели достигнуты.
Если этот материал вам покажется интересным, то в следующей статье я опишу как можно запускать несколько процессов для обработки соединений (один мастер и несколько воркеров), межпроцессное взаимодействие, интеграцию с вашим фреймворком на примере компонента yii.

Writing a webserver in pure PHP — Tutorial

Solution Architect creating the black magic for Cineman, handyman for ClanCats, useless PHP things Entrepreneur and general Idiot.

Writing a webserver in pure PHP — Tutorial

authorMario Döring

Well, this is pretty useless, but it is possible. But again its pretty.. uesless. This tutorial will hopefully help you to better understand how a simple webserver could work and that it’s no problem writing one in PHP. But again using this in production would be trying to eat a soup with a fork. So just, . just don’t. Let me shortly explain why this is not a that good idea.

PHP is a scripting language that simply is not really designed for such tasks.

A webserver is a long running process which PHP is simply not made for. Also PHP does not natively support multi-threading ( pthreads ), which will make developing a good performing web-server a really hard/impossible task. PHPs memory allocations can be quite wasteful and even if the interpreter is incredibly fast it still has the overhead of an interpreter.
But these things might change in the future.
If you met some programmers, there was at least one who made jokes about how bad PHP is and there are obvious reasons why PHP became such an industry inside joke. But the language and its eco-system has evolved, . alot.
There are still people who can’t see behind the jokes, a lot of them either didn’t touch PHP for at least 10 years or copycat other peoples opinions to be a cool kid.
I would bet a few bucks that things like long running php applications with embedded web servers and other things will show up more and more.

Use the Source, Luke

Basics

Enough warnings about doing what I’m about to explain how to do. let’s get started eating the god damit soup with god damit chopsticks.

How does a webserver basically work?

  1. The server listens for incoming connections.
  2. A client connects to the server.
  3. The server accepts the connection and handles the input.
  4. The server responds to the client.

Structure

I’m going to build this as an abstraction of the Request and Response. There are many ways designing an application, As a first step I prefer writing the part of application that consumes my API. Followed by writing the actual API.

So later I wan’t to be able to use the thing like this:

The directory structure (just as help):

I’m going to use PSR-4 autoloading. So start by creating a new composer.json file. ( Don’t forget to run composer install afterwards )

Initializing

Next we create the script file ( server ) which will take care of starting the server. We don’t add the .php extension. So that bash knows what do do, add the following header:

We are always going to bind the server to localhost but we wan’t to be able to define the port as command line argument.

This allows as to start the server later like this:

If we combine that with what we defined before we get the following file:

Server Object

Next lets create our src/Server.php file which is going to handle the socket.

Our server class is going to hold the host, the port and the socket resource so add the following class variables.

Create a socket

To bind a socket we need to have one so create the createSocket function.

The first argument specifies the domain / protocol family of the socket. AF_INET is for IPv4 TCP and UDP protocols.

The second argument defines the communication type of the socket. SOCK_STREAM is a simple full-duplex connection based byte stream.

The third argument sets the protocol.

Bind the socket

This is pretty self explaining. The socket_bind function returns false when something goes wrong. Because this should never happen we throw an exception with the socket error message.

Create and bind the socket on construct

We could also create a connect function, but to keep stuff simple we just do it in the constructor.

Listen for connections

Beacuse I don’t want to split this function in 20 segments just to explain what happens, I added my bullshit to the comments.

Request Object

Now it’s time to create the src/Request.php file which is going to handle the user input.

Our Request is going to hold the HTTP request method , the uri , parameters and headers . So add these class variables:

Parse the header

Now in our listen function we already pass the socket input / request header to the withHeaderString function. A http header looks like this:

So what we need to do is parse that data. The first line indicates the request method, uri and protocol. Followed by key, value header parameters.

Our constructor recives $method , $uri and $headers . We could simply just assign them to a class variable. But for this example I want to split and parse the query parameters.

Create request getter methods

Because our class variables method, uri, parameters and headers are protected we need to create some getters to make the request data accessible.

There is nothing specail with the method and uri getters. They just return..

Now the header and param getter should allow giving a default value. Which get return if no data with the given key is found.

Response Object

Being muted isn’t much fun. Of course we wan’t to be able to respond to our request. As you see in the listen function, the given callback has to return a Response object. Otherwise a 404 response is returend.

How does a http response look like? Actually pretty much the same as the request. We have a header and a body. And we will simply write them both into the socket to respond to the client.

Again this is not the optimal way for a solid implementation its an example..

Create a new file src/Response.php .

Status codes

404 in tha house! To be able to build our header string we need to know the http status codes. We could also set them manually, but who the hell wants to write stuff manually?

This array pretty much covers the http status codes definitions. Taken from CCF.

Just a little thing aside there is a repository that covering the 7xx http status codes: https://github.com/joho/7XX-rfc They are hilarious 🙂

Response constructor

The general parameters our response object should implement are the http status, body and other headers.

Body should be a must argument in the constructor, while the status and other headers should be optional. Also the constructor should set some default values like the current Date or the Server header.

Setting headers

To be able to add new header parameters to the object we need to create a setter method.

ucfirst is loved by lazy folks like me. It uppercases ( if thats actually a word ) the first character of a string. This way you can create new responses like this:

Building the header string

We made eveything so fancy abstracted but we cannot simply pass our response object to the socket writer. We need to build a string out of our data.

A http header response string will look like the following:

We have all the data we need so we can create the following function:

Let the magic happen

And because again we are all lazy fucks that don’t want to execute buildHeaderString just to build a header string, we create the magic __toString method that returns the entire string written to the open connection.

Thats it!

Well thats everything, hopefully. You should now be able to start your server just like this:

And access it with your browser:

post-scriptum

When I started this article I did not thought it would be that that long and would consume so much of my time. So I have to admit that I got a bit annoyed after the first hour or so. Please excuse that the quality is not consistent or even close to good. But I hope the tutorial and especially the source might still help people who work primarily with PHP and are interested to better understand how a Webserver works.

Mario Döring

Mario Döring

Solution Architect creating the black magic for Cineman, handyman for ClanCats, useless PHP things Entrepreneur and general Idiot.

WebSocket сервер на PHP

Протокол WebSocket предназначен для решения разных задач и снятия ограничений обмена данными между браузером и сервером. Он позволяет пересылать любые данные, на любой домен, безопасно и почти без лишнего сетевого трафика. Для установления соединения WebSocket клиент и сервер используют протокол, похожий на HTTP. Клиент формирует особый HTTP-запрос, на который сервер отвечает определенным образом.

Простой сокет-сервер

В первую очередь надо в файле php.ini расскомментировать строку, позволяющую работать с сокетами и перезапустить сервер:

Вот как выглядит простейший сокет-сервер:

Запустим его в работу:

Попробуем пообщаться с сервером с помощью telnet :

Получив приглашение telnet , даем команду:

И видим сообщение от сервера:

Наш сервер в другом окне тоже встрепенулся:

WebSocket сервер

Протокол WebSocket работает над TCP. Это означает, что при соединении браузер отправляет по HTTP специальные заголовки, спрашивая: «Поддерживает ли сервер WebSocket?». Если сервер в ответных заголовках отвечает «Да, поддерживаю», то дальше HTTP прекращается и общение идёт на специальном протоколе WebSocket, который уже не имеет с HTTP ничего общего.

Здесь GET и Host — стандартные HTTP-заголовки, а Upgrade и Connection указывают, что браузер хочет перейти на WebSocket.

Сервер может проанализировать эти заголовки и решить, разрешает ли он WebSocket с данного домена Origin . Ответ сервера, если он понимает и разрешает WebSocket-подключение:

Для тестирования работы сервера нам нужен клиент:

Проверим его в работе. Открываем HTML-страницу в браузере и заполняем первое поле «Сервер»:

Это гарантированно работающий WebSocket echo-сервер, которые отправляет все сообщения обратно. Жмем кнопку «Установить соединение», набираем текст сообщения в поле «Сообщение», жмем кнопку «Отправить сообщение»:

А теперь код WebSocket сервера на PHP:

Для тестирования напишем небольшой PHP-скрипт, который запускает в работу сервер и все сообщения клиента отправляет обратно (echo-сервер):

Запускаем скрипт в работу:

Еще один пример использования сервера — клиент отправляет команды, а сервер их выполняет:

Альтернативная реализация WebSocket сервера с использованием функций для работы с потоками:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *