Flash / Передача данных посредством Peer-to-peer во Flash Player

Итак, что же вообще из себя представляет Peer-to-Peer во Flash? Обмениваться сообщениями экземплярам Flash Player позволяет RTMFP — Real-Time Media Flow Protocol. Суть его в том, что клиент лишь раз обращается к серверу за помощью в соединении с остальными клиентами, после чего, получив уникальный Peer ID, может начинать пересылать любые данные напрямую.

Сервер


Для организации пиринговой сети между Flash Player существует бесплатный сервис Adobe Cirrus. Подробнее о нем можно почитать на странице Adobe Labs.
Он предназначен, скорее, для демонстрации возможностей RTMFP, поэтому нам не удастся выполнять какой-либо свой код или что-то изменять на стороне сервера. Конечно же, ничто не мешает поднять собственный сервер, совместимый с протоколом RTMFP, но сейчас для наших целей подходит Cirrus.
Для доступа к возможностям сервиса нам необходимо получить ключ разработчика, который привязывается к аккаунту Adobe. Ключ понадобится при подключении.

План действий


Я считаю, что лучшим способом познать P2P является написание собственного класса для подключения к Cirrus.
Процесс работы с Cirrus, выглядит в нашем случае следующим образом:
1. Подключаемся к Cirrus, используя наш ключ.
2. Подключаемся к группе (NetGroup).
NetGroup — класс, который предоставляет информацию о группе, в которой находится клиент. Чтобы обмениваться сообщениями, нам понадобится объединить всех наших клиентов в одну группу.
3. Обрабатываем необходимые события NetStatusEvent.NET_STATUS.
NetStatusEvent.NET_STATUS — события, связанные с удачным/неудачным подключением, получением сообщений и действиями членов группы.
4. Предоставляем клиентам возможность обмениваться сообщениями.

Написание класса


1. Подключаемся к Cirrus

Для этого нам нужен класс flash.net.NetConnection, создающий соединение между клиентом и сервером.
Функцию connect(), инициализирующую подключение я реализовал так:
// Подключение
public function connect()
{
  netConnection = new NetConnection(); // переменная netConnection объявлена в теле класса
  // Событие NET_STATUS нужно для отслеживания состояния подключения к серверу
  netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
  // Пример: "rtmfp://p2p.rtmfp.net/abcdefghijklmnopqrstu-ads12asfv"
  netConnection.connect("rtmfp://p2p.rtmfp.net/" + "Ваш ключ разработчика");  
}

// Функция прослушивания событий NetStatusEvent
private function netStatusHandler( e:NetStatusEvent ):void
{
  switch ( e.info.code )
  {
    case "NetConnection.Connect.Success":
      trace ("Успешное подключение к серверу!")
      // Подключились к Cirrus'у - переходим к настройке нашей группы
      setupNetGroup();
      break;
    case "NetGroup.Posting.Notify":
      // Событие получения сообщения
      messageReceived(e.info.message);
      break;
  }
}

// Настройка группы
private function setupNetGroup ():void
{

}

// Обработка входящих сообщений
private function messageReceived (message:Object):void
{

}

* This source code was highlighted with Source Code Highlighter.


Так же я заранее объявил еще три функции:
netStatusHandler — прослушивание событий от NetConnection,
setupNetGroup — вызывается при успешном подключении к серверу для настройки группы,
messageReceived — вызывается при получении сообщения от другого клиента.

2. Настраиваем класс NetGroup

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

Класс GroupSpecifier позволяет задать параметры группы для ее создания. Так как и netConnection и netGroup для оповещения о своем состоянии используют события NetStatusEvent, то для прослушивания событий будет использоваться та же функция, что и для netConnection netStatusHandler:
// Настройка группы
private function setupNetGroup ():void
{
  var groupSpecifier:GroupSpecifier = new GroupSpecifier("НазваниеГруппы");
  groupSpecifier.serverChannelEnabled = true; // Для обмена с сервером
  groupSpecifier.postingEnabled = true; // Разрешаем отправку сообщений в группе

  // переменная netGroup объявлена в теле класса
  netGroup = new NetGroup(netConnection, groupSpecifier.groupspecWithAuthorizations() );
  netGroup.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler); // Прослушка событий группы
}


* This source code was highlighted with Source Code Highlighter.


3. Обрабатываем NetStatusEvent

Теперь нам необходимо разобраться, для чего нужны события NetStatusEvent. Рассмотрим функцию netStatusHandler:
// Функция прослушивания событий NetStatusEvent
private function netStatusHandler( e:NetStatusEvent ):void
{
  trace(e.info.code);
  switch ( e.info.code )
  {
    case "NetConnection.Connect.Success":
      trace ("Успешное подключение к серверу!")
      // Подключились к Cirrus'у - переходим к настройке нашей группы
      setupNetGroup();
      break;
    case "NetGroup.Posting.Notify":
      // Событие получения сообщения
      messageReceived(e.info.message);
      break;
  }
}


* This source code was highlighted with Source Code Highlighter.


Событие NetStatusEvent.NET_STATUS возвращает в e.info.code строку, представляющую событие NET_STATUS. NetStatusEvent.NET_STATUS вызывается при любых изменениях в группе.
Нам понадобились только два события NET_STATUS (на самом деле их намного больше, можно посмотреть здесь):
«NetConnection.Connect.Success» — успешное подключение netConnection к серверу Cirrus.
«NetGroup.Posting.Notify» — получение входящих сообщений, обработку которых мы рассмотрим далее.

4. Обмен сообщениями.

При получении нового сообщения вызывается функция messageRecieved, в которую передается объект сообщения, полученный из e.info.message. Так как мы пишем абстрактный класс, то при получении сообщения просто вызовем внешнюю функцию, в которую и передадим его, чтобы дальше можно было легко его обработать.

// Обработка входящих сообщений
public var callback_messageReceived:Function; // callback
private function messageReceived (message:Object):void
{
  if (callback_messageReceived)
    callback_messageReceived ( message );
}


* This source code was highlighted with Source Code Highlighter.


Осталось написать функцию отправки сообщения. Всё, что она должна делать, — вызывать netGroup.post():

public function sendMessage( message:Object ):void
{
  if (!message)
    return;
  netGroup.post(message); // Отправляем сообщение в группу
}


* This source code was highlighted with Source Code Highlighter.


Заключение


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

Я считаю, что эта технология обязательно найдет своё применение. Скорее всего — в играх. Ведь серверу не придется контролировать игровые сессии и синхронизировать игроков, да и пинг при прямом подключении ниже (зависит от расстояния между игроками).

Спасибо за прочтение моей статьи! Надеюсь, вы найдете что-то полезное для себя.

Источник полезной информации по теме P2P во Flash: FlashRealtime.com.

Класс, который получился в результате

Поделитесь интересным с друзьями: