많은 분들(?)이 제 블로그에 오셔서 질문을 해주셔서 간단한 예제를 통한 설명을....-_- 나중에 저도 참고하려고 기록용-_-

일단, Flash에서 CrossDomain에 걸리는 데이터를 요청할 때 Plicy File인 crossdomain.xml 파일을 root에 정의해둬서 해당 도메인이면 데이터를 허용하게 할 수 있습니다.

소켓도 마찬가지입니다. 해당 도메인에서 해당 포트로 들어온 요청은 받겠다는 정책파일을 작성할 수 있습니다.
최초 소켓이 정책파일을 요청하게 되는 포트는 843포트입니다. 만약 이포트가 열러있지 않다면 현재 연결하려고 하는 포트로 <policy-file-request/>를 날려서 정책파일을 요청하게 됩니다.
그러면 843이든, 해당포트든 간에 정책파일만 날려주면 됩니다.

[code]<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*" to-ports="10000-10001" />
</cross-domain-policy>[/code]
소켓에 대한 정책파일을 정의한 것인데, domain에는 허용할 도메인을 쓰고, to-ports에는 허용할 포트를 쓰면 됩니다.
이걸 날려주면 이제 연결할 포트로부터 데이터를 주고 받을 수 있습니다.

초간단 에코예제!
일단 서버는 자바로...(그나마 자신있는 언어라서ㅠㅠ)

일단 PlicyFileServer를 하나 돌릴 쓰레드를 만듭니다.
PlicyFileServer.java
[code]import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class PolicyFileServer extends Thread{
   
    private ServerSocket serverSocket;
    private String policyFile = "<?xml version='1.0'?>" +
                                "<!DOCTYPE cross-domain-policy SYSTEM '/xml/dtds/cross-domain-policy.dtd'>" +
                                "<cross-domain-policy>" +
                                "<allow-access-from domain='*' to-ports='10000' />" +
                                "</cross-domain-policy>";
   
    @Override
    public void run() {
        try {
            serverSocket = new ServerSocket(843);
            while (true) {
                final Socket socket = serverSocket.accept();
                new Runnable() {
                    @Override
                    public void run() {
                        try {
                            socket.setSoTimeout(10000);
                            InputStream in = socket.getInputStream();
                            byte[] buffer = new byte[23];
                            if ( in.read(buffer) != -1 && (new String(buffer)).startsWith("<policy-file-request/>") ) {
                                OutputStream out = socket.getOutputStream();
                                out.write(policyFile.getBytes());
                                out.write(0x00);
                                out.flush();
                                out.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            try { socket.close();} catch(Exception ex){}
                        }
                    }
                }.run();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
[/code]
내용을 보면 그냥 서버소켓하나 만들어서 요청이 들어오면 그 소켓으로 policy file을 전송하는 형태입니다. 파일은 만들기 귀찮아서-_- 그냥 String으로 선언-_- 보면 모든도메인에 한해서 10000포트를 열어주는 겁니다.

이제 메인서버!
SocketTest.java
[code]import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class SocketTest {

    private ServerSocket server;
   
    public SocketTest() {
        try{
            server = new ServerSocket(10000);
            System.out.println("접속을 기다립니다.");
           
            while (true){
                final Socket socket = server.accept();
                Thread t = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
                            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                            String line = null;
                            while ((line = br.readLine()) != null) {
                                System.out.println("수신데이터 : " + line);
                                pw.println("Hello! " + line);
                                pw.flush();
                            }
                        } catch (Exception e) {
                            try { if(socket != null) socket.close(); } catch (Exception ex) {}
                        }
                    }
                });
                t.start();
               
            }
        } catch(Exception e){
            System.out.println("Error!");
        }
    }
   
    public static void main(String[] args) {
        new PolicyFileServer().start();
        new SocketTest();
    }
}[/code]
데이터를 받으면 다시 Hello!를 붙여서 다시 전송해주는 echo서버를 하나 만듭니다.
그리고 main함수에서는 PolicyFileServer쓰레드를 하나 시작하고, EchoServer를 돌립니다.

이제 Flex!
[code]<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
               applicationComplete="application1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
           
            import spark.components.mediaClasses.VolumeBar;
           
            private var socket:Socket;
           
            protected function application1_applicationCompleteHandler(event:FlexEvent):void
            {
                socket = new Socket("127.0.0.1", 10000);
                socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
                socket.addEventListener(Event.CONNECT, connectHandler);
            }

            protected function btnSend_clickHandler(event:MouseEvent):void
            {
                // TODO Auto-generated method stub
                socket.writeUTFBytes(inputMessage.text + "\n");
                socket.flush();
            }
           
            private function connectHandler(event:Event):void
            {
                trace("접속완료!");   
                hbox.visible = true;
            }
           
            private function socketDataHandler(event:ProgressEvent):void
            {
                var message:String = socket.readUTFBytes(socket.bytesAvailable);
                trace("수신메세지 : " + message);
                textResult.text = message;
            }

        ]]>
    </fx:Script>
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
   
    <s:layout>
        <s:VerticalLayout />
    </s:layout>
   
    <mx:HBox id="hbox" visible="false" width="100%" horizontalAlign="center">
        <s:TextInput id="inputMessage" />
        <s:Button id="btnSend" label="송신하기" click="btnSend_clickHandler(event)"/>
    </mx:HBox>
   
    <mx:Text id="textResult" width="100%" textAlign="center"/>
</s:Application>
[/code]
Socket만들어서 10000포트로 연결합니다. 그러면 도메인이 다르게 되면 swf가 843포트로 "<policy-file-request/>"를 날려서 정책파일을 달라고하는데, 서버에서 만들어놓은 PolicyFileServer가 정책파일을 내려주면 받게되면 10000포트로 다시 연결해 연결을 시작하게 됩니다.

몬가 별거 없는데 장황하게 설명해놨네.

그리고, 이런식으로 PolicyFileServer를 서버어플에 통합하면 안되겠죠? 나중에 서버어플이 늘어난다면 계속 새로 추가해야하니, PolicyfileServer를 따로 만들어서 띄워놓으면 되겠죠?^^
일단, 여러 폴리시서버 예제는 구글링하면 많이 나와요~
여기 아래주소는 Java, PHP, C#, VB.NET, Python 등등 예제가 있어요.
http://code.google.com/p/assql/wiki/SecurityInformation
이건 c로 만든거!
http://panzergruppe.hp.infoseek.co.jp/fspfd.html
 
Posted by 머드초보

댓글을 달아 주세요

 
air어플을 만들어야할 것 같아서(근데 이제 취소된 듯-_-), ApplicationUpdater를 삽질해봤습니다.

일단 air 어플리케이션은 수정사항이 있으면 자동적으로 업데이트를 체크해서 어플을 업데이트하는 기능이 꼭 들어가야합니다. 그래서 ApplicationUpdater클래스를 이용해 삽질을 해봤습니다.

보면 ApplicationUpdaterUI라는 클래스도 있는데, 이건 ui적으로 기능이 이미 구현이 되어있는 것입니다. 사용자가 이런 ui를 새롭게 꾸미려면 ApplicationUpdater클래스를 이용해서 내부적으로 처리를 하고, 나머지 ui는 직접 꾸밀 수가 있습니다. 그래서 사용자가 직접 새로운 업데이트air파일을 받는 모습과 업데이트체크를 해서 현재 업데이트가 있는지 등을 구현할 수 있습니다.

사실 과정은 applicationUpdater.checkNow()한번 때려버리면 업데이트가 있으면, 논스톱으로 강제 업데이트를 시켜버리긴합니다만-_- 강제업데이트말고, 사용자에게 업데이트 과정을 알려주기 위해서 과정을 삽질해봤습니다.

강제 업데이트를 중지하려면 각각 프로세스별로 event 중지를 시켜야합니다.

[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="vertical" 
    title="Sample Version Checker Application"
    applicationComplete="applicationCompleteHandler()">
    <mx:Script>
        <![CDATA[
            import air.update.events.DownloadErrorEvent;
            import air.update.events.StatusUpdateErrorEvent;
            import air.update.events.StatusUpdateEvent;
            import air.update.events.UpdateEvent;
            import air.update.ApplicationUpdater;
             
            private var applicationUpdater:ApplicationUpdater;
            
            private function applicationCompleteHandler():void
            {
                // 현재버전 찍기
                   var appXml:XML = NativeApplication.nativeApplication.applicationDescriptor;
                var ns:Namespace = appXml.namespace();
                var appVersion:String = appXml.ns::version[0];
                log.text += "현재 어플리케이션버전 : " + appVersion + "\n";
               
                // ApplicationUpdater초기화
                applicationUpdater = new ApplicationUpdater();
                applicationUpdater.configurationFile = new File("app:/update.xml");
               
                // initialize할 때
                applicationUpdater.addEventListener(UpdateEvent.INITIALIZED, initializedUpdateEventHandler);
                applicationUpdater.addEventListener(ErrorEvent.ERROR, errorErrorEventHandler);
               
                // checkNow할 때
                applicationUpdater.addEventListener(StatusUpdateEvent.UPDATE_STATUS, updateStatusStatusUpdateEventHandler);
                applicationUpdater.addEventListener(UpdateEvent.CHECK_FOR_UPDATE, checkForUpdateUpdateEventHandler);
                applicationUpdater.addEventListener(StatusUpdateErrorEvent.UPDATE_ERROR, updateErrorStatusUpdateEventHandler);
               
                // downloadUpdate할 때               
                applicationUpdater.addEventListener(UpdateEvent.DOWNLOAD_START, downloadStartUpdateEventHandler);
                applicationUpdater.addEventListener(ProgressEvent.PROGRESS, progressProgressEventHandler);
                applicationUpdater.addEventListener(UpdateEvent.DOWNLOAD_COMPLETE, downloadCompleteUpdateEventHandler);
                applicationUpdater.addEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, downloadErrorDownloadErrorEventHandler);
               
                // installUpdate할 때
                applicationUpdater.addEventListener(UpdateEvent.BEFORE_INSTALL, beforeInstallUpdateEventHandler);
               
                // 초기화
                applicationUpdater.initialize();
            }
           
            private function initializedUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "initialized : " + applicationUpdater.currentState +"\n";
                applicationUpdater.checkNow();
            }
           
            private function errorErrorEventHandler(event:ErrorEvent):void
            {
                log.text += "error : " + applicationUpdater.currentState + "\n";
            }
           
            private function updateStatusStatusUpdateEventHandler(event:StatusUpdateEvent):void
            {
                event.preventDefault();
                log.text += "update_status : " + applicationUpdater.currentState +"\n";
                log.text += "version : " + event.version + "\n";
                log.text += "available : " + event.available + "\n";
               
                if (event.available)
                {
                    applicationUpdater.downloadUpdate();
                }
            }
           
            private function updateErrorStatusUpdateEventHandler(event:StatusUpdateEvent):void
            {
                log.text += "update_error : " + applicationUpdater.currentState + "\n";
            }
           
            private function checkForUpdateUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "check_for_update : " + applicationUpdater.currentState + "\n";
            }
           
            private function downloadStartUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "download_start : " + applicationUpdater.currentState + "\n";
            }
           
            private function progressProgressEventHandler(event:ProgressEvent):void
            {
                log.text += "progress : " + event.bytesLoaded + "/" + event.bytesTotal + "\n";
            }
           
            private function downloadCompleteUpdateEventHandler(event:UpdateEvent):void
            {
                event.preventDefault();
                log.text += "download_complete : " + applicationUpdater.currentState + "\n";
                btnInstall.visible = true;   
            }
           
            private function downloadErrorDownloadErrorEventHandler(event:DownloadErrorEvent):void
            {
                log.text += "download_error : " + applicationUpdater.currentState + "\n";   
            }
           
            private function beforeInstallUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "before_install : " + applicationUpdater.currentState + "\n";
            }
           
            private function clickInstallHandler(event:MouseEvent):void
            {
                applicationUpdater.installUpdate();
            }
        ]]>
    </mx:Script>
    <mx:TextArea id="log" width="100%" height="100%" />
    <mx:Button id="btnInstall" click="clickInstallHandler(event)" visible="false" label="설치"/>
</mx:WindowedApplication>
[/code]

air안에 있는 update.xml파일
[code]<?xml version="1.0" encoding="utf-8"?>
 <configuration xmlns="http://ns.adobe.com/air/framework/update/configuration/1.0" >
   <url>웹에있는update.xml파일 주소</url>
   <delay>1</delay>
</configuration>[/code]

web에 올려진 update.xml파일
[code]<?xml version="1.0" encoding="utf-8"?>
<update xmlns="http://ns.adobe.com/air/framework/update/description/1.0">
  <version>1.5</version>
  <url>업데이트할 air파일 주소</url>
  <description>
      <![CDATA[update! 1.5!]]>
  </description>
</update>
[/code]

과정은 initialize()한다음에 checkNow()를 호출하면....
현재 업데이트가 있는지 update_status가 발생하면, 그 event변수에 version과 available값이 오는데, 거기에 available이 true면 업데이트가 있는 겁니다.
downloadUpdate()를 호출하면 파일을 막 다운로드하는데, 파일 받는 과정을 progressevent로 받을 수 있습니다.
완료가 되면 downloadComplete이벤트가 발생해서 installUpdate()를 때려주면 어플이 종료되고 업데이트를 시작합니다.

사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지

PS. 오늘따라....시간이 엄청 안가네-_-
 
Posted by 머드초보

댓글을 달아 주세요

  1. 달빛화가 2010.11.10 15:27 신고  댓글주소  수정/삭제  댓글쓰기

    제가 머드초보의 블로그 내용을 가지고 ApplicationUpdater를
    그대로 이용해보았는데요.

    이상하게도 전 16803 에러가 발생하네요.
    아무리 뒤져봐도 16803에 대한 내용이 없어서 질문을 드립니다.
    혹시 왜 16803 에러가 발생하는지 알고 계시나요.?

    • 머드초보 2010.12.05 20:20 신고  댓글주소  수정/삭제

      음..... 파일이 없거나 그러진 않는지?ㅠㅠ
      저도 그 에러에 대해선 잘 모르겠네요

  2. BlogIcon 박경석 2012.07.20 13:25 신고  댓글주소  수정/삭제  댓글쓰기

    "6803 잘못된 Adobe AIR 파일입니다(형식)"
    레퍼런스에 이렇게 나와 있는걸로 봐서 파일을 배포하실때 혹시 exe로 배포하신게 아닌지 생각됩니다. 업데이트 하려면 확장자를 air로 해서 배포해야 ApplicationUpdater를 사용할수 있다고 합니다.

 
최근 스티브잡스횽이 까고 있는 플래시-_-

CS5 한글판이 나와서 한번 깔아봤는데, 역시 전 한글이 편하네요-_- 어설픈 한글로 되어있어도 그냥 영어로 된 것 보단 낫다고 생각하는 1人...

일단 Flash랑 Flash Builder를 둘 다 깔아야해요~
안깔면 안깔려있다고 뭐라그래요~

일단 Flash에서 액션스크립트 편집기가 좋아졌다고 하는데(전 잘 안써봐서-_-), 여전히 안좋은 것 같은 느낌이 드는....
액션스크립트 클래스 편집은 그냥 Flash Builder에서 하면 맘 편하네요~
둘이서 연동이 되어서 빌더에서 에러 수정 및 편집이 매우 용이해요~

일단 플래시화면!
사용자 삽입 이미지
일단 Flash Builder도 같이 띄워놓아야 합니다. 안그러면 선택하라는 거 안나오는 것 같음.
여기서 ActionScript3프로젝트로 새로 만든다음에, ActionScript 3.0 클래스를 새로 만들면
"어떤 응용 프로그램에서 ActionScript3.0 클래스를 만들어야 합니까?"라는 말로 물어보고, Flash Professional인지 Flash Builder인지 선택할 수 있어요.
그러면 Flash Builder에서 해당 Flash프로젝트 파일인 fla파일을 선택하면 되는데요. 그러면 자동으로 프로젝트도 만들고, ActionScript Class만드는 창도 떠서 만들어지게 됩니다.
[code]package
{
    import flash.display.Sprite;

    public class DrawRect extends Sprite
    {
        public function DrawRect()
        {
            this.graphics.beginFill(0x000000);
            this.graphics.drawRect(0, 0, 100, 100);
            this.graphics.endFill();
        }
    }
}[/code]
사각형을 그리고~ flash에서 불러올 때 자동으로 DrawRect라는 클래스가 코드힌트로 나옴!
사용자 삽입 이미지

[code]var drawRect:DrawRect = new DrawRect();
this.addChild(drawRect);[/code]

사용자 삽입 이미지
잼있는 건 Flash Builder에서도 Control + Enter눌러도 된다는......
일단....여전이 Flash에서 ActionScript편집은 불편합니다. 그래서 얘네들이 Flash Builder 연동을 해서 하는 것 같은데, 정말 괜찮은 기능인 듯 합니다.
 
Posted by 머드초보

댓글을 달아 주세요

  1. BlogIcon 길땡 2010.05.18 00:03 신고  댓글주소  수정/삭제  댓글쓰기

    플렉스 그래프가 이쁜것 같아서. . .

    DB의 값을 받아와서 그래프로 만들어주려고 했는데. . .

    아직 그렇다할 자료가 ㅋㅋㅋㅋㅋ ㅠㅠㅠㅠㅠ

    그래도 여기와서 많은 정보 얻고 갑니다. . .ㅎㅎ

    • 머드초보 2010.05.21 10:44 신고  댓글주소  수정/삭제

      아...일단 직접 다 구현하기 힘드시면 flex에서 제공하는 많은 차트가 있거든요. 그걸 사용하시는게^^
      이쁘고 좋습니다^^

  2. BlogIcon 성주 2010.06.16 12:43 신고  댓글주소  수정/삭제  댓글쓰기

    저는 왜 FlashBuilder에서 Ctrl+Enter하면 줄바꿈이 될까요...

  3. 2012.10.30 00:40  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

 
ServerSocket이라는 것이 Adobe AIR 2에서 생겼는데요.
뭐 특별히 제약사항같은 것은 없는 것 같군요. 그냥 서버소켓 만들어서 쓰면 됩니다.

우선 Adobe AIR 2 셋팅Flash Builder를 다운로드

서버소켓 생성하는 방법은
new ServerSocket();
serverSocket.bind(포트, "아이피");
serverSocket.listen();
하면 서버소켓이 생성됩니다.

Event.Connect를 이벤트 추가하면 상대방이 연결해왔을 때 호출이 됩니다.
보통 Java에서는 Thread를 통해서 하게 되는데, Actionscript3는 특성상 이벤트기반이기에... 그냥 여러개가 연결이 되도 이벤트가 발생하고, 그 발생한 이벤트객체에서 socket이 들어있어서 그걸 이용하면 되구요.

그 소켓에 다시 이벤트를 걸어주면 됩니다. 그 소켓은 client와 연결된 소켓! 기존 client소켓처럼 쓰면 됩니다^^
데이터는 read, write로 주고 받으면 되죠.

아래는 간단한 예제를....-_-
ServerSocketTest.mxml
[code]<?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="windowedapplication_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import flash.events.Event;
            import flash.events.IOErrorEvent;
            import flash.events.ProgressEvent;
            import flash.events.ServerSocketConnectEvent;
            import flash.net.ServerSocket;
            import flash.net.Socket;
           
            import mx.events.FlexEvent;
           
            private var socketPolicyUtil:SocketPolicyUtil;
           
            private var serverSocket:ServerSocket;
            private var listSocket:Vector.<Socket>;
           
            protected function windowedapplication_applicationCompleteHandler(event:FlexEvent):void
            {
                socketPolicyUtil = new SocketPolicyUtil();
               
                // 소켓리스트 초기화
                listSocket = new Vector.<Socket>;
               
                serverSocket = new ServerSocket();
               
                serverSocket.addEventListener(Event.CONNECT, connectHandler);
                serverSocket.addEventListener(Event.CLOSE, onClose);
       
                serverSocket.bind(10000, "127.0.0.1");
               
                serverSocket.listen();
                trace("Listening on " + serverSocket.port);
            }

            private function connectHandler(event:ServerSocketConnectEvent):void
            {
                // The socket is provided by the event object
                var socket:Socket = event.socket as Socket;
                listSocket.push(socket);
               
                socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
                socket.addEventListener(Event.CLOSE, onClientClose);
                socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
               
                socket.writeUTFBytes("Connected.");
                socket.flush();
               
                trace("Sending connect message");
            }
           
            private function socketDataHandler(event:ProgressEvent):void
            {
                var socket:Socket = event.target as Socket;
               
                // Read the message from the socket
                var message:String = socket.readUTFBytes(socket.bytesAvailable);
                trace("Received: " + message);
               
                // 등록된 소켓에 모두 전송
                for each (var s:Socket in listSocket)
                {
                    s.writeUTFBytes(message);
                    s.flush();
                }
            }
           
            private function onClientClose(event:Event):void
            {
                trace("Connection to client closed");
                var socket:Socket = event.target as Socket;
               
                // 등록된 소켓 삭제
                var i:int = 0;
                for each(var s:Socket in listSocket)
                {
                    if (s == socket)
                    {
                        trace("같음");
                        listSocket.splice(i, 1);
                        return;
                    }
                    i++;
                }
            }
           
            private function onIOError(event:IOErrorEvent):void
            {
                trace("IOError: " + event.text);   
            }
           
            private function onClose(event:Event):void
            {
                trace("Server socket closed by OS.");       
            }
        ]]>
    </fx:Script>
   
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
</s:WindowedApplication>
[/code]
그냥 코드만 봐도 그리 어렵지 않아요.
서버소켓을 생성하고, 연결되는 소켓들은 list(Vector)에 추가해서 관리하게 되고, 끊어지면 list에서 빼버리고, 대화요청이 들어오면 받아서 모두에게 뿌려주면 돼요.

여기서 제가 만든 클래스가 하나 있는데요. SocketPolicyUtil클래스인데, 크로스 도메인을 위한 클래스입니다. 아래에서 설명을....-_-


크로스 도메인 설정 클래스
여기서 제가 보안샌드박스를 위한 클래스를 하나 만들었는데요. 크로스도메인에서 socket요청이 들어온 경우에는 crossdomain.xml을 넘겨줘야하는데, 그것도 air에서 다 할 수 있습니다.
타 도메인에서 요청하는 경우, 클라이언트 측에서는 843포트를 통해 crossdomain.xml을 요청하게 되어있습니다. 물론 클라이언트에서 843이 아닌 다른 포트로 요청을 원하면 바꿀 수 있지요.
 클라이언트 측에 이런 코드를 넣으면 되죠. 그러면 10001로 포트를 요청하죠.
[code]Security.loadPolicyFile("xmlsocket://127.0.0.1:10001");[/code]
SocketPolicyUtil.as
[code]package
{
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.ServerSocketConnectEvent;
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.net.ServerSocket;
    import flash.net.Socket;

    public class SocketPolicyUtil
    {
        private var serverSocket:ServerSocket;
       
        public function SocketPolicyUtil(port:int = 843)
        {
            serverSocket = new ServerSocket();
            serverSocket.bind(port, "127.0.0.1");
            serverSocket.listen();
            serverSocket.addEventListener(Event.CONNECT, connectHandler);
            serverSocket.addEventListener(Event.CLOSE, onClose);
        }
       
        private function connectHandler(event:ServerSocketConnectEvent):void
        {
            var socket:Socket = event.socket as Socket;
           
            socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
            socket.addEventListener(Event.CLOSE, onClientClose);
            socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
           
            trace("Policy Connected.");
        }
       
        private function socketDataHandler(event:ProgressEvent):void
        {
            var socket:Socket = event.target as Socket;
           
            var message:String = socket.readUTFBytes(socket.bytesAvailable);
            trace("Policy received : " + message);
           
            var file:File = new File(File.applicationDirectory.nativePath + File.separator + "policy.xml");
            var stream:FileStream = new FileStream();
            stream.open(file, FileMode.READ);
            var data:String = stream.readUTFBytes(stream.bytesAvailable);
            trace("policy data = " + data);
            socket.writeUTFBytes(data);
            socket.writeByte(0);
            socket.flush();
        }
       
        private function onClientClose(event:Event):void
        {
            trace("Policy close");
            removeClientSocketEvent(event.target as Socket);
        }
       
        private function onIOError(event:IOErrorEvent):void
        {
            trace("ioerror = " + event.text);   
        }
       
        private function onClose(event:Event):void
        {
            trace("Server socket closed by OS.");
        }
       
        private function removeClientSocketEvent(socket:Socket):void
        {
            socket.removeEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
            socket.removeEventListener(Event.CLOSE, onClientClose);
            socket.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);   
        }
    }
}[/code]
중요한 점은 policy.xml파일 날릴 때 마지막에 writeByte(0)으로 끝을 맺어줘야한다는...
이 클래스를 보면 policy.xml파일은 File.applicationDirectory에서 찾고 있으니 이걸 src폴더에다가 넣어버리고 패키지할 때 같이 묶어버리면 됩니다. 그리고 그냥 843포트로 열어서 crossdomain.xml파일을 그냥 전송해주기만 하면 되죠. *는 권장사항이 아니니...-_- 해당 도메인이랑 포트번호를 정확히 입력하는게...-_-
policy.xml
[code]<?xml version='1.0' encoding='UTF-8'?>
<cross-domain-policy>
    <allow-access-from domain='*' to-ports='*'/>
</cross-domain-policy>[/code]

아래는 클라이언트 코드
ClientSocketTest.mxml
[code]<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:s="library://ns.adobe.com/flex/spark"
               xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768"
               applicationComplete="application1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.FlexEvent;

            private var socket:Socket;
           
            protected function application1_applicationCompleteHandler(event:FlexEvent):void
            {
                //Security.loadPolicyFile("xmlsocket://127.0.0.1:10001");
            }

            protected function btnDisconnect_clickHandler(event:MouseEvent):void
            {
                if (inputId.text.length < 1)
                {
                    Alert.show("아이디를 입력하세요");
                    return;
                }
                socket = new Socket("127.0.0.1", 10000);
                socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
                currentState = "connect";
                labelId.text = inputId.text;
            }
           
            protected function btnConenct_clickHandler(event:MouseEvent):void
            {
                socket.close();
                currentState = "disconnect";
            }
           
           
            private function socketDataHandler(event:ProgressEvent):void
            {
                var message:String = socket.readUTFBytes(socket.bytesAvailable);
                trace("message = " + message);
                taChat.text += message + "\n";
            }

            protected function inputChat_enterHandler(event:FlexEvent):void
            {
                if (inputChat.text.length < 1)
                {
                    return;
                }
                socket.writeUTFBytes("[" + labelId.text + "] : " + inputChat.text);
                socket.flush();
                inputChat.text = "";
            }

            protected function btnChat_clickHandler(event:MouseEvent):void
            {
                if (inputChat.text.length < 1)
                {
                    return;
                }
                socket.writeUTFBytes("[" + labelId.text + "] : " + inputChat.text);
                socket.flush();
                inputChat.text = "";
            }
        ]]>
    </fx:Script>
    <s:states>
        <s:State name="disconnect"/>
        <s:State name="connect"/>
    </s:states>
   
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
   
    <s:TextInput id="inputId" x="231" y="10" includeIn="disconnect"/>
    <s:Button id="btnConenctClose" x="367" y="11" label="접속" label.connect="종료"
              click.connect="btnConenct_clickHandler(event)" click.disconnect="btnDisconnect_clickHandler(event)"/>
    <s:Label id="labelId" includeIn="connect" x="211" y="10" width="159" height="21"/>
   
    <s:TextArea id="taChat" includeIn="connect" x="10" y="49" width="621" height="402"/>
    <s:TextInput id="inputChat" includeIn="connect" x="10" y="459" width="507" enter="inputChat_enterHandler(event)"/>
    <s:Button id="btnChat" includeIn="connect" x="526" y="459" label="Button" click="btnChat_clickHandler(event)"/>
</s:Application>[/code]

아래 프로그램 설치하고 실행시켜놓은 뒤, 아래사이트를 접속해서 테스트할 수 있어요. 아이피는 127.0.0.1로 하드코딩되어있기 때문에 자기 pc에서 띄워놓고 자기가 접속한 클라이언트에서만 가능해요^^
클라이언트 접속 http://mudchobo.tomeii.com/swf/ClientSocketTest.swf


 
Posted by 머드초보

댓글을 달아 주세요

  1. BlogIcon 지돌스타 2009.11.26 02:08 신고  댓글주소  수정/삭제  댓글쓰기

    멋지고 좋은 글입니다. 잘보고 갑니다.

  2. 푸카 2009.11.29 07:37 신고  댓글주소  수정/삭제  댓글쓰기

    빠른 좋은글 감사합니다^^. 잘보고 갑니다.

  3. z 2010.02.18 15:55 신고  댓글주소  수정/삭제  댓글쓰기

    zzzzzzzzzzzzzzzzzzzzzz

  4. 싸부 2010.06.21 18:39 신고  댓글주소  수정/삭제  댓글쓰기

    이게 air로 서버소켓이 리눅스나 유닉스쪽에서도 가능해진건가요?

    • 머드초보 2010.06.26 15:27 신고  댓글주소  수정/삭제

      음....air에서 소켓은 무조건 gui가 있어야 하기 때문에....
      리눅스쪽에서 가능해지긴 합니다만, gui같은 게 있어야 합니다.
      그냥 air에서 서버가 필요한 이유는 뭐 p2p같은 게 가능하고, 더욱 다양한 어플개발이 가능하기 때문입니다^^

  5. 2010.11.03 18:56  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • 머드초보 2010.11.05 21:23 신고  댓글주소  수정/삭제

      음 air에서 그냥serversocket하나 열어놓으시고
      나머지 swf파일들로 거기에 접속하면 될 것 같은데요^^
      이거 Client에서 서버쪽 진짜 아이피로 접속하면 됩니다^^

  6. S 2010.12.02 17:49 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 글 잘 보고 갑니다 ^^

 
Adobe AIR 2에서는 제한적으로 프로세스를 실행할 수 있는 기능이 추가되었습니다. NativeProcess클래스인데요. 이 기능을 제공하는 이유는 그런 것 같습니다. AIR에서 안되는 거 C로 짠거랑 데이터 통신할 수 있는 게 주 목적인 듯.
Java처럼 그냥 exe파일 실행하는 것은 안되구요. 샌드박스에 위배되지 않는 파일만 실행할 수 있습니다.
즉, document폴더나 application폴더, 또는 applicationstorage 등과 같은 폴더에 있는 것은 자동으로 실행시킬 수 있지만, 그 외에 파일들은 사용자가 선택한 것만 실행할 수 있습니다. 즉, 열 수 있는 파일과 샌드박스안에 있는 파일만 실행할 수 있다는 얘기죠^^
안그럼 이런 에러가 뜨는 것 같습니다.
NativeProcessStartupInfo.executable does not specify a valid executable file.

뭐 사용자가 exe파일 선택만 해준다면야 모든 실행파일을 실행할 수 있네요.

우선 플래시 빌더를 다운로드를..ㅠㅠ
Flash Builder4 beta2 다운로드

아래 예제는 그냥 실행파일 선택하면 그 실행파일을 실행해주는 예제입니다.
[code]<?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.Event;
            import flash.events.MouseEvent;
            import flash.net.FileFilter;
           
            import mx.controls.Alert;
           
            private var nativeProcess:NativeProcess;
           
            protected function btnRun_clickHandler(event:MouseEvent):void
            {
                if (NativeProcess.isSupported)
                {
                    var file:File = new File();
                    file.addEventListener(Event.SELECT, selectHandler);
                    file.browseForOpen("실행파일을 선택하세요", [new FileFilter("exe파일", "*.exe")]);
                }
                else
                {
                    Alert.show("NativeProcess를 지원하지 않음.");                       
                }
            }
           
            private function selectHandler(event:Event):void
            {
                var file:File = event.currentTarget as File;
               
                var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                //var args:Vector.<String> = new Vector.<String>;
                //args.push("/root,c:\\");
                //nativeProcessStartupInfo.arguments = args;
                nativeProcessStartupInfo.executable = file;
                nativeProcess = new NativeProcess();
                nativeProcess.start(nativeProcessStartupInfo);
               
            }
        ]]>
    </fx:Script>
   
    <s:Button id="btnRun" label="실행파일 선택"  click="btnRun_clickHandler(event)"/>
</s:WindowedApplication>[/code]
예제의 핵심은 NativeProcess클래스랑 NativeProcessStartupInfo클래스입니다.
NativeProcessStartupInfo클래스에 해당 file이랑 arguments를 넣어주고, NativeProcess로 실행하면 됩니다^^
그리고 Descriptor파일에 한줄 추가해줘야 하는 것 같은데요.
[code]<supportedProfiles>extendedDesktop</supportedProfiles>[/code]
왜 추가 안해도 잘 되지-_- 깜빡하고 추가안했는데, 잘 돼요. 이거 추가안하면 통신이 안되나...암튼 잘 모르겠네요.
근데, 저거 추가하고, Flash Builder에서 제공하는 빌더로 export하면 에러납니다-_-
[code]Error creating AIR file: NativeProcessTest-app.xml: error 306: Descriptor must support the profile desktop, mobileDevice, or extendedMobileDevice.[/code]
이거 웬지 Builder에서 제공하는 것은 1.5로 패키징하는 것 같음-_-
암튼, 저거 빼고 해서 air파일을 만들어도 실행하면 설치가 안됩니다. 이런 에러메세지가 나는군요.
[code]This application has been designed for a different device profile and will not run on your system. Contact the application author to see if a compatible version is available.[/code]
그래서 adt를 이용해서 command line으로 직접 패키징을 해야합니다. 아.....난 콘솔이 싫은데.....

우선 amxmlc로 컴파일합니다.
[code]amxmlc NativeProcess.mxml[/code]
그러면 NativeProcess.swf파일이 생김.
이제 패키징을 하면 됩니다.
[code]adt -package -storetype pkcs12 -keystore c:\Users\mudchobo\Documents\mudchobo.p12 -target native NativeProcessTest.exe NativeProcessTest-app.xml NativeProcessTest.swf[/code]
-keystore값에는 자기인증서 경로를 적구요. -target은 native로 하면 exe파일로 생성됩니다.
그리고 포함할 파일을 적으면 되구요.
그리고 빌더로 만든 *-app.xml파일을 보시면 <content>태그가
[code]<content>[This value will be overwritten by Flash Builder in the output app.xml]</content>[/code]로 되어있는데, 이거 swf파일 경로 적어 줘야합니다.
[code]<content>NativeProcess.swf</content>[/code]
비밀번호 입력하면 NativeProcessTest.exe파일이 생성되는데, 그거 실행하면 설치화면이 뜹니다.
이 설치파일은 기존 air파일로 패키징한거랑 틀리게 배경이 흰색이네요-_-

사용자 삽입 이미지
암튼, 설치하고 나면 실행 잘 됩니다.

※참고로 제가 탐섹히를 실행하려고 해봤는데, 이런 기능이 필요한 이유가 어떤 다운로드프로그램에서 사용자가 받은 파일이 있는 폴더를 손쉽게 열 수 있게 하는 것이 목표인데, 그럴려면 탐색기 실행이 필요합니다.
그래서 그냥 패키징할 때 탐색기 프로그램을 같이 패키징해버리면 보안샌드박스에 있기 때문에 실행할 수 있습니다-_-
c:\Windows\explorer.exe파일을 복사해서 패키징해버리고, 실행하면 탐색히가 뜹니다.
해당 폴더를 여는건 explorer.exe /root,c:\\로 하면 된다는.......-_-

마지막으로 다시한번 플래시빌더를 다운로드를-_-

Flash Builder4 다운로드

참고자료
http://www.adobe.com/devnet/air/flex/quickstart/interacting_with_native_process.html
 
Posted by 머드초보

댓글을 달아 주세요

  1. BlogIcon sewonist 2009.11.25 00:51 신고  댓글주소  수정/삭제  댓글쓰기

    와우~ AIR2 에서 달라진게 뭐가 있는 두리번 거리고 있었는데 이건 조금 대박이네요. 결국 샌드박스 안에만 있으면 .exe 파일을 실행 시킬 수 있다는 의미인거죠? 당장 내일 테스트 해봐야겠습니다.

    • 머드초보 2009.11.25 11:29 신고  댓글주소  수정/삭제

      네 자기가 c나 다른 언어로 짠 것을 air에 포함시켜서 그 프로그램과 통신도 할 수 있죠.
      방문해주셔서 감사해요~ ^^

  2. flex초보 2010.03.11 14:08 신고  댓글주소  수정/삭제  댓글쓰기

    근데요 꼭 c로 짠 exe만 실행할 수 있나요???
    C# 으로 짠 exe는 상호 통신할 수 없나요???
    혹 있으시면 알려주세요 ㅠ

  3. BlogIcon grimtong 2010.04.05 16:01 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 우선 정보 감사합니다.
    Flex3에서 NativeProcess관련해서 설명해주신대로 수동 컴파일까지는 성공했습니다.
    컴파일된 exe파이를 설치를 하고 실행까지는 잘 동작합니다.

    그런데 nativeProcess관련하여 commandLine 명령어를 지원하는 exe 프로그램이 AIR에서 start를 하면
    커맨트창이 잠깐 보였다가 실행되고 꺼집니다...
    이창을 숨기거나 안나오게 하는 방법이 있는지요?

    ^____________^

    • 머드초보 2010.04.09 10:19 신고  댓글주소  수정/삭제

      안녕하세요~
      저도....NativeProcess그냥 나왔을 때 잠깐 해본거라ㅠㅠ
      잘 모르겠습니다 ㅠㅠ

  4. 2010.07.27 16:42  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • 머드초보 2010.07.28 20:47 신고  댓글주소  수정/삭제

      음...혹시...프로젝트이름을 NativeProcess로 하셨나요?
      몬가 충돌이 일어나는 것 같아보이는데....

  5. 플래시 2010.09.24 13:52 신고  댓글주소  수정/삭제  댓글쓰기

    머드초보님..
    저는 플렉스는 모르지만 플래시는 아는데요.
    Flash CS5로 그냥 텍스트툴을 사용해서 t라는 텍스트필드를 만들고
    t.text = NativeProcess.isSupported.toString();
    이렇게 액션을 준 후 app xml에는 다음과 같이 주었습니다.
    <supportedprofiles>extendedDesktop desktop</supportedprofiles>
    그리고 플래시 속성창의 Air2 설정의 [편집]버튼을 눌러서 제작 버튼을 누르면 air파일은 만들어지는데
    <supportedprofiles>노드가 없어져 버립니다.
    그래서 air파일을 실행해서 보면 텍스트필드에도 NativeProcess.isSupported가 false로 나옵니다.

    <supportedprofiles>extendedDesktop</supportedprofiles> 이렇게만 줘도 없어져 버리거든요.
    어떻게 하면 <supportedprofiles>노드를 없애지 않을 수 있을까요?

    • 머드초보 2010.11.05 19:21 신고  댓글주소  수정/삭제

      음...이거 베타 때 한것이고, Flash에서 해보질 않아서 잘 모르겠네요ㅠㅠ
      뭔가 정책이 바뀐 것 같은데, adobe 문서를 확인해보시면ㅠㅠ