Сокеты во Flash
Я начал активно писать сетевые приложения более 10 лет назад (еще до появления в 2002-м году .NET Framework). Тогда я делал ActiveX-компоненты на VB6 и иногда даже запускал их в SQL2000. 10 лет назад у меня уже в SQL2000 был свой собственный полноценный Nitification Server (который в отличии от микрософтовского прекрасно работал). Позднее я сделал на базе этих наработок полноценный товарный продукт - Notification Server, который работал в компании digitalshop.ru. Кое какие фрагменты этой моей старинной проги нашли отражение у меня на сайте. А в 2006-м году я написал (на шестом бейсике) для этой же компании еще один сетевой продукт - весьма специфический крученый клон ICQ.
Я также увлекся терминальными сетями - которые представляют собой сплошное взаимодействие по сети Remote SQL execute for PostgreSQL on GSM/GPRS channel with extreme compress and cryptography. Я также продолжил писать разнообразнейшие SQL CLR сборки, которые работали с сетью, например SQL-Client_for_remote_XML-WebService - клиент meteonova.ru.
Я создал для своего хостинга криптографически защищенную систему защиты от копирования платных программ WebActivator - клиент/сервер защиты от копирования для платных программ, которая передает на сервер защиты от копирования индивидуальные сведения о компьютере на котором запускается программа, и если оборудование компьютера изменилось значительно - то работа платной программы блокируется.
Я писал всевозможные сетевые пауки и парсеры в несметных количествах, всевозможные импортеры прайсов с автоматической скачкой, автоопределением форматов и укладкой прайса в базу и в конце концов написал даже собственную поисковую машину (некий клон Гугла или Яндекса), который однако работал по иным алгоритмам - WebDownloader_UltraLite - ваш личный поисковик по рунету с особыми возможностями поиска.
Я написал и оформил как OpenSource SNMP-тестер сетевых устройств. GUI-программа на Windows.Forms для Linux, Xping - утилита контроля качества связи. Вот еще моя заметка о сетевом программировании от 2005-го года.
Как видите, библиотеки сетевого обмена - это ключевой компонент любой современной системы. Просто десктопные приложения, которые что-то там сами в себе ковыряются и никому ничего в сеть не сообщают - это нонсенс. Таких приложений можно придумать крайне мало. Практически все приложения, которые вы видите у меня на сайте - так или иначе работают в TCP/IP сети.
Даже Web-cервера, на которых хостятся сайты - как правило, тоже помимо движка, который выплевывает в сеть по запросам браузеров html-странички, содержат множество разнообразных web-сервисов. Например сервисы, к которым можно приконнектится и получить AJAX подсказка/автозаполнение на jQuery.
Я не только показывал как работать с такими web-сервисами из JavaScript - например Мой первый сайт на MVC 3 Razor или из ASP.NET Этюды на ASP2. Обращение к Whois-сервису - но я даже написал свой собственный, альтернативный OpenSource клиент к такому стандартному микрософтофтовскому WCF-серверу - WCF_CLIENT - клиент Web-сервиса (первая версия).
Как видите, .NET Framework - это одно сплошное сетевое взаимодействие.
Поэтому, первое чем я занялся при освоении Flex - это сетевым взаимодействием FLEX-AIR приложений с внешним миром. Я отработал для себя (и показал всем остальным) :
- как можно делать Web-сервисы, с которыми удобно работать из FLEX-AIR приложений - Как сделать SOAP/WSDL-вебсервис на ASP.NET/MONO для вызова его из FLEX.
- как сделать простейшие серверные хандлеры, формирующие XML или JSON для Flex-Air приложений - Как сделать простейший Web-handler - формирующий XML или JSON.
- как обрабатывать JSON, формируемый указанным хандлером - OpenSource TextBannerRotator - простой ротатор текстовых баннеров с эффектом BLUR
- как сделать FLEX-клиента к SOAP/WSDL-вебсервису - OpenSource Freeware FotoSlider on Flex 3 and jQuery.
- наконец, я показал третий вариант доступа из FLEX-AIR к данным сервера - вообще без какого-либо серверного хандлера - Freeware OpenSource панорамный фотослайдер.
Как видите, Adobe Framework - это тоже сплошные сетевые взаимодействия. Так же как и на .NET Framework. Хотя в Adobe Framework доминирует, возможно, все-таки мультимедиа, анимация и графика. Но графика довольно активно присутствует и в .NET - Наложение копирайта на рисунки, но в меньших масштабах и всегда только на сервере.Так в чем же разница между этими виртуальными машинами?
Хотя Adobe Framework проигрывает возможностям NET Framework по многим позициям - например он не дает возможностей многопоточного программирования, на нем невозможно даже создать полноценный WSDL/SOAP вебсервис, сам язык ActionScript на порядок более ограниченный по выразительности чем бейсик. И возможности объектного программирования у него по сравнению с современным бейсиком - (Практическое применение наследования, полиморфизма, интерфейсов, дженериков и делегатов на примерах в Visual Basic .NET) - в самом зародыше. Напоминают обьектные возможности шестого бейсика. Но... этот фреймворк чрезвычайно легкий. Легкий до такой степени, что даже может работать в браузере. И плюс у него есть нечто такое, чего вовсе нет в .NET Framework - анимация, звук, видео.
Если бы у меня спросили чекисты, чем отличаются эти фреймворки - то я бы обьяснил им так на понятном для них языке: Adobe Framework - это ампулка с полонием или таблетка диоксина. А бейсик - это уже грузовик с гексогеном. И для каждой задачи - свой инструмент. Одно дело - отравить Ющенко или Литвиненко, а другое дело - взорвать жилой дом на окраине Москвы или Рязани. Одна задача - просто одноразово убить в тюрьме Магницкого за 6 миллионов долларов взятки, другое дело - добиться чтобы вообще правосудия не было во всей стране в принципе и не для кого, а все решалось бы исключительно по волеизъявлению чекистов и только в их интересах. Одно дело - произвести ковровые бомбометания по всей Чечне, другое дело - подорвать в Катаре Яндарбиева и его сына на выходе из мечети. Одно дело - добиться чтобы собственность чекистов на Абхазию признали папуасы из Науру, Умумбы и Вануату, другое дело чтобы это же сделали США и Евросоюз. Одно дело - красиво отглумиться только над одной партией Яблоко - чтобы даже на участке где лично живет Митрохин - не оказалось при подсчете конторой Чурова ни единого бюллетеня за Яблоко, другое дело дело - красиво отглумиться над всей 140-миллионной страной, раскинувшейся от Балтики до Тихого океана и назначить ее презиком слабоумного и с детства недоразвитого блоггера. Одно дело - сгноить в ГУЛАГе десятки миллионов несогласных, другое дело - тихонько и точно в толпе уколоть отравленным зонтиком несогласного Маркова. Одно дело - полномасштабная война с Грузией, другое дело - просто случайно погиб Качинский со всем своим генеральным штабом. Эти ведь разные задачи - и решаются они различным инструментарием. И даже самые мелкие задачи - типа Магницкого, Троцкого, Качинского, Ющенко, Литвиненко, Яндарбиева, Маркова - тоже могут быть решены исключительно эффективно, красиво и показательно. Примерно такое же различие существует и у Adobe Fremework и NET Framework - ActionScript и Adobe Framework это исключительно точный, мощный и изящный инструмент - такой же как ампулка с полонием, карманный постановщик помех высотомеру самолета, таблеточка с диоксином, укол зонтиком, удар топором-ледорубом точно в макушку.
Итак, в Adobe Framework предусмотрено несколько классов для сетевого взаимодействия:
- flash.net.NetConnection - для коннектов с Flash Media Server, Adobe Stratus service, виртуальной машиной Flash Player или Web-сервером по протоколам rtmp, rtmpe, rtmpt, rtmpte, rtmps, rtmfp. Примеры и описание этих взаимодействий я сделал в заметке Видео-камеры, видео-чаты и Flash-медиасервера (работающие по RTMP и самописным протоколам).
- flash.net.LocalConnection - используется для связи Flex-AIR приложений между собой в пределах одного кампутера. Используют для связи со старыми приложениями на ActionScript 1,2 и для преодоления заморочек с безопасностью. Например один SWF имеет право работать только с файловой системой капутера, второй только с сетью. Между собой их взаимодействие может быть налажено именно с помощью этого класса.
- mx.controls.SWFLoader - этим методом динамически загружают компоненты SWF чтобы уменьшить вес первоначально загружемого компонента SWF. Также этим методом загружают SVG, GIF, JPEG, PNG - файлы.
- spark.modules.ModuleLoader - тоже используется для динамической загрузки SWF, но может использоваться для создания нескольких экземпляров загруженной библиотеки.
- mx.rpc.soap.mxml.WebService - с помощью этого класса загружают результат работы веб-сервиса. Пример на страничке OpenSource Freeware FotoSlider on Flex 3 and jQuery.
- flash.display.Loader - используется для загрузки отображаемых объектов (SWF, PG, PNG, GIF). Пример применения смотрите в топике - Freeware OpenSource панорамный фотослайдер.
- flash.net.URLLoader - этим методом загружают в приложения данные. Пример в том же топике - там таким способом грузится XML-файл.
- flash.net.Socket - самое низкоуровневое средство работы по сети. По возможностям не отличается от возможностей сокетной связи в бейсике. Позволяет надстроить над этим классом что угодно, любой собственный протокол. Именно этот последний класс будет и рассмотрен на данной страничке.
Протокол, используемый нижеследующей библиотечкой весьма прост - перед осмысленными данными добавляется три параметра, номер клиента, номер пакета и длина пакета. После приема данных сервером эти же три протокольные поля просто возвращаются назад инициатору передачи данных.
Сокетное соединение после передачи не закрывается и передачи по нему могут быть возобновлены в любой момент. Как со следующим номером пакета, так и постоянно ожидающему даннные на сокете серверу их может в любой момент передать и другой клиент.
Для чего полезна такая библиотека? В отличие от бейсика (который весьма проблематично выполнить в браузере - не считая чудо-технологии Silverlight) - флешевые компоненты настолько легкие, что без проблем считаются прямо на клиенте, прямо в браузере. Поэтому обмениться данными они могут непосредственно между собою, минуя сервер. То есть, грубо говоря, нижеследующий код является основой скайпа и любой флешовой сетевой игры.
Понятно, что в реальности столь простой протокол может быть применен лишь для простейших незащищенных приложений. Конечно для ваших реальных приложений сам протокол придется намного усложнить. Такая святая простота (добавили три поля перед данными и их же вернули как подтверждение приема) - хуже воровства. Если вы будете строить свои реальные игры браузер-браузер на столь простом протоколе, то любой флешер в два счета напишет эмулятор вашей игры и никто из ваших игроков уже не будет тыкать мышкой в браузере, а просто будет управлять эмулятором игрового поведения. Однако для простейших приложений типа аськи, где взлом протокола не имеет значения - такая библиотека прекрасно подойдет.
Вот как выглядит сетевой обмен с помощью этой библиотеки:
Итак, вот собственно тот самый код работы с сокетами по простейшему собственному протоколу. Именно с этой точки данный образец моего ActionScript кода вы можете начинать гнуть в любую сторону.
1: //минимальный вариант вызова сокетного сервера - слушает порт LocalTextPort.text
2: //
3: //import ru.net.asp.socket.*;
4: //private var LocalTextListener:Network_Listener = new Network_Listener( LocalTextPort.text );
5: //LocalTextListener.addEventListener(Network_Listener.DATA_RECEIVED, DATA_RECEIVED_handler);
6: //LocalTextListener.addEventListener(Network_Listener.DATA_ERROR, Error_handler);
7: //LocalTextListener.addEventListener(Network_Listener.CONNECT, Error_handler);
8: //LocalTextListener.addEventListener(Network_Listener.OPEN_ERROR, Error_handler);
9: //LocalTextListener.addEventListener(Network_Listener.CLOSE_ERROR, Error_handler);
10: //LocalTextListener.addEventListener(Network_Listener.PORT_OPEN, Error_handler);
11: //LocalTextListener.addEventListener(Network_Listener.PORT_CLOSE, Error_handler);
12: //LocalTextListener.addEventListener(Network_Listener.PROTOCOL_ERROR, Error_handler);
13: //LocalTextListener.addEventListener(Network_Listener.STRING_ERROR, Error_handler);
14: //LocalTextListener.Network_Open();
15: //
16: //обработчик ошибок
17: //protected function Error_handler(e:Event):void{
18: // ErrorMessage.text += e.type + " on port " + e.currentTarget.PortNumber +"\n";
19: //}
20: //
21: //прием строки
22: //protected function DATA_RECEIVED_handler(e:Event):void{
23: // Message.text += "<=" + e.currentTarget.ReadString() + "\n";
24: //}
25: //
26: //прием потока байтов
27: //protected function DATA_RECEIVED_handler(e:Event):void{
28: // var Arr1:ByteArray=e.currentTarget.ReceivedBytes;
29: //}
30: //
31: //при приеме кроме данных приходит еще два дополнительных параметра - номер пакета и номер клиента
32: // e.currentTarget.ClientNumber = 1001;
33: // e.currentTarget.PacketNumber += 1;
34: package ru.net.asp.socket
35: {
36: import flash.events.*;
37: import flash.events.IEventDispatcher;
38: import flash.net.ServerSocket;
39: import flash.net.Socket;
40: import flash.utils.ByteArray;
41:
42: public class Network_Listener extends EventDispatcher
43: {
44: public var ReceivedBytes:ByteArray; //принятые сервером данные
45: public var ClientNumber:int; //номер, который передал клиент
46: public var PacketNumber:int; //порядковый номер пакета
47:
48: //события сокетного сервера
49: public static const DATA_RECEIVED:String="DATA_RECEIVED";
50: public static const DATA_ERROR:String="DATA_ERROR";
51: public static const CLOSE_ERROR:String="CLOSE_ERROR";
52: public static const OPEN_ERROR:String="OPEN_ERROR";
53: public static const CONNECT:String="CONNECT";
54: public static const PORT_OPEN:String="PORT_OPEN";
55: public static const PORT_CLOSE:String="PORT_CLOSE";
56: public static const PROTOCOL_ERROR:String="PROTOCOL_ERROR";
57: public static const STRING_ERROR:String="STRING_ERROR";
58:
59: public function get PortNumber():int{
60: return portNumber;
61: }
62: private var portNumber:int;
63:
64: public function get Server_Socket():ServerSocket{
65: return ServerSocket1;
66: }
67: private var ServerSocket1:ServerSocket;
68:
69: public function Network_Listener(LocalPortNumber:String)
70: {
71: super();
72: portNumber=Number(LocalPortNumber);
73: ServerSocket1 = new ServerSocket();
74: }
75:
76: public function Network_Open():void
77: {
78: try
79: {
80: ServerSocket1.addEventListener(Event.CONNECT, socketConnectHandler);
81: ServerSocket1.addEventListener(Event.CLOSE,socketCloseHandler);
82: ServerSocket1.bind(portNumber);
83: ServerSocket1.listen();
84: dispatchEvent(new Event(Network_Listener.PORT_OPEN));
85: }
86: catch (error:Error)
87: {
88: dispatchEvent(new Event(Network_Listener.OPEN_ERROR));
89: }
90: }
91:
92:
93: private function socketConnectHandler(event:ServerSocketConnectEvent):void
94: {
95: var socket:Socket = event.socket;
96: dispatchEvent(new Event(Network_Listener.CONNECT));
97: socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
98: }
99:
100: //после приема байтовых данных - они в массиве ReceivedBytes
101: private function socketDataHandler(event:ProgressEvent):void
102: {
103: try
104: {
105: var Socket1:Socket = event.target as Socket;
106: //первые три числа - протокольная обвязка
107: var PacketLength:int;
108: ClientNumber = Socket1.readInt();
109: PacketNumber = Socket1.readInt();
110: PacketLength = Socket1.readInt();
111: //вычитали данные
112: ReceivedBytes = new ByteArray();
113: Socket1.readBytes(ReceivedBytes);
114: dispatchEvent(new Event(Network_Listener.DATA_RECEIVED));
115:
116: Socket1.flush();
117: //заголовок возвращаем обратно в сеть
118: Socket1.writeInt(ClientNumber);
119: Socket1.writeInt(PacketNumber);
120: Socket1.writeInt(ReceivedBytes.length);
121: Socket1.flush();
122: if (ReceivedBytes.length!=PacketLength){
123: dispatchEvent(new Event(Network_Listener.PROTOCOL_ERROR));
124: }
125: }
126: catch (error:Error)
127: {
128: dispatchEvent(new Event(Network_Listener.DATA_ERROR));
129: }
130: }
131:
132: public function ReadString():String{
133: try {
134: ReceivedBytes.position=0;
135: return (ReceivedBytes.readUTFBytes(ReceivedBytes.length));
136: }
137: catch (error:Error)
138: {
139: dispatchEvent(new Event(Network_Listener.STRING_ERROR));
140:
141: }
142: return "";
143: }
144:
145: public function Network_Close():void
146: {
147: try {
148: ServerSocket1.close();
149: }
150: catch (error:Error)
151: {
152: dispatchEvent(new Event(Network_Listener.CLOSE_ERROR));
153: }
154: }
155:
156: private function socketCloseHandler(e:Event):void
157: {
158: dispatchEvent(new Event(Network_Listener.PORT_CLOSE));
159: }
160: }
161: }
1: //минимальный вариант отправки данных сокетному серверу на адресе RemoteIP.text и порту RemotePort.text
2: //
3: //import ru.net.asp.socket.*;
4: //private var RemoteTextSender:Network_Sender = new Network_Sender( RemoteIP.text, RemotePort.text );
5: //RemoteTextSender.addEventListener(Network_Sender.DATA_SENDED, ERROR_handler);
6: //RemoteTextSender.addEventListener(Network_Sender.DATA_ERROR, ERROR_handler);
7: //RemoteTextSender.addEventListener(Network_Sender.CONNECT, ERROR_handler);
8: //RemoteTextSender.addEventListener(Network_Sender.OPEN_ERROR, ERROR_handler;
9: //RemoteTextSender.addEventListener(Network_Sender.CLOSE_ERROR, ERROR_handler);
10: //RemoteTextSender.addEventListener(Network_Sender.PORT_OPEN, ERROR_handler;
11: //RemoteTextSender.addEventListener(Network_Sender.PORT_CLOSE, ERROR_handler);
12: //RemoteTextSender.addEventListener(Network_Sender.PROTOCOL_ERROR, ERROR_handler);
13: //RemoteTextSender.addEventListener(Network_Sender.SECURITY_ERROR, ERROR_handler);
14: //RemoteTextSender.Network_Open();
15: //
16: //обработчик ошибок
17: //protected function ERROR_handler(e:Event):void{
18: // ErrorMessage.text += e.type + " on " + e.currentTarget.ipAddress + ":" + e.currentTarget.PortNumber +"\n";
19: //}
20: //
21: //посылка в сеть строки
22: //if (RemoteTextSender !== null){
23: // RemoteTextSender.SendString(SendMessage.text);
24: //}
25: //
26: //посылка в сеть байтового массива
27: //if (RemoteTextSender !== null){
28: // var Buf1:ByteArray=new ByteArray;
29: // Buf1.writeDouble(Math.PI);
30: // Buf1.position=0;
31: // RemoteTextSender.Send(SendMessage.text);
32: //}
33: //
34: //при передаче можно еще задать два параметра - номер пакета и номер клиента
35: // RemoteTextSender.ClientNumber = 1001;
36: // RemoteTextSender.PacketNumber += 1;
37:
38: package ru.net.asp.socket
39: {
40: import flash.events.*;
41: import flash.events.IEventDispatcher;
42: import flash.net.Socket;
43: import flash.utils.ByteArray;
44:
45: public class Network_Sender extends EventDispatcher
46: {
47:
48: public var SendBytes:ByteArray; //данные для передачи
49: public var ClientNumber:int; //идентификационный номер клиента
50: public var PacketNumber:int; //номер пакета
51:
52: //протокол клиента, передающего данные на сокетный сервер
53: public static const OPEN_ERROR:String="OPEN_ERROR";
54: public static const PORT_OPEN:String="PORT_OPEN";
55: public static const CONNECT:String="CONNECT";
56: public static const DATA_SENDED:String="DATA_SENDED";
57: public static const DATA_ERROR:String="DATA_ERROR";
58: public static const PROTOCOL_ERROR:String="PROTOCOL_ERROR";
59: public static const PROTOCOL_OK:String="PROTOCOL_OK";
60: public static const SECURITY_ERROR:String="SECURITY_ERROR";
61: public static const CLOSE_ERROR:String="CLOSE_ERROR";
62: public static const PORT_CLOSE:String="PORT_CLOSE";
63:
64:
65: public function get PortNumber():int{
66: return portNumber;
67: }
68: private var portNumber:int;
69: public function get IpAddress():String{
70: return ipAddress;
71: }
72: private var ipAddress:String;
73:
74: public function get Sender_Socket():Socket{
75: return SenderSocket1;
76: }
77: private var SenderSocket1:Socket;
78:
79: public function Network_Sender(RemoteIPaddr:String, RemotePortNumber:Object):void
80: {
81: super();
82: portNumber = Number(RemotePortNumber);
83: ipAddress = RemoteIPaddr;
84: SendBytes=new ByteArray;
85: SenderSocket1 = new Socket();
86: }
87:
88: public function Network_Open():void
89: {
90: try
91: {
92: SenderSocket1.addEventListener(Event.CLOSE, SocketCloseHandler);
93: SenderSocket1.addEventListener(Event.CONNECT, SocketConnectHandler);
94: SenderSocket1.addEventListener(IOErrorEvent.IO_ERROR, SocketIoErrorHandler);
95: SenderSocket1.addEventListener(SecurityErrorEvent.SECURITY_ERROR, SocketSecurityErrorHandler);
96: SenderSocket1.addEventListener(ProgressEvent.SOCKET_DATA, socketSocketDataHandler);
97: SenderSocket1.connect(ipAddress, portNumber);
98: dispatchEvent(new Event(Network_Sender.PORT_OPEN));
99: }
100: catch (error:Error)
101: {
102: dispatchEvent(new Event(Network_Sender.OPEN_ERROR));
103: }
104: }
105:
106: private function SocketConnectHandler(event:Event):void {
107: dispatchEvent(new Event(Network_Sender.CONNECT));
108: }
109:
110: //все что передавать побайтово - задается глобальными переменными отдельно
111: public function Send():void{
112: try
113: {
114: //пишем в сеть сначала заголовок
115: SenderSocket1.writeInt(ClientNumber);
116: SenderSocket1.writeInt(PacketNumber);
117: SenderSocket1.writeInt(SendBytes.length);
118: //пишем данные
119: SenderSocket1.writeBytes(SendBytes,0,SendBytes.length);
120: SenderSocket1.flush();
121: dispatchEvent(new Event(Network_Sender.DATA_SENDED));
122: }
123: catch (error:Error)
124: {
125: dispatchEvent(new Event(Network_Sender.DATA_ERROR));
126: }
127: }
128:
129: //упрощенный вариант передачи - только текст
130: public function SendString(Str1:String):void{
131: SendBytes=new ByteArray;
132: SendBytes.writeUTFBytes(Str1);
133: SendBytes.position=0;
134: Send();
135: }
136:
137: private function socketSocketDataHandler(e:ProgressEvent):void {
138: try
139: {
140: var Sicket1:Socket=e.currentTarget as Socket; //читаем ответ, по протокоду - просто возвращается заголовок
141: var RequestClientNumber:int= SenderSocket1.readInt();
142: var RequestPacketNumber:int= SenderSocket1.readInt();
143: var RequestSendBytes:int= SenderSocket1.readInt();
144: SenderSocket1.flush();
145: if (RequestClientNumber!=ClientNumber || RequestPacketNumber!=PacketNumber || RequestSendBytes!=SendBytes.length){
146: dispatchEvent(new Event(Network_Sender.PROTOCOL_ERROR));
147: }
148: else {
149: dispatchEvent(new Event(Network_Sender.PROTOCOL_OK));
150: }
151: }
152: catch (error:Error)
153: {
154: dispatchEvent(new Event(Network_Sender.DATA_ERROR));
155: }
156: }
157:
158: private function SocketCloseHandler(event:Event):void {
159: try
160: {
161: dispatchEvent(new Event(Network_Sender.PORT_CLOSE));
162: }
163: catch (error:Error)
164: {
165: dispatchEvent(new Event(Network_Sender.CLOSE_ERROR));
166: }
167: }
168:
169: private function SocketIoErrorHandler(event:IOErrorEvent):void {
170: dispatchEvent(new Event(Network_Sender.DATA_ERROR));
171: }
172:
173: private function SocketSecurityErrorHandler(event:SecurityErrorEvent):void {
174: dispatchEvent(new Event(Network_Sender.SECURITY_ERROR));
175: }
176:
177: public function Network_Close():void
178: {
179: try {
180: SenderSocket1.close();
181: }
182: catch (error:Error)
183: {
184: dispatchEvent(new Event(Network_Listener.CLOSE_ERROR));
185: }
186: }
187: }
188: }
Для того чтобы организовать простейшую текстовую болталку на базе этих классов - надо сделать следующее. Разместить на форме параметры для работы этого класса - IP и порт. Понятно, что в реальном приложении эти адреса и порты могут не только задаваться явно на форме (как в моем тексте), но и вычитываться из конфига, с сервера, генерироваться динамически и так далее. Применение этого класса для передачи по сети бинарных данных вы можете посмотреть на страничке - Видео-камеры, видео-чаты и Flash-медиасервера (работающие по RTMP и самописным протоколам).
Итак, для простейшего текстового чата разместим на форме кнопку отправки текстового сообщения и форму отображения текстового сообщения:
1: <s:TileGroup horizontalGap="12" verticalGap="12" left="10" right="10" top="10" bottom="10">
2: <mx:Form dropShadowVisible="true" borderAlpha="1.0" borderVisible="true" borderStyle="solid" width="300" height="132">
3: <mx:FormHeading label="Message:"/>
4: <mx:FormItem>
5: <s:RichText id="AllMessage" />
6: </mx:FormItem>
7: <mx:FormItem >
8: <s:TextInput id="SendMessage" width="100%" enter="SendMessage_enterHandler(event)"/>
9: </mx:FormItem>
10: <mx:FormItem>
11: <s:Button label="Send" id="MessageSend" click="MessageSend_clickHandler(event)"/>
12: </mx:FormItem>
13: </mx:Form>
14: <mx:Form width="300" dropShadowVisible="true" borderStyle="solid" borderVisible="true">
15: <mx:FormHeading label="SERVER"/>
16: <mx:FormItem label="Local IP" id="LocalIP">
17: <s:Label text="127.0.0.1"/>
18: </mx:FormItem>
19: <mx:FormItem label="Local Audio Port">
20: <s:TextInput text="101" id="LocalAudioPort"/>
21: </mx:FormItem>
22: <mx:FormItem label="Local Video Port">
23: <s:TextInput text="102" id="LocalVideoPort"/>
24: </mx:FormItem>
25: <mx:FormItem label="Local Text Port">
26: <s:TextInput text="103" id="LocalTextPort"/>
27: </mx:FormItem>
28: <mx:FormItem>
29: <s:Button label="Start" id="StartLocalNetwork" click="StartLocalNetwork_clickHandler(event)"/>
30: </mx:FormItem>
31: <mx:FormItem>
32: <s:Button label="Stop" id="StopLocalNetwork" click="StopLocalNetwork_clickHandler(event)"/>
33: </mx:FormItem>
34: </mx:Form>
35: <mx:Form width="300" dropShadowVisible="true" borderStyle="solid" borderVisible="true">
36: <mx:FormHeading label="SENDER:"/>
37: <mx:FormItem label="Remote IP">
38: <s:TextInput text="127.0.0.1" id="RemoteIP"/>
39: </mx:FormItem>
40: <mx:FormItem label="Remote Audio Port">
41: <s:TextInput text="101" id="RemoteAudioPort"/>
42: </mx:FormItem>
43: <mx:FormItem label="Remote Video Port">
44: <s:TextInput text="102" id="RemoteVideoPort"/>
45: </mx:FormItem>
46: <mx:FormItem label="Remote Text Port">
47: <s:TextInput text="103" id="RemoteTextPort"/>
48: </mx:FormItem>
49: <mx:FormItem>
50: <s:Button label="Start" id="StartRemoteNetwork" click="StartRemoteNetwork_clickHandler(event)"/>
51: </mx:FormItem>
52: <mx:FormItem>
53: <s:Button label="Stop" id="StopRemoteNetwork" click="StopRemoteNetwork_clickHandler(event)"/>
54: </mx:FormItem>
55: </mx:Form>
56: <mx:Form dropShadowVisible="true" borderAlpha="1.0" borderVisible="true" borderStyle="solid" width="300" height="132">
57: <mx:FormHeading label="System message:"/>
58: <mx:FormItem >
59: <s:RichText id="ErrorMessage" text="" />
60: </mx:FormItem>
61: </mx:Form>
62: </s:TileGroup>
А далее нужно добавить вызов этого класса, например так:
1: <fx:Script>
2: <![CDATA[
3: import flash.events.*;
4: import mx.controls.Alert;
5: import mx.events.FlexEvent;
6: import mx.graphics.codec.JPEGEncoder;
7: import ru.net.asp.socket.*;
8:
9: protected function windowedapplication1_contentCreationCompleteHandler(event:FlexEvent):void
10: {
11: var Sender1:Network_Sender=new Network_Sender("127.0.0.1",101);
12: }
13:
14: ...
15: //***********************************************************************
16: ...
17:
18: private var ServerAudioListener:Network_Listener;
19: private var ServerVideoListener:Network_Listener;
20: private var ServerTextListener:Network_Listener;
21: private var Loader1:Loader;
22:
23: protected function StartLocalNetwork_clickHandler(event:MouseEvent):void
24: {
25: try {
26: ServerAudioListener = NetworkServerPrepare(ServerAudioListener,LocalAudioPort.text);
27: ServerVideoListener = NetworkServerPrepare(ServerVideoListener,LocalVideoPort.text);
28: ServerTextListener = NetworkServerPrepare(ServerTextListener, LocalTextPort.text);
29: ServerAudioListener.addEventListener(Network_Listener.DATA_RECEIVED, Server_Audio_DATA_RECEIVED_handler);
30: ServerVideoListener.addEventListener(Network_Listener.DATA_RECEIVED, Server_Video_DATA_RECEIVED_handler);
31: ServerTextListener.addEventListener(Network_Listener.DATA_RECEIVED, Server_Text_DATA_RECEIVED_handler);
32: }
33: catch (error:Error)
34: {
35: ErrorMessage.text += "Server Error: " + error.message + "\n";
36: }
37: }
38:
39: private function NetworkServerPrepare(X:Network_Listener, LocalPort:String):Network_Listener{
40: X = new Network_Listener(LocalPort);
41: X.addEventListener(Network_Listener.DATA_ERROR, ServerInfo_handler);
42: X.addEventListener(Network_Listener.CONNECT, ServerInfo_handler);
43: X.addEventListener(Network_Listener.OPEN_ERROR, Server_OPEN_ERROR_handler);
44: X.addEventListener(Network_Listener.CLOSE_ERROR, ServerInfo_handler);
45: X.addEventListener(Network_Listener.PORT_OPEN, ServerInfo_handler);
46: X.addEventListener(Network_Listener.PORT_CLOSE, ServerInfo_handler);
47: X.addEventListener(Network_Listener.PROTOCOL_ERROR, ServerInfo_handler);
48: X.addEventListener(Network_Listener.STRING_ERROR, ServerInfo_handler);
49: X.Network_Open();
50: return X;
51: }
52:
53: protected function StopLocalNetwork_clickHandler(event:MouseEvent):void
54: {
55: try {
56: ServerAudioListener.Network_Close();
57: ServerVideoListener.Network_Close();
58: ServerTextListener.Network_Close();
59: }
60: catch (error:Error)
61: {
62: ErrorMessage.text += "Local error: " + error.message + "\n";
63: }
64: }
65:
66: protected function ServerInfo_handler(e:Event):void{
67: ErrorMessage.text += "Server: " + e.type + " on local port " + e.currentTarget.PortNumber +"\n";
68: }
69: protected function Server_OPEN_ERROR_handler(e:Event):void{
70: ErrorMessage.text += "Server local port " + e.currentTarget.PortNumber + " in use. Select another port. \n";
71: }
72:
73: protected function Server_Text_DATA_RECEIVED_handler(e:Event):void{
74: ErrorMessage.text += "Server: " + e.type + " on local port " + e.currentTarget.PortNumber +"\n";
75: AllMessage.text += "<=" + e.currentTarget.ReadString() + "\n";
76: }
77:
78: ...
79: //***********************************************************************
80: ...
81:
82: private var SenderAudioSender:Network_Sender;
83: private var SenderVideoSender:Network_Sender;
84: private var SenderTextSender:Network_Sender;
85:
86: protected function StartRemoteNetwork_clickHandler(event:MouseEvent):void
87: {
88: try {
89: SenderTextSender = NetworkSenderPrepare(SenderTextSender, RemoteIP.text, RemoteTextPort.text);
90: ...
91: }
92: catch (error:Error)
93: {
94: ErrorMessage.text += "Sender Error: " + error.message + "\n";
95: }
96: }
97:
98: private function NetworkSenderPrepare(X:Network_Sender,RemoteIpAddr:String, RemotePort:String):Network_Sender{
99: X = new Network_Sender(RemoteIpAddr, RemotePort);
100: X.addEventListener(Network_Sender.DATA_SENDED, SenderInfo_handler);
101: X.addEventListener(Network_Sender.DATA_ERROR, SenderInfo_handler);
102: X.addEventListener(Network_Sender.CONNECT, SenderInfo_handler);
103: X.addEventListener(Network_Sender.OPEN_ERROR, SenderInfo_handler);
104: X.addEventListener(Network_Sender.CLOSE_ERROR, SenderInfo_handler);
105: X.addEventListener(Network_Sender.PORT_OPEN, SenderInfo_handler);
106: X.addEventListener(Network_Sender.PORT_CLOSE, SenderInfo_handler);
107: X.addEventListener(Network_Sender.PROTOCOL_ERROR, SenderInfo_handler);
108: X.addEventListener(Network_Sender.PROTOCOL_OK, SenderInfo_handler);
109: X.addEventListener(Network_Sender.SECURITY_ERROR, SenderInfo_handler);
110: X.Network_Open();
111: return X;
112: }
113:
114: protected function StopRemoteNetwork_clickHandler(event:MouseEvent):void
115: {
116: try {
117: SenderAudioSender.Network_Close();
118: SenderVideoSender.Network_Close();
119: SenderTextSender.Network_Close();
120: }
121: catch (error:Error)
122: {
123: ErrorMessage.text += "Sender Error: " + error.message + "\n";
124: }
125: }
126:
127: protected function SenderInfo_handler(e:Event):void{
128: ErrorMessage.text += "Sender: " + e.type + " on " + e.currentTarget.IpAddress + ":" + e.currentTarget.PortNumber +"\n";
129: }
130:
131: protected function MessageSend_clickHandler(event:MouseEvent):void
132: {
133: AllMessage.text += "=>" + SendMessage.text + "\n";
134: if (SenderTextSender !== null){
135: SenderTextSender.SendString(SendMessage.text);
136: }
137: SendMessage.text="";
138: }
139: ]]>
140: </fx:Script>
Одновременно эта страничка получилась прекрасной демонстрацией обьектных возможностей ActionScript - в продолжение топика Реклама в видеоплеере (возможности объектного программирования ActionScript).
|