<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Melnaron &#187; Flash</title>
	<atom:link href="http://melnaron.net/tag/flash/feed" rel="self" type="application/rss+xml" />
	<link>http://melnaron.net</link>
	<description>Серьёзная веб-разработка</description>
	<lastBuildDate>Tue, 31 Jan 2012 09:01:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Peer-to-Peer Chat in Flash</title>
		<link>http://melnaron.net/development/peer-to-peer-chat-in-flash</link>
		<comments>http://melnaron.net/development/peer-to-peer-chat-in-flash#comments</comments>
		<pubDate>Sun, 30 May 2010 18:40:45 +0000</pubDate>
		<dc:creator>Melnaron</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[P2P]]></category>
		<category><![CDATA[Stratus]]></category>

		<guid isPermaLink="false">http://melnaron.net/?p=261</guid>
		<description><![CDATA[peer-to-peer p2p chat in flash with adobe stratus service]]></description>
			<content:encoded><![CDATA[<p>Этой статьей я хочу открыть новую тему в своем блоге, посвященную разработке на Flash/Flex/ActionScript3. Итак приступим.</p>
<h3>Flash Player 10.0</h3>
<p>Начиная с версии 10.0 во Flash Player появилась очень интересная возможность, которая позволяет устанавливать прямые соединения между плеерами при помощи сервиса <a href="http://labs.adobe.com/technologies/stratus/" target="_blank">Stratus</a>:</p>
<div class="codecolorer-container actionscript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="actionscript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">nc = <span style="color: #000000; font-weight: bold;">new</span> <span style="color: #0066CC;">NetConnection</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;<br />
nc.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>NetStatusEvent.<span style="color: #006600;">NET_STATUS</span>, netStatusHandler<span style="color: #66cc66;">&#41;</span>;<br />
nc.<span style="color: #0066CC;">connect</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'rtmfp://stratus.adobe.com/YOUR_DEVELOPER_KEY_HERE'</span><span style="color: #66cc66;">&#41;</span>;</div></td></tr></tbody></table></div>
<p>Этот сервис выполняет роль организатора: подключившись к нему ваш клиент получает уникальный Peer ID, зная который, другие такие же как и вы клиенты, могут напрямую подключаться к вашему клиенту и подписываться на данные, которые вы публикуете. </p>
<p>Давайте рассмотрим эту схему более подробно, по шагам:</p>
<ul>
<li>Flash-клиент в вашем браузере соединяется с сервисом <a href="http://labs.adobe.com/technologies/stratus/" target="_blank">Stratus</a></li>
<li>В клиенте создается поток (NetStream), в который начинается публикация какого-то контента</li>
<li>Другие flash-клиенты также соединяются с сервисом <a href="http://labs.adobe.com/technologies/stratus/" target="_blank">Stratus</a></li>
<li>В них создаются потоки, которые подключаются к вашему клиенту по его Peer ID и подписываются на контент от него</li>
</ul>
<p>Это самая простая схема: один клиент публикует контент, а другие подписываются на получение контента.</p>
<p>Но давайте попробуем усложнить эту схему. Что если каждый клиент и публикует и подписывается на потоки всех известных ему клиентов?</p>
<p><span id="more-261"></span></p>
<h3>Чат</h3>
<p>Сейчас я много занимаюсь разработкой приложений для коммуникации. Это и аудио/видео чаты, и обычные текстовые чаты (кто-нибудь помнит мой Mel.Chat?). И в связи с этим я хочу поделиться небольшим примером разработки простого текстового чата, основанного на технологии Peer-to-Peer или P2P. В отличии от старичка Mel.Chat&#8217;а, который основан на постоянной долбежке сервера AJAX-запросами, в этом чате все очень похоже на технологию клиент/сервер. Создаются необходимые соединения, которые просто остаются открыты столько сколько нужно и просто ожидают данные, которые отправляют (публикуют) другие клиенты. Но если с технологией клиент/сервер все более или менее понятно (все клиенты соединяются с одним определенным сервером, и все коммуникации осуществляются путем отправки сообщений на сервер, который в свою очередь отправляет сообщения всем подключенным к нему клиентам), то с P2P у нас так не получится, потому что тут у нас нет какого-то определенного сервера, а каждый отдельный клиент должен быть подписан на всех других участвующих в чате клиентов.</p>
<p>Схема ясна, переходим к практике.</p>
<h3>Сервис регистрации Peer ID</h3>
<p>Во-первых необходимо организовать сервис обмена Peer ID (далее фингерпринт) между клиентами. Это нужно для того, чтобы каждый новый подключившийся клиент мог самостоятельно найти хотябы одного активного в данный момент клиента и установить с ним соединение. Ниже представлен простейший код такого сервиса на PHP &#038; MySQL:</p>
<div class="codecolorer-container php default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<br />
<span style="color: #b1b100;">require</span> <span style="color: #0000ff;">'lib/db.php'</span><span style="color: #339933;">;</span><br />
<br />
Db<span style="color: #339933;">::</span><span style="color: #004000;">connect</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'localhost'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'user'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'password'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'database'</span><span style="color: #009900;">&#41;</span> or <span style="color: #990000;">die</span><span style="color: #009900;">&#40;</span>Db<span style="color: #339933;">::</span><span style="color: #004000;">error</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'DB_TABLE'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'p2pchat'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #000088;">$time</span> <span style="color: #339933;">=</span> <span style="color: #990000;">time</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #000088;">$cooldown</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">900</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// 15 min</span><br />
<span style="color: #000088;">$limit</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">5</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #666666; font-style: italic;">// удаляем все истекшие записи</span><br />
Db<span style="color: #339933;">::</span><span style="color: #004000;">delete</span><span style="color: #009900;">&#40;</span>DB_TABLE<span style="color: #339933;">,</span> <span style="color: #0000ff;">'timestamp &lt; '</span><span style="color: #339933;">.</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$time</span> <span style="color: #339933;">-</span> <span style="color: #000088;">$cooldown</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'insert'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span> <span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'insert'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// выбираем последние обновленные записи для попытки соединения с ними</span><br />
&nbsp; &nbsp; <span style="color: #000088;">$fingerprints</span> <span style="color: #339933;">=</span> Db<span style="color: #339933;">::</span><span style="color: #004000;">select</span><span style="color: #009900;">&#40;</span>DB_TABLE<span style="color: #339933;">,</span> <span style="color: #0000ff;">'fingerprint'</span><span style="color: #339933;">,</span> <span style="color: #009900; font-weight: bold;">NULL</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'timestamp DESC'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$limit</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// записываем в базу свой фингерпринт</span><br />
&nbsp; &nbsp; Db<span style="color: #339933;">::</span><span style="color: #004000;">insert</span><span style="color: #009900;">&#40;</span>DB_TABLE<span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'fingerprint'</span> <span style="color: #339933;">=&gt;</span> Db<span style="color: #339933;">::</span><span style="color: #004000;">escape</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'insert'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'timestamp'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$time</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// отправляем в ответ выбранные записи</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fingerprints</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> <span style="color: #cc66cc;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000088;">$response</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">foreach</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$fingerprints</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$fp</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000088;">$response</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$fp</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'fingerprint'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #990000;">implode</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">','</span><span style="color: #339933;">,</span> <span style="color: #000088;">$response</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'update'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #339933;">!</span> <span style="color: #990000;">empty</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'update'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #666666; font-style: italic;">// обновляем свой фингерпринт</span><br />
&nbsp; &nbsp; Db<span style="color: #339933;">::</span><span style="color: #004000;">update</span><span style="color: #009900;">&#40;</span>DB_TABLE<span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'timestamp'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$time</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'fingerprint = &quot;'</span><span style="color: #339933;">.</span>Db<span style="color: #339933;">::</span><span style="color: #004000;">escape</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'update'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'&quot;'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>Давайте я немного расскажу о принципе работы этого сервиса (или трекера). После того как ваш flash-клиент установил соединение с сервисом <a href="http://labs.adobe.com/technologies/stratus/">Stratus</a> он отправляет GET-запрос с вашим Peer ID в качестве параметра сервису регистрации фингерпринтов. Ваш фингерпринт сохраняется в базе с текущим временем. Также сервис выбирает из базы несколько последних добавленных/обновленных фингерпринтов и отправляет их в ответ вашему клиенту. Клиент пытается установить соединение с каждым из полученных фингерпринтов:</p>
<div class="codecolorer-container actionscript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br /></div></td><td><div class="actionscript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">var</span> loader:URLLoader = <span style="color: #000000; font-weight: bold;">new</span> URLLoader<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;<br />
<br />
loader.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>Event.<span style="color: #006600;">COMPLETE</span>, <span style="color: #000000; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span><span style="color: #0066CC;">e</span>:Event<span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0066CC;">log</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'Insert fingerprint complete'</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">var</span> <span style="color: #0066CC;">fps</span>:<span style="color: #0066CC;">Array</span> = <span style="color: #0066CC;">e</span>.<span style="color: #0066CC;">target</span>.<span style="color: #0066CC;">data</span>.<span style="color: #0066CC;">split</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">','</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">// перебираем все полученные фингерпринты,</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">// и пытаемся с ними соединиться</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">for</span> <span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">var</span> i:<span style="color: #0066CC;">int</span> = <span style="color: #cc66cc;">0</span>; i <span style="color: #66cc66;">&lt;</span> <span style="color: #0066CC;">fps</span>.<span style="color: #0066CC;">length</span>; i++<span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0066CC;">fps</span><span style="color: #66cc66;">&#91;</span>i<span style="color: #66cc66;">&#93;</span>.<span style="color: #0066CC;">length</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; initRecvStream<span style="color: #66cc66;">&#40;</span><span style="color: #0066CC;">fps</span><span style="color: #66cc66;">&#91;</span>i<span style="color: #66cc66;">&#93;</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><br />
<span style="color: #66cc66;">&#125;</span><span style="color: #66cc66;">&#41;</span>;<br />
<br />
<span style="color: #0066CC;">try</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; loader.<span style="color: #0066CC;">load</span><span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> URLRequest<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'http://url/to/p2pchatreg.php?insert='</span>+myPeerID<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;<br />
<span style="color: #66cc66;">&#125;</span> <span style="color: #0066CC;">catch</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0066CC;">error</span>:<span style="color: #0066CC;">Error</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #0066CC;">log</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'Unable to insert my peer id'</span><span style="color: #66cc66;">&#41;</span>;<br />
<span style="color: #66cc66;">&#125;</span></div></td></tr></tbody></table></div>
<p>И тут начинается самое интересное&#8230;</p>
<h3>Оповещение о новом участнике</h3>
<p>Так как запрос регистрации возвращает ограниченное количество последних обновленных фингерпринтов, наш клиент не сможет подключиться ко всем участвующим в чате клиентам, но этого и не потребуется, потому что клиенты сами &#8220;рассказывают&#8221; друг другу о подключении новых участников к чату, а также оповещают подключающихся к ним клиентах о своих именах:</p>
<div class="codecolorer-container actionscript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br /></div></td><td><div class="actionscript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">client.<span style="color: #006600;">onPeerConnect</span> = <span style="color: #000000; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span>subscriber:<span style="color: #0066CC;">NetStream</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">Boolean</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">// когда ко мне присоединяется подписчик, проверяем,</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">// если это новый подписчик, то оповещаем всех о нем,</span><br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">// а также подписываемся на его поток</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span> <span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">!</span> hasSubscriptionTo<span style="color: #66cc66;">&#40;</span>subscriber.<span style="color: #006600;">farID</span><span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; sendStream.<span style="color: #0066CC;">send</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'onBroadcastNewSubscriber'</span>, subscriber.<span style="color: #006600;">farID</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; initRecvStream<span style="color: #66cc66;">&#40;</span>subscriber.<span style="color: #006600;">farID</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #808080; font-style: italic;">// ... и конечно же оповещаем его о своем имени</span><br />
&nbsp; &nbsp; subscriber.<span style="color: #0066CC;">send</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'onPeerNameUpdate'</span>, myPeerID, myName<span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #b1b100;">return</span> <span style="color: #000000; font-weight: bold;">true</span>;<br />
<span style="color: #66cc66;">&#125;</span>;</div></td></tr></tbody></table></div>
<p>Итак, подключившись хотя бы к одному активному участнику чата или пиру (от англ. peer), тот немедленно проверяет свой список подключенных к нему пиров, и если вашего клиента с его уникальным peer id там еще нет, то он оповещает всех о новом подключенном к конференции клиенте, и все уже связанные друг с другом пиры сами устанавливают соединение с вами. Таким образом образуется самоформирующаяся группа участников конференции (во Flash Player 10.1 всю эту механику берет на себя новый класс NetGroup + GroupSpecifier).</p>
<h3>Актуальность фингерпринтов</h3>
<p>Актуальность активных в данный момент фингерпринтов &#8211; это ключевой момент всего приложения. Чтобы поддерживать актуальность своего фингерпринта каждый активный клиент через определенный промежуток времени отправляет GET-запрос на сервис регистрации фингерпринтов с просьбой обновить время активности своего фингерпринта:</p>
<div class="codecolorer-container actionscript default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="actionscript codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #808080; font-style: italic;">// запускаем таймер который будет тикать каждые 5 минут (300 секунд)</span><br />
<span style="color: #000000; font-weight: bold;">var</span> timer:Timer = <span style="color: #000000; font-weight: bold;">new</span> Timer<span style="color: #66cc66;">&#40;</span><span style="color: #cc66cc;">300</span> <span style="color: #66cc66;">*</span> <span style="color: #cc66cc;">1000</span><span style="color: #66cc66;">&#41;</span>;<br />
<br />
timer.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>TimerEvent.<span style="color: #006600;">TIMER</span>, <span style="color: #000000; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #000000; font-weight: bold;">var</span> loader:URLLoader = <span style="color: #000000; font-weight: bold;">new</span> URLLoader<span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;<br />
<br />
&nbsp; &nbsp; loader.<span style="color: #006600;">addEventListener</span><span style="color: #66cc66;">&#40;</span>Event.<span style="color: #006600;">COMPLETE</span>, <span style="color: #000000; font-weight: bold;">function</span><span style="color: #66cc66;">&#40;</span><span style="color: #0066CC;">e</span>:Event<span style="color: #66cc66;">&#41;</span>:<span style="color: #0066CC;">void</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0066CC;">log</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'Update fingerprint complete'</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><span style="color: #66cc66;">&#41;</span>;<br />
<br />
&nbsp; &nbsp; <span style="color: #0066CC;">try</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; loader.<span style="color: #0066CC;">load</span><span style="color: #66cc66;">&#40;</span><span style="color: #000000; font-weight: bold;">new</span> URLRequest<span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'http://url/to/p2pchatreg.php?update='</span>+myPeerID<span style="color: #66cc66;">&#41;</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span> <span style="color: #0066CC;">catch</span> <span style="color: #66cc66;">&#40;</span><span style="color: #0066CC;">error</span>:<span style="color: #0066CC;">Error</span><span style="color: #66cc66;">&#41;</span> <span style="color: #66cc66;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #0066CC;">log</span><span style="color: #66cc66;">&#40;</span><span style="color: #ff0000;">'Unable to update my peer id'</span><span style="color: #66cc66;">&#41;</span>;<br />
&nbsp; &nbsp; <span style="color: #66cc66;">&#125;</span><br />
<span style="color: #66cc66;">&#125;</span><span style="color: #66cc66;">&#41;</span>;<br />
<br />
timer.<span style="color: #0066CC;">start</span><span style="color: #66cc66;">&#40;</span><span style="color: #66cc66;">&#41;</span>;</div></td></tr></tbody></table></div>
<p>Таким образом новые участники конференции всегда получают пачку самых последних активных пиров на данный момент. И подключившись хотя бы к одному активному пиру, тот в свою очередь, оповещает всех о новом участнике.</p>
<h3>Заключение</h3>
<p>В итоге, у нас получается достаточно простой чат, который не использует сервер для передачи сообщений, и поддерживает большое количество клиентов онлайн. Мгновенно отправляет и получает сообщения. К такому чату можно добавить аудио/видео звонки и передачу файлов (аля Skype). Основными требованиями для работы такого чата являются наличие установленного Flash Player 10.0+ и разрешенный UDP-траффик.</p>
<p>В следующей статье я расскажу об использовании более удобных средств для организации групп при помощи классов NetGroup и GroupSpecifier из FlashPlayer 10.1.</p>
<p>Если вы хотите быть в курсе моих новых публикаций о flash и peer-to-peer, то не стесняйтесь и подписывайтесь на <a href="http://melnaron.net/feed" target="_blank">RSS-ленту моего блога</a> или <a href="http://twitter.com/Melnaron" target="_blank">следите за мной в Twitter</a>.</p>
<p><a href="http://melnaron.net/flash/p2pchat/" target="_blank"><strong>Живой пример P2PChat</strong></a> &#8211; Чтобы опробовать чат откройте его в 2х или более окнах браузера, расположите их рядом друг с другом и попробуйте что-нибудь написать.</p>
<p><a href="http://melnaron.net/files/P2PChat.zip"><strong>Скачать исходные коды P2PChat</strong></a> &#8211; Для Flash Builder 4 + Flex SDK 4. Для работы со Стратусом вам необходимо получить &#8220;stratus developer key&#8221; зайдя на страницу <a href="http://labs.adobe.com/technologies/stratus/">Stratus</a> и перейдя по ссылке &#8220;Signup for a Stratus beta developer key&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://melnaron.net/development/peer-to-peer-chat-in-flash/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

