ADOBE/ ActionScript

[AS] Flash (AIR)와 소켓통신: 관련 데이터 나누어 지는 현상 보안

AlrepondTech 2020. 9. 23. 02:08
반응형

 

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

출처: http://www.delmadang.com/community/bbs_view.asp?bbsNo=17&indx=431359

AIR로 서버를 구축하고

델파이로 Client를 구축해보았습니다.

서버 -> 클라이언트 로 문자열 값을 전송테스트 하기위해

서버에 text필드 한개와 버튼을 만들고, 

text필드에 써진 문자열을 전송하고 버튼을 누르지 않아도 추가적으로 end 라는 

문자열을 추가전송 시켜보았습니다.

클라이언트는 서버에서 전송된 내용을 받아서 메모에 찍도록 해두었습니다.

서버에서 start 라는 문자열 을 전송한 경우///

클라이언트가 처음 접속하면 메모에 찍힐때..

receive => start

receive => end 

이렇게 2번 문자열이 온 것으로 처리가 되어 찍히고..

다시한번 start 라는 문자열을 전송하면

receive => startend

이런식으로 문자열이 2개가 오는 것이 아니라 1개로 합쳐져서 오게 됩니다..

일반적으로 소켓에서 문자열을 받을때..

TCPClient.readString 을 이용하는데..플래시와 하다보니 다음과 같이 문자열 값을

받게 해두었습니다..




var

  si,cnt : Integer;

  us: UTF8String;

begin

  with TcpClient do begin



    cnt := ReadFromStack;

    us := ReadString(cnt);

    Form1.Memo1.Lines.Add('receive->'+Utf8ToAnsi(us));

    result := Utf8ToAnsi(us);

  end;

end;



클라이언트에서 문자열이 합쳐져 들어 오는 근본적인 원인을 해결할 수 있는 방법이 없을지..

고수님들의 조언을 부탁드립니다;;

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

참 자주 듣는 질문인데요
버그는 아니구요 당연한 결과 입니다.
구분자를 두어 파싱하셔야 합니다.
데이타크기가 크지 않다면 요즘 많이 사용하는 xml로 보내는것도
좋을것 같군요.

참고로 Ttcpclient라면 Receiveln을 이용하면 편합니다.

보낼때 `start`+#9

받을때 TcpClient.Receiveln(#9)하시면 됩니다.

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

그리고 왜 합쳐서 들어오는게 당연 하나면요,
제가 시하나 낭송할께요.
"가카새키 짬뽕은 국물이 끝내줘요."
한번에 들으셨죠?
전 그게 아니었고
"가카새키 짬뽕은"
"국물이 끝내줘요."
이렇게 두줄이었습니다.
상대방은 1줄인지 3줄인지 알길이 없잖아요.
무전기처럼 어쩌구저쩌구 "오바" 하듯이 끝임을 알려줘야 하겠지요

또한가지 방법은
"가카새키 짬뽕은" 한박자 쉬고
"국물이 끝내줘요."
하면 2둘로 따로따로 들어온걸 알겠지요.
실제로 start와 end를 시간차를 주고 전송하면
이벤트도 2번 일어나고 start end가 따로따로 들어 옵니다.

이방식은 추천하지 않아요.
비효율적인면도 있지만, 인터넷환경이나 클라이언트에서 즉각 받지않고 딴짓하면 역시나 읽을땐 붙어서 읽혀져요,

결론은 
(1) send(`start`);
    send(`end`);

(2) send(`startend`);

두가지가 같은결과 입니다.

(1) send(`start`+chr(9));
    send(`end`);

(2) send(`start`+chr(9)+`end`);

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://mo_ksha.blog.me/90075708099

 

바이너리 소켓을 사용해서 플렉스 구현중이다.


플렉스 내부에서는 소켓을 생성하여 
웹서버를 이용하지 않고 C++로 제작한 서버에 접속하고 있다.


웹브라우져로 어디서든 접속이 가능하게 하려면
'샌드박스' 보안 모델 때문에 약간의 설정이 필요하다.




참고서적에는
'웹서버루트에 crossdomin.xml 파일을 만들고 목적에 맞게 설정하라.' 라고 했지만
이건 잘 안되는 것이...


브라우져는 웹서버에서 플렉스파일(swf)을 가져오고,
플렉스파일에서는 C++서버에서 정보를 얻고 있는데
보안은 C++서버에서 자료를 얻어오지 못하는데 있었다.


웹서버도 아니니 웹루트에 설정파일을 만들어두는 것은 의미가 없어 느껴져서
Adobe 사이트를 툴러보니 Policy File Server 를 만들라며 샘플이 올려져 있는데
받아보니 xml 파일만 달랑.








Adobe의 상세(?)한 설명에 따르면 보안설정의 방식은 2가지인데,


하나가 웹서버의 루트에 crossdomain.xml 을 설정하는 것. 이 방법은 웹서버가 아니기에 적용하지 못할 것 같고,
나머지 방법은, '최초 소켓통신을 설정할 때 설정정보를 보내주는 방식' 이란다.


두번째 방법은 어찌 하라는 건지 이해하지 못하다가
'서버에서 Accep 할 때 정보를 보내라는 뜻인가?' 라는 생각에
서버에서 클라이언트 접속을 수락하면 곧바로 정책정보를 전송하는 코드를 추가하였다.


#################################################################
void NetworkPeer:OnAccept()    // 클라이언트의 접속을 처리한다.
{
char accept_domain[200]="<cross-domain-policy>\n <site-control permitted-cross-domain-policies=\"all\"/>\n <allow-access-from domain='*' to-ports='*' />\n</cross-domain-policy>\n"; 


Send(accept_domain, sizeof(accept_domain) );
}
#################################################################


이렇게 하게되면 플렉스의 소켓 수신 핸들러 부분에서는 바이너리 서버에 접속할때 수신받는
정책정보( 200 바이트 )를 수신하는 것을 볼 수 있다. 재미있게 생각되는 건 이 정책파일을
수신받아 응용단에서 특별한 처리를 하지는 않아도 보안샌드박스 문제를 통과한다는 것이다.
플렉스 내부에서 수신이벤트를 호출하기 전에 내용을 살펴본다고 생각할 수 있겠다.
[출처] 플렉스 소켓 통신에서 보안 주의사항(crossdomain.xml policyfile.xml)|작성자 래시

 

 

 

=================================

=================================

=================================

 

 

출처ㅣ http://blog.jidolstar.com/629

지난 2009년 11월 어도비(Adobe)에서는 AIR 2.0 Beta를 새롭게 발표하면서 퍼포먼스 향상과 더 많은 OS의 자원을 사용할 수 있는 API 기능을 추가해서 발표했다. Flash Player 10.1 Prerelease와 함께 발표된 Adobe AIR 2.0의 새로운 기능 및 추가/개선 사항에 대해서 정리해보았다. 

참고로 이 글은 Elad Elrom의 AIR 2 Enhancements Complete Overview  와 AIR 2 Rerelease Note를 참고해서 정리한 것이다. 




  1. 새로운 기능 (이번 페이지에서 다룸)
    - File Promises
    - Screen reader
    - Native Processes
    - New networking support
    - Global Error Handling
    - Packaging an AIR application in a native installer
  2. 기존 API에 대한 추가된 기능 (다음 편에 다룸)
    - Max Size of NativeWindow
    - Idle time-out
    - Mac Vector Printing
    - Database Transaction Savepoints
    - Microphone Access API
    - Open file with Default Application
  3. 플랫폼 인식 관련 API (다음 편에 다룸)
    - Multi-touch functionality
    - Mass Storage Device Detection
  4. 퍼포먼스 향상 (다음 편에 다룸)



 

 

출처 : http://tinyurl.com/ydxevnu




Flash Builder 4 beta 2에서 Adobe AIR 2.0로 개발 환경 만들기 

여기에서 보여지는 예제는 모두 Flash Builder 4 beta 2에 기본으로 설치된 Flex 4 SDK를 이용한다. 하지만 AIR 2.0로 테스트해보기 위해서는 AIR 2.0 SDK를 따로 받아 다음과 같은 과정이 필요하다. 

Windows 환경이라면 다음과 같이 하면 되겠다. 

 

  • 만약 Flash Builder를 설치 안했다면 다음 링크를 통해 받는다.
    http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026
    1. AIR 2.0 SDK와 Runtime을 다운로드 받는다.
    2. 다운받은 Runtime을 실행해 설치한다.
    3. Flash builder가 설치된 sdks/4.0.0과 sdks/3.4.1 폴더를 복사해서 sdks 폴더안에 붙인뒤 4.0.0_AIR2.0, 3.4.1_AIR2.0을 만든다. 이렇게 되면 sdks 폴더안에는 3.4.1, 4.0.0, 3.4.1_AIR2.0, 4.0.0_AIR2.0 이름을 가진 4개의 SDK가 만들어진다.
      본인의 경우는 C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks 내에 있다. 
    4. AIR 2.0 SDK는 압축을 풀고 그안에 있는 내용을 방금 만든 4.0.0_AIR2.0, 3.4.1_AIR2.0 폴더에 복사한다. 
    5. Flash Builder를 실행한다.
    6. 메뉴에서 Window > Preference 로 들어간다.
    7. 창이 뜨면 왼쪽 메뉴에서 Flash builder > Installed Flex SDKs를 선택한다.
    8. 우측에 Add버튼을 눌러 위에서 새로 만든 AIR 2.0을 위한 SDKs들을 선택한다.  
      Flex SDK Name은 각각 Flex 3.4 AIR 2.0, Flex 4.0 AIR 2.0 등으로 이름을 바꿔도 된다.
      본인의 경우 아래 경로가 되겠다.
      C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks/3.4.1_AIR2.0/
      C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks/4.0.0_AIR2.0/
      아래처럼 Flex 4.0으로 체크되어 있다면 AIR 2.0기반의 Flex 4.0 SDK를 디폴트로 바꾸자.
    9. 이제 모든 개발환경이 완료되었다. 자신의 프로젝트 생성시에 원하는 SDK를 선택하면 되겠다.  물론 아래에서 소개하는 예시를 따르려면 Flex 3든 Flex 4든 AIR 2.0 SDK를 선택해야겠다.


    Mac 환경이라면 다음과 같이 하면 되겠다. 

    Mac에서 AIR 2.0 Beta 개발환경 만들기 - Flash Builder 4 Beta 2 기반

    참고로 Flash CS4나 CS3, 드림위버나 기타 다른 IDE에서 개발하시는 분들에게는 본인의 경험이 없어 정보를 줄 수 없다.

 


  
1. Adobe AIR 2.0 새로운 기능 

1.1 IPv6 지원 
AIR 2.0은 모든 Network API에 대해서 IPv6를 지원하게 되었다. 현재 인터넷에서 주로 사용하고 있는 IP 주소의 버전은 IPv4로서 무려 20년이 넘도록 사용하고 있다. IPv4는 32비트 주소체계를 가지며 4,294,967,296개의 제한된 갯수의 주소를 가진다. IPv6는 이 부족함을 해소하기 위해 만들어진 IP 주소 체계이다. IPv6는 128비트 주소체계를 가진다. IPv6가 일반적으로 사용되고 있는 것은 아니지만  가까운 미래에 IPv6가 범용적으로 도입되어 네트워크 IP 주소체계가 더욱 확장될 수 있으므로 미리 지원될 수 있도록 한 것으로 여겨진다. IPv6에 대한 자세한 내용은 아래 링크를 참고한다.




1.2  UDP(User Datagram Protocol) 소켓, Server 소켓, TLS/SSL 소켓 지원
AIR 2.0에서는 더욱 다양한 소켓 통신 관련 클래스들이 추가되었다. 이전버전까지 반쪽짜리 였다면 이제 어느정도 구색을 갖춘듯 하다. 

AIR 2.0에서 지원하는 네트워크에 관련된 내용은 다음 링크를 참고한다.



서버 소켓 : ServerSocket 클래스 
기존 AIR 버전에서는 반쪽 소켓통신만 지원되었다. 즉, 클라이언트는 만들 수 있었지만 서버측을 만들지 못했다. 이번 AIR 2.0에서 서버 소켓이 지원됨에 따라 이를 이용한 다양한 네트워크 애플리케이션을 만들 수 있을 것으로 보인다. 

서버 소켓은 TCP(Transmissin Control Protocal)을 기반으로 한다. TCP는 연결지향 통신 규약으로 인터넷 상의 컴퓨터들 사이에 데이터를 통신하기 위해 IP와 함께 사용된다. TCP는 데이터의 통신 중간에 데이터가 유실되더라도 신뢰도를  체크해서 완벽한 데이터를 통신할 수 있는 신뢰형 기반의 데이터 통신시 사용된다. 일반적인 웹브라우져를 이용한 통신데이터는 모두 TCP를 근간으로 한다. 



AIR 2.0에서 지원하는 서버 Socket는 ServerSocket 클래스에서 관장한다.



UDP 소켓 : DatagramSocket 클래스
UDP는 User Datagram Protocal의 약어로 IP를 사용해 네트워크내에서 컴퓨터들간에 데이타 통신하기 위해 제한된 서비스만 제공하는 통신 규칙이다. TCP의 대안이기도 하며 IP와 함께 사용하면 UDP/IP라고 표현하기도 한다. UDP는 정확한 데이터를 전송할 의무가 주어지지 않은 비연결지향성을 가져 데이터의 흐름에 신뢰를 할 수 없는 것이 특징이다. TCP처럼 통신을 하기전 상대방을 확인하는 절차가 없기 때문에 데이터 정확도는 떨어질 수 있지만 빠른 데이타 전송에 유리하다. 그래서 음악 및 비디오 실시간 스트리밍 서비스에 잘 사용하는 프로토콜이다. 



AIR 2.0에서는 UDP 통신을 DatagramSocket 클래스와 DatagramSocketDataEvent 클래스를 지원하고 있다. 더욱 자세한 내용은 아래 링크를 참고한다.



Secure 클라이언트 소켓 : SecureSocket 클래스 
소켓서버에 접속하기 위해 SSLv4(Secure Socket Layer version 4) 또는 TLSv1(Transport Layer Security version 1) 기반으로 하는 프로토콜을 이용하여 SecureSocket 클래스로 접속할 수 있다. Secure 소켓을 이용하면 암호화된 데이터 전송으로 서버인증(server authentication), 데이터 무결성(data integrity), 메시지 기밀성(message confidentiality)등의 장점을 가진다. 




1.3 네트워크 정보 
AIR 2.0의 API로 네트워크 정보를 얻어낼 수 있는 클래스인 NetworkInfo가 추가되었다. 이 클래스를 이용하면 사용자의 기기의 Mac Address, IP정보 등의 네트워크 정보를 얻어올 수 있다.


Adobe 문서에서 보여지는 예제에서는 var networkInfo:NetworkInfo = new NetworkInfo() 처럼 생성해서 사용하도록 되어 있는데 잘못된 것이다. var networkInfo:NetworkInfo = NetworkInfo.networkInfo; 처럼 static 속성으로 접근해야한다. 
그리고 NetworkInterface에 networkInterfaceDefault 속성 또한 존재하지 않는다.  

package {
    import flash.display.Sprite;
    import flash.net.InterfaceAddress;
    import flash.net.NetworkInfo;
    import flash.net.NetworkInterface;
     
    public class NetworkInformationExample extends Sprite
    {
        public function NetworkInformationExample()
        {
            var networkInfo:NetworkInfo.<NetworkInterface> = NetworkInfo.networkInfo;
            var interfaces:Vector.<NetworkInterface> = networkInfo.findInterfaces();
             
            if( interfaces != null )
            {
                trace( "Interface count: " + interfaces.length );
                for each ( var interfaceObj:NetworkInterface in interfaces )
                {
                    trace( "\nname: "             + interfaceObj.name );
                    trace( "display name: "     + interfaceObj.displayName );
                    trace( "mtu: "                 + interfaceObj.mtu );
                    trace( "active?: "             + interfaceObj.active );
                    //trace( "default?: "         + interfaceObj.networkInterfaceDefault );
                    trace( "parent interface: " + interfaceObj.parent );
                    trace( "hardware address: " + interfaceObj.hardwareAddress );
                    if( interfaceObj.subInterfaces != null )
                    {
                        trace( "# subinterfaces: " + interfaceObj.subInterfaces.length );
                    }
                    trace("# addresses: "     + interfaceObj.addresses.length );
                    for each ( var address:InterfaceAddress in interfaceObj.addresses )
                    {
                        trace( "  type: "           + address.ipVersion );
                        trace( "  address: "         + address.address );
                        trace( "  broadcast: "         + address.broadcast );
                        trace( "  prefix length: "     + address.prefixLength );
                    }
                }            
            }
        }    
    }
}

 

 

  
위 프로그램은 NetworkInfo를 사용하는 아주 간단한 예시이다. Flash Builder 4 에서 이 예시를 테스트해볼때는 AIR 프로젝트 생성시 마지막에 mxml대신 as로 확장자를 두고 생성해서 테스트 하면 되겠다.


1.4 DNS lookup
AIR 2.0에서부터 DNSResolver 클래스를 제공하여 DNS(Domain Name System) 정보를 찾을 수 있게 되었다. 정보를 찾게되면 DNSResolverEvent가 발생한다. 

DNS는 각 도메인 이름(가령 examples.com)을 IP주소로 가리킨다. 일반적으로 사람은 111.123.431.11와 같은 IP주소보다 examples.com처럼 도메인 이름과 같은 단어조합을 더욱 인지하기 쉽다. 이러한 이유로 실제로는 도메인 이름을 사용하면 DNS를 걸쳐 이에 해당하는 IP주소로 연결시켜 관련 서버로 접속하게 한다. AIR 2.0에서 제공하는 DNSResolver 클래스를 이용하면 이러한 관계를 조사해볼 수 있게 된다. 

IP주소는 IPv4(32비트) 또는 IPv6(128비트)를 사용할 수 있다. 

DNSResolver 클래스의 lookup() 함수를 통해 host이름과 레코드 형태(record type)을 넘겨주면 DNSResolverEvent.LOOK_UP 이벤트를 발생하여 관련 레코드 타입에 대한 정보를 반환해준다. 
 




 다음은 예제이다.

package
{
    import flash.desktop.NativeApplication;
    import flash.display.Sprite;
    import flash.events.DNSResolverEvent;
    import flash.events.ErrorEvent;
    import flash.net.dns.AAAARecord;
    import flash.net.dns.ARecord;
    import flash.net.dns.DNSResolver;
    import flash.net.dns.MXRecord;
    import flash.net.dns.PTRRecord;
    import flash.net.dns.SRVRecord;
    import flash.utils.getQualifiedClassName;
     
    public class DNSResolverExample extends Sprite
    {
        private var lookupCount:int = 0;
        public function DNSResolverExample()
        {
            //Create the resolver object
            var resolver:DNSResolver = new DNSResolver();
            resolver.addEventListener( DNSResolverEvent.LOOKUP, lookupComplete );
            resolver.addEventListener( ErrorEvent.ERROR, lookupError );
             
            //Look up records
            resolver.lookup( "yahoo.com", ARecord );
            resolver.lookup( "yahoo.com", AAAARecord );
            resolver.lookup( "yahoo.com", MXRecord );
            resolver.lookup( "209.191.93.53", PTRRecord );
            resolver.lookup( "_sip._tcp.yahoo.com.", SRVRecord );
        }
         
        private function lookupComplete( event:DNSResolverEvent ):void
        {
            trace("-------------------------------------");
            trace( "Query string: " + event.host );
            trace( "Record type: " +  flash.utils.getQualifiedClassName( event.resourceRecords[0] ) +
                ", count: " + event.resourceRecords.length );
            for each( var record:* in event.resourceRecords )
            {
                if( record is ARecord ) trace( record.name + " : " + record.address );
                if( record is AAAARecord ) trace( record.name + " : " + record.address );
                if( record is MXRecord ) trace( record.name + " : " + record.exchange + ", " + record.preference );
                if( record is PTRRecord ) trace( record.name + " : " + record.ptrdName );
                if( record is SRVRecord ) trace( record.name + " : " + record.target + ", " + record.port +
                    ", " + record.priority + ", " + record.weight );
            }   
            if( ++lookupCount == 5 )
            {
                NativeApplication.nativeApplication.exit();
            }
        }
         
        private function lookupError( error:ErrorEvent ):void
        {
            trace("-------------------------------------");
            trace("Error: " + error.text );
            if( ++lookupCount == 5 )
            {
                NativeApplication.nativeApplication.exit();
            }
        }
    }
}



1.5 스크린 리더 지원(Screen Reader Support)

AIR 2.0은 스크린 리더를 지원하기 위한 Accessibility 클래스가 추가되었다. 스크린 리더는 시각 장애 사용자들을 위한 보조 기술로서 화면 내용을 오디오 버전으로 제공하는 것이다. 이 클래스는 현재 Windows 환경에서만 제공된다. 이것이 지원되는지 확인하려면 Capabilities.hasAccessibility 정적속성이 true이어야 한다. 버튼, 무비클립, 텍스트필드와 같은 객체에 액세스 가능한 속성을 얻고 설정하기 위해 DisplayObject.accessibilityProperties 속성을 사용하며 이 값이 바뀌었다는 것을 알려주기 위해 Accessibility.updateProperties() 정적메소드를 이용한다. 

이 기능을 사용하기 위해 프로젝트의 컴파일 옵션을 추가해야한다. Flash builder 4에서 해당 프로젝트를 선택후 메뉴의 Project > Properties를 선택한다. 새창이 나오면 Flex Compiler를 선택후 컴파일 옵션에 Generate accessible SWF file을 선택후 OK 버튼을 누른다. 

package {
    import flash.accessibility.Accessibility;
    import flash.desktop.NativeApplication;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.utils.setTimeout;
     
    public class AccessibilityExample extends Sprite {
        public static const BUTTON_WIDTH:uint = 90;
        public static const BUTTON_HEIGHT:uint = 20;
         
        private var gutter:uint = 5;
        private var menuLabels:Array = new Array("PROJECTS", "PORTFOLIO", "CONTACT");
        private var menuDescriptions:Array = new Array("Learn more about our projects"
            , "See our portfolio"
            , "Get in touch with our team");
         
        public function AccessibilityExample() {
            configureAssets();
            NativeApplication.nativeApplication.autoExit = true;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.nativeWindow.activate();
            setTimeout(updateAccessibility, 2000);
        }
         
        private function updateAccessibility():void {
            trace("Accessibility.active: " + Accessibility.active);
            if(Accessibility.active) {
                Accessibility.updateProperties();
            }
        }
         
        private function configureAssets():void {
            var child:CustomAccessibleButton;
            for(var i:uint; i < menuLabels.length; i++) {
                child = new CustomAccessibleButton();
                child.y = (numChildren * (BUTTON_HEIGHT + gutter));
                child.setLabel(menuLabels[i]);
                child.setDescription(menuDescriptions[i]);
                addChild(child);
            }
        }
    }
}


import flash.accessibility.AccessibilityProperties;
import flash.display.Shape;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.TextField;


class CustomAccessibleButton extends Sprite {
    private var button:SimpleButton;
    private var label:TextField;
    private var description:String;
    private var _name:String;
     
    public function CustomAccessibleButton(_width:uint = 0, _height:uint = 0) {
        _width = (_width == 0) ? AccessibilityExample.BUTTON_WIDTH : _width;
        _height = (_height == 0) ? AccessibilityExample.BUTTON_HEIGHT : _height;
         
        button = buildButton(_width, _height);
        label = buildLabel(_width, _height);
         
        addEventListener(Event.ADDED, addedHandler);
    }
     
    private function addedHandler(event:Event):void {
        trace("addedHandler: " + this._name);
        var accessProps:AccessibilityProperties = new AccessibilityProperties();
        accessProps.name = this._name;
        accessProps.description = description;
        accessibilityProperties = accessProps;
        removeEventListener(Event.ADDED, addedHandler);
    }
     
    private function buildButton(_width:uint, _height:uint):SimpleButton {
        var child:SimpleButton = new CustomSimpleButton(_width, _height);
        addChild(child);
        return child;
    }
     
    private function buildLabel(_width:uint, _height:uint):TextField {
        var format:TextFormat = new TextFormat();
        format.font = "Verdana";
        format.size = 11;
        format.color = 0xFFFFFF;
        format.align = TextFormatAlign.CENTER;
        format.bold = true;
         
        var child:TextField = new TextField();
        child.y = 1;
        child.width = _width;
        child.height = _height;
        child.selectable = false;
        child.defaultTextFormat = format;
        child.mouseEnabled = false;
         
        addChild(child);
        return child;
    }
     
    public function setLabel(text:String):void {
        label.text = text;
        this._name = text;
    }
     
    public function setDescription(text:String):void {
        description = text;
    }
}


class CustomSimpleButton extends SimpleButton {
    private var upColor:uint = 0xFFCC00;
    private var overColor:uint = 0xCCFF00;
    private var downColor:uint = 0x00CCFF;
     
    public function CustomSimpleButton(_width:uint, _height:uint) {
        downState = new ButtonDisplayState(downColor, _width, _height);
        overState = new ButtonDisplayState(overColor, _width, _height);
        upState = new ButtonDisplayState(upColor, _width, _height);
        hitTestState = new ButtonDisplayState(upColor, _width, _height);
        useHandCursor = true;
    }       
}


class ButtonDisplayState extends Shape {
    private var bgColor:uint;
    private var _width:uint;
    private var _height:uint;
     
    public function ButtonDisplayState(bgColor:uint, _width:uint, _height:uint) {
        this.bgColor = bgColor;
        this._width = _width;
        this._height = _height;
        draw();
    }
     
    private function draw():void {
        graphics.beginFill(bgColor);
        graphics.drawRect(0, 0, _width, _height);
        graphics.endFill();
    }
}



1.6 네이티브 프로세스의 직접 실행과 상호 작용( Launching and Interacting with Native Processes )
네이티브 프로세스에 대해서 본인도 처음 접하는 용어라 해석하기 어려웠는데, 이해한바 각 운영체제에 실행될 수 있는 코드라고 생각하면 될 것 같다. 가령, 윈도우에서는 exe, dll, ocx등이겠고 맥이라면 dmg, app 등이 아닐까 생각한다.

AIR 2.0부터 기존의 애플리케이션을 실행해 AIR 애플리케이션과 표준입출력을 통한 상호작용할 수 있는 기능이 추가되었다. 가령, C언어로 만든 프로그램을 자신의 AIR 애플리케이션과 함께 배포했다고 하자. AIR 애플리케이션이 이 C언어로 만든 프로그램을 실행하고 표준입출력을 이용해 통신이 가능해진다. 또는 이미 설치된 Editplus에 File을 넘겨서 실행하고 종료시점도 알 수 있다. 

자세한 내용은 다음을 참고한다.

이 기능을 수행하려면 반드시 AIR 디스크립터 파일에 다음 코드를 꼭 넣어야 한다. 

<supportedProfiles>extendedDesktop</supportedProfiles>



아래는 Editplus를 이용해 Text파일을 열고 Editplus가 닫힐 때 감지하는 간단한 Flex 4를 이용한 AIR 2.0 애플리케이션 코드이다. NativeProcess와 NativeProcessStartupInfo 클래스의 용도를 잘 살펴보자. 

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/halo">
    <fx:Script>
        <![CDATA[
            import flash.events.NativeProcessExitEvent;
             
            public function executeNativeProcess():void
            {
                var executable:File = new File("C:/Program Files/EditPlus 3/editplus.exe"); //c:/Windows/System32/notepad.exe
                var workingDirectory:File = new File("c:/");
                 
                var nativeProcess:NativeProcess = new NativeProcess();
                 
                if (NativeProcess.isSupported)
                {
                    trace("Native Process Supported");
                }
                 
                var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                nativeProcessStartupInfo.executable = executable;
                nativeProcessStartupInfo.workingDirectory = workingDirectory;
                 
                var args:Vector.<String> = new Vector.<String>();  
                args.push("c:/LAN.log"); // open file that was given with the executable application
                nativeProcessStartupInfo.arguments = args;
                 
                nativeProcess.addEventListener( NativeProcessExitEvent.EXIT, onExitError );
                 
                try {
                    nativeProcess.start(nativeProcessStartupInfo);
                } catch (error:IllegalOperationError) {
                    trace("Illegal Operation: "+error.toString());
                } catch (error:ArgumentError) {
                    trace("Argument Error: "+error.toString());
                } catch (error:Error) {
                    trace ("Error: "+error.toString());
                }
                 
                if (nativeProcess.running)
                {
                    trace ("Native Process Support");
                }      
            }
             
            public function onExitError(event:NativeProcessExitEvent):void
            {
                trace( "Native Process Exit code: "+event.exitCode );
            }           
             
        ]]>
    </fx:Script>
     
    <s:Button id="button"
              label="Open File foobar.txt with text editor"
              click="executeNativeProcess();"
              width="250" />
     
</s:WindowedApplication>


Mac인 경우 Mac에 설치되어 기본으로 설치되어 있는 텍스트 에디터를 실행할 수도 있다. 이것도 별다른 설정없이 같은 방법으로 실행할 수 있다. new File()에는 "/Applications/TextEdit/TextEdit.app/Contents/MacOS/TextEdit"와 "/" 로 대체하고 args.push()부분은 foobar.txt가 데스크톱에 있을때 "/User/사용자계정/Desktop/foobar.txt"로 대체해서 실행하면 된다. 

NativeProcess 클래스는 단순히 실행과 종료만 담당하지 않는다. AIR 애플리케이션과 Native 실행 소스간에 통신도 가능하다. 다음 링크는 HTML/Javascript, Flex, Flash 개발자들을 위한 예제이다. AIR코드 뿐 아니라 C코드도 있으니 꼭 다운받아 실행해보길 바란다. 

다음과 같이 Flex 4 코드로 변경해봤다.

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/halo"
                       width="350" height="150"
                       applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import flash.events.ProgressEvent;
            import flash.system.Capabilities;
            import flash.utils.IDataInput;
             
            import mx.controls.Alert;
            import mx.events.FlexEvent;
             
            private var process:NativeProcess;
             
            private function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
                //Native Process가 지원되는지 확인.
                //지원되려면 반드시 디스크립터에  <supportedProfiles>extendedDesktop</supportedProfiles>을 추가해야한다.
                if( NativeProcess.isSupported ) {
                    launchEchoTest();
                } else {
                    Alert.show("NativeProcess not supported.","Error");
                }
            }          
            private function launchEchoTest():void {
                //실행할 Native 애플리케이션 선택
                var file:File = File.applicationDirectory;
                file = file.resolvePath("NativeApps");
                if( Capabilities.os.toLocaleLowerCase().indexOf("win") > -1 ) {
                    file = file.resolvePath("Windows/bin/echoTestWin.exe");
                } else {
                    file = file.resolvePath("Mac/bin/echoTestMac");
                }
                 
                //Native Process를 진행하기 위한 정보 설정
                var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                info.executable = file;
                 
                //Native Process생성
                process = new NativeProcess();
                 
                //보내고 받을때 발생하는 이벤트에 대한 처리
                process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, function(event:ProgressEvent):void {
                    var date:Date = new Date();
                    var bytes:IDataInput = process.standardOutput;
                    textReceived.text = bytes.readUTFBytes( bytes.bytesAvailable );
                    launchEchoTest();
                    lbDateStamp.text = date.toString();
                } );
                process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, function(event:ProgressEvent):void {
                    process.closeInput();
                } );
                //Native Process 시작
                process.start( info );
            }
            private function writeData():void {
                process.standardInput.writeUTFBytes( textToSend.text + "\n" );
            }
        ]]>
    </fx:Script>
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <s:VGroup paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10">
        <mx:Form>
            <mx:FormItem label="Text to send" direction="horizontal">
                <s:TextInput id="textToSend"/>   
                <s:Button label="writeData()" click="writeData()"/>
            </mx:FormItem>
            <mx:FormItem label="Text received">
                <s:TextInput id="textReceived" editable="false"/>
            </mx:FormItem>
        </mx:Form>
        <s:Label id="lbDateStamp"/>
    </s:VGroup>
</s:WindowedApplication>



아래 코드는 AIR 애플리케이션과 통신할 C코드(Mac용)이다. 

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>


#define BUFFER_SIZE 8192


int main(int argc, char** argv)
{
    char buf[BUFFER_SIZE];
    int  cnt;
     
    while ( !feof(stdin) )
    {
        cnt = read(STDIN_FILENO, buf, sizeof buf);
        if (-1 == cnt)
        {
            perror("read");
            exit(1);
        }
        if (0 == cnt)
        {
            // eof reached...
            exit(0);
        }


        write(STDOUT_FILENO, buf, cnt );
    }
     
    return 0;
}

 

 



NativeProcess의 standardInput과 standardOutput 속성은 각각 IDataOutput, IDataInput을 입력과 출력값으로 받고 있다. 그러므로 이 속성을 이용해 표준입출력(standard input/output)을 이용해 서로 통신하게 된다. 

매우 다양한 형태로 응용이 가능할 것이라 판단한다.


1.7 File Promise 지원 

AIR 2.0부터 FilePromise 클래스가 새로 추가되어 드래그&드롭 형태로 리모트 파일을 AIR 애플리케이션 외부로 다운로드 받을 수 있게 되었다. URLFilePromise 클래스를 이용해 이 작업을 할 수 있다. 

이 기능은 Windows와 Mac에서만 지원되며(Clipboard.supportsFilePromise로 확인) 반드시 AIR 애플리케이션 외부로 드롭하는 경우만 해당한다. 일반적인 클립보드 복사&붙이기 기능으로는 할 수 없다.

다음 코드는 예시이다.

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/halo"
                       applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.DragEvent;
            import mx.events.FlexEvent;
             
            private var clipboard:Clipboard;
            private function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
                clipboard = new Clipboard();
                if( !clipboard.supportsFilePromise ) {
                    Alert.show( "File Promise를 지원하지 않습니다." );
                }
            }
            private function onDragOut(event:DragEvent):void {
                trace( event.target, event.type );
                var items:Array = fileData.selectedItems;
                var promises:Array = new Array();
                for each (var item:Object in items) {
                    var filePromise:URLFilePromise = new URLFilePromise();
                    var request:URLRequest = new URLRequest(item.url);
                    filePromise.request = request;
                    filePromise.relativePath = item.name;
                    promises.push(filePromise);
                }
                clipboard.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises);
                NativeDragManager.doDrag(fileData, clipboard);    
            }
            private function onDragOutComplete(event:NativeDragEvent):void {
                trace( "Drag out complete");
                clipboard = new Clipboard();
            }           
        ]]>
    </fx:Script>
     
    <fx:Declarations>
        <mx:ArrayCollection id="arrColl">
            <mx:source>
                <fx:Array>
                    <fx:Object name="rhall.jpg" url="http://a1.twimg.com/profile_images/57117466/robert_m_hall_bio_photo_big_normal.jpg" />
                    <fx:Object name="bobjim.jpg" url="http://a1.twimg.com/profile_images/51723308/ryancampbell3_normal.jpg"/>
                    <fx:Object name="jenschr.jpg" url="http://a1.twimg.com/profile_images/43222252/jenschr_mugshot3_normal.jpg"/>
                    <fx:Object name="adamflater.jpg" url="http://a1.twimg.com/profile_images/21503622/Photo_8_normal.jpg"/>
                    <fx:Object name="reboog711.jpg" url="http://a1.twimg.com/profile_images/16984682/DSCF0044_normal.jpg"/>
                </fx:Array>
            </mx:source>
        </mx:ArrayCollection>
    </fx:Declarations>
     
    <mx:DataGrid id="fileData" dragEnabled="true"
                 dataProvider="{arrColl}"
                 dragStart="onDragOut(event)"
                 nativeDragComplete="onDragOutComplete(event)" width="100%">
        <mx:columns>
            <mx:DataGridColumn dataField="name" width="100"/>
            <mx:DataGridColumn dataField="url"/>
        </mx:columns>
    </mx:DataGrid>
     
</s:WindowedApplication>



 



1.8 Global Error Handling 

AIR 애플리케이션이 런타임 도중에 에러가 발생할 때 국부적 Error 처리는 이미 가능했다. 하지만 개발자가 애플리케이션의 어느 부분에서 Error가 발생할지 또는 문제가 발생할지 찾기 힘든 것이 현실이다. 국부적으로 일어나는 에러를 처지하지 못하는 경우 애플리케이션이 동작중에 어떤 부분에서 에러가 발생하든지 대처할 수 있도록 Global Error를 처리할 수 있는 기능이 AIR 2.0과 Flash Player 10.1에 추가가 되었다. 다음 글을 참고하자.

Global Error를 처리하는데 UncaughtErrorEvent 클래스를 이용한다. 이름에서도 볼 수 있듯이 기존의 try..catch문으로 처리하지 못한 Error가 발생할 때 발생함을 알 수 있다. 

발생하는 UncaughtErrorEvent를 처리하기 위해 LoaderInfo 객체의 uncaughtErrorEvents 속성에 이벤트 처리를 위한 함수를 등록하면 된다. 

loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, handler );


단, 로드된 SWF 객체의 경우에는 Loader의 uncaughtErrorEvents 속성에 이벤트 처리 함수를 등록한다.

loader.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, handler );


하나의 SWF가 다른 SWF에 로드되는 경우 UncaughtErrorEvent 이벤트 전파에 대한 처리가 필요하다.  A.swf가 B.swf를 Loader를 이용해 로드했다면 다음과 같은 순서로 LoaderInfo와 Loader 객체에 UncaughtErrorEvent 가 전파된다.

1. (Capture phase) A.swf의 LoaderInfo
2. (Capture phase) A.swf내의 Loader 
3. (Target phase) B.swf의 LoaderInfo
4. (Bubble phase) A.swf의 Loader
5. (Bubble phase) A.swf의 LoaderInfo


Loader객체의 uncaughtErrorEvents 속성은 결코 이벤트 전파시 target phase가 발생하지 않음에 주목하자. 그리고 이렇게 이벤트가 전파되는데는 디스플레이 리스트에 등록되어 있어야만 한다. 즉 addChild()된 객체어야한 한다. 중간에 이벤트 전파를 중단하려면 stopPropagation()이나 stopImmediatePropagation() 메소드를 이용하면 되겠다. 또한 ADL 기반나 디버그용 Flash Player에서 구동 경우 에러표시 다이얼로그가 뜨는 것을 방지하기 위해 UncaughtErrorEvent 이벤트 발생시 preventDefault() 메소드를 사용해 안뜨도록 할 수 있다.

아래는 AIR 2.0 예제이다. 

package {
    import flash.desktop.*;
    import flash.display.*;
    import flash.events.*;
    public class UncaughtErrorEventExample extends Sprite {
        public function UncaughtErrorEventExample()
        {
            var options:NativeWindowInitOptions = new NativeWindowInitOptions();
            options.type = NativeWindowType.UTILITY;
             
            var window:NativeWindow = new NativeWindow(options);
            window.width = 200;
            window.height = 200;
            window.title = "UncaughtErrorEventExample";
             
            window.stage.scaleMode = StageScaleMode.NO_SCALE;
            window.stage.align = StageAlign.TOP_LEFT;
             
            window.activate();
            window.addEventListener(Event.CLOSING, function($event:Event):void {
                NativeApplication.nativeApplication.exit();
            });        


            var btn:Sprite = new Sprite();
            btn.useHandCursor = true;
            btn.buttonMode = true;
            btn.graphics.clear();
            btn.graphics.beginFill(0xFFCC00);
            btn.graphics.drawRect(0, 0, 100, 50);
            btn.graphics.endFill();
            btn.addEventListener(MouseEvent.CLICK, clickHandler);          
            window.stage.addChild(btn);
             
            loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
        }
        private function uncaughtErrorHandler(e:UncaughtErrorEvent):void {
            if (e.error is Error) {
                var error:Error = e.error as Error;
                // do something with the error
                trace(error.errorID, error.name, error.message);
            } else if (e.error is ErrorEvent) {
                    var errorEvent:ErrorEvent = e.error as ErrorEvent;
                    // do something with the error
                    trace(errorEvent.errorID);
            } else {
                // a non-Error, non-ErrorEvent type was thrown and uncaught
                trace( " a non-Error, non-ErrorEvent type was thrown and uncaught " );
            }
            e.preventDefault();
        }
        private function clickHandler(event:MouseEvent):void
        {
            throw new Error("Gak!");
        }
    }
}

 

 


위 예제에서 볼 수 있듯이 AIR 애플리케이션의 노란 부분을 클릭하면 강제로 에러를 발생시킨다. 이때 이 에러를 처리하기 위한 try...catch문이 없으므로 UncaughtErrorEvent가 발생하고 그것을 처리하고 있다. 마지막에 e.preventDefault()를 둬서 에러표시 다이얼로그가 뜨는 것을 방지한다. 


1.9 Packaging an AIR application in a native installer

AIR 2.0가 생소하게 다가오는 이유중에 하나는 바로 AIR 2.0을 설치할 때 과정이다. 뱃지(Badge)기능을 이용해 웹페이지에서 AIR 런타임 및 해당 AIR 애플리케이션을 다운로드 받을 수 있지만 그 과정이 일반 사용자에게 생소하게 다가올 수 있다. AIR 2.0에서는 이러한 생소함을 덜어주기 위해 각 OS별로 설치패키지를 따로 구성할 수 있도록 지원하고 있다. 가령, Windows는 exe, Mac은 dmg으로 설치 패키지를 구성할 수 있다는 말이다. 

AIR 2.0의 ADT의 옵션설정으로 설치 패키지를 만들 수 있다. 다음 링크를 참고한다.


연관글





정리하며 

이번 글은 Adobe AIR 2.0 Overview 첫번째 글로 AIR 2.0에 추가된 새로운 기능에 대해서만 언급했다.  "기존 API에서 추가된 기능", "플랫폼 인식 관련 API", "퍼포먼스 향상"에 관련된 내용도 조만간 언급하겠다. 이 글을 읽고 AIR 2.0 기반으로 하는 다양한 애플리케이션 예제들이 많이 나왔으면 하는 바램이다. 

글쓴이 : 지돌스타(http://blog.jidolstar.com/629)

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://tibyte.kr/190

 

플래시 클라이언트와 소켓서버의 연결 ( <policy-file-request/> 문제 )

게시판/▶ Flash/AS3.0 2014/01/08 00:40

http://tibyte.kr/190 

 

이 글은 tibyte.kr과 cafe.naver.com/sdbx에 게시됩니다.

(설명글은 http://cafe.naver.com/sdbx/737에 있습니다.) 

 

플래시 클라이언트는 소켓서버에 연결할 때 정책파일(crossdomain.xml)을 달라는 요청을 보내는데 이 파일을 보내주지 않으면 통신을 시작할 수 없습니다.

클라이언트에서 Security.loadPolicyFile()메서드로 원격 서버와 같은 도메인에 연결하여 xml파일을 직접 받아올 수 있지만 그러지 못하는 상황에는 서버프로그램에서 이 정책파일을 직접 보내줄 수도 있습니다.

ActionScript 3.0으로 작성된 Adobe AIR 소켓서버 코드를 보겠습니다.

 

 

 

우선 ServerSocket객체를 만들고 bind()와 listen()을 수행하여 서버소켓을 가동시키는 함수입니다. (serverSocket은 이 클래스의 private멤버변수입니다.)

 

private function bind():void

{

//기존에 실행 중인 소켓서버가 있으면 닫고 새로 실행

if(serverSocket.bound)

{

serverSocket.close();

serverSocket = new ServerSocket();

}

serverSocket.bind(로컬포트로컬IP);

serverSocket.addEventListener(ServerSocketConnectEvent.CONNECT, onConnect);

serverSocket.listen();

 

//로그 코드 삽입

}

 

 

 

다음은 ServerSocketConnectEvent.CONNECT 이벤트가 발생했을 때  실행되는 onConnect함수입니다.

 

 

private function onConnect(event:ServerSocketConnectEvent):void

{

var socket:Socket = event.socket;

clientSockets[socket.remoteAddress] = 1;//연결된 클라이언트들을 배열로 관리

socket.addEventListener(Event.CLOSE, onClientDisconnect);//클라이언트가 접속을 끊었을 

socket.addEventListener(ProgressEvent.SOCKET_DATA, onClientSocketCert);//클라이언트로부터 데이터가 들어올 때

//이 밖에도 다른 이벤트나 예외처리구문로그(기록남기는 구문들을 넣어줍니다.

}

 

 

 

연결이 되면 클라이언트는 서버로 메시지를 보내고 이에 따라 ProgressEvent.SOCKET_DATA 이벤트가 발생합니다.

 

 

 클라이언트가 정책파일을 찾지 못했을 때는 843번 포트로 "<policy-file-request/>" 이라고 정책파일을 요청하는 메시지를 보냅니다따라서 서버에서는 저 메시지가 들어왔을 때 정책파일을 보내주어야 합니다.

 

 

 

private function onClientSocketCert(event:ProgressEvent):void

{

var socket:Socket = event.target as Socket;

var message:String = socket.readUTFBytes(socket.bytesAvailable);

if (message == "<policy-file-request/>") {

var policy:String = "<cross-domain-policy>" +

"<allow-access-from domain=\"*.example.com\" to-ports=\"8800\"/>" +

"</cross-domain-policy>\x00";

socket.writeUTFBytes(policy);

socket.flush();

socket.close();

} else if(message=="BEGIN") {

socket.removeEventListener(ProgressEvent.SOCKET_DATA, onClientSocketCert);

socket.addEventListener(ProgressEvent.SOCKET_DATA, onClientSocketData);

socket.writeUTFBytes("READY");

socket.flush();

      }

//이 밖에도 로그 남기는 코드를 삽입

}

 

위 함수에서 볼 수 있듯이 XML파일을 문자열형태로 직접 전송합니다.

 

"<cross-domain-policy>" +

"<allow-access-from domain=\"*.example.com\" to-ports=\"8800\"/>" +

"</cross-domain-policy>\x00"

 

(여기서 쓴 8800번 포트는 임의로 정한 포트번호입니다.)

 

크로스도메인(crossdomain.xml)파일에서 도메인과 포트를 지정해주는데요, 와일드카드 문자를 쓸 수 있습니다.

 

 

=================================

=================================

=================================

 

 

반응형