Flex通訊-與Java實現Socket通訊實例

Flex通訊-與Java實現Socket通訊實例  轉自:http://blessht.iteye.com/blog/1136888

博客分類:

  • 環境準備
【服務器端】
JDK1.6,「java.net」包對網絡編程提供了很是全面的支持,包括Socket
開發環境:Eclipse
【客戶端】
Flex4,」flash.net」包也提供了Socket的支持
開發環境:FlashBuilder4
 
  • 實例效果
我是用Java啓動一個ServerSocket做爲服務器,Flex建立一個頁面,當點擊頁面上的按鈕時向Java服務器發送消息。
 
Flex客戶端輸入「阿里巴巴」再點擊按鈕:

 Java控制檯:


 
 
  • 注意事項
Flex項目分爲兩種:一種是普通的本地項目,也就是不依賴其它服務器;另外一種是遠程服務器項目,這須要依賴其它語言的服務器容器。若是是普通的本地項目,Socket通訊是很是容易的,可是若是是遠程項目,Socket通訊須要考慮Flex安全沙箱問題,後面會詳細介紹。


 
 
  • Java Socket服務器
編寫Socket Server代碼的步驟一般是:
①建立ServerSocket,定義服務端口號
②使用ServerSocket.accept()監聽socket請求,若是有請求會建立一個Socket對象
③經過socket.getInputStream()獲取客戶端的請求數據
④經過socket.getOutputStream()向客戶端返回數據
⑤經過socket.close()結束本次會話
按照上面的步驟,若是有多個客戶端向服務器發送請求的話,服務器只會處理第一個請求,其它請求會排隊等待,只有第一個請求執行socket.close的時候下一個客戶端請求才會運行。爲了實現多客戶端併發請求,在第②步後面須要創建多線程。
 
廢話很少說,直接代碼說明。
 
首先建立一個 SocketUtil類,用於建立ServerSocket和獲取Socket
Java代碼  
  1. public class SocketUtil {   
  2.        
  3.     /**  
  4.      * 建立ServerSocket  
  5.      * @param port  
  6.      * @return  
  7.      */  
  8.     public static ServerSocket getServerSocket(int port){   
  9.         ServerSocket server = null;   
  10.         try {   
  11.             server = new ServerSocket(port);   
  12.             System.out.println("------ServerSocket建立成功,Port:"+port);   
  13.             return server;   
  14.         } catch (IOException e) {   
  15.             if(server!=null && !server.isClosed()){   
  16.                 try {   
  17.                     server.close();   
  18.                 } catch (IOException e1) {   
  19.                     e1.printStackTrace();   
  20.                 }   
  21.             }   
  22.             throw new RuntimeException("建立ServerSocket時發生異常,Port:"+port,e);   
  23.         }   
  24.     }   
  25.        
  26.     /**  
  27.      * 獲取Socket  
  28.      * @param server  
  29.      * @return  
  30.      */  
  31.     public static Socket getSocket(ServerSocket server){   
  32.         Socket socket = null;   
  33.         try {   
  34.             socket = server.accept();   
  35.             System.out.println("------Socket鏈接成功,IP:"+socket.getInetAddress());   
  36.             return socket;   
  37.         } catch (IOException e) {   
  38.             if(socket!=null && !socket.isClosed()){   
  39.                 try {   
  40.                     socket.close();   
  41.                 } catch (IOException e1) {   
  42.                     e1.printStackTrace();   
  43.                 }   
  44.             }   
  45.             throw new RuntimeException("建立Socket時發送異常",e);   
  46.         }   
  47.     }   
  48.        
  49. }  
 
而後建立一個帶多線程的類,用於服務器與客戶端的IO通訊
Java代碼  
  1. public class SocketThread implements Runnable {   
  2.     private Socket socket;   
  3.     private String encoding;   
  4.        
  5.     public SocketThread(Socket socket,String encoding) {   
  6.         this.socket = socket;   
  7.         this.encoding = encoding;   
  8.     }   
  9.        
  10.     /**  
  11.      * 與客戶端交互代碼  
  12.      */  
  13.     @Override  
  14.     public void run() {   
  15.         try {   
  16.             BufferedReader br = new BufferedReader(new InputStreamReader(socket   
  17.                     .getInputStream(), encoding));   
  18.             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(   
  19.                     socket.getOutputStream(), encoding));   
  20.                
  21.             String getMsg;   
  22.             while ((getMsg = br.readLine()) != null && !"exit".equalsIgnoreCase(getMsg)) {   
  23.                 // 客戶端未提出"exit"命令,則循環交流   
  24.                 System.out.println("From client message:" + getMsg);   
  25.                 bw.append("你好[" + socket.getInetAddress() + "],服務器收到你的信息:"  
  26.                         + getMsg + "\r\n");   
  27.                 bw.flush();   
  28.             }   
  29.                
  30.             //客戶端提出"exit"請求,關閉當前socket...   
  31.             br.close();   
  32.             bw.close();   
  33.             socket.close();   
  34.             System.out.println("當前Socket鏈接結束......");   
  35.         } catch (Exception e) {   
  36.             if(!socket.isClosed()){   
  37.                 try {   
  38.                     socket.close();   
  39.                 } catch (IOException e1) {   
  40.                     e1.printStackTrace();   
  41.                 }   
  42.             }   
  43.             throw new RuntimeException("Socket線程類發送異常...",e);   
  44.         }   
  45.     }   
  46. }  
 
最後在Main函數中啓動Socket服務便可:
Java代碼  
  1. public static void main(String[] args) {   
  2.         new SocketServer().startSocket();   
  3.     }  
 
  • Flex客戶端代碼
Flex端建立Socket有兩種方式:
第一種經過connect方法鏈接Socket服務器:
Java代碼  複製代碼  收藏代碼
  1. var socket:Socket = new Socket();   
  2. socket.connect("localhost",10086);  
 第二種經過建立Socket實例時在構造函數中傳入服務器ip和端口號鏈接:
Java代碼  
  1. socket = new Socket("localhost",10086);  
 Flex經過ByteArray傳送IO數據,這裏有一點稍微注意一下在寫入的內容後面會加" \r\n"回車換行符,由於java端是經過BufferedReader.readLine的形式獲取一行數據,若是不換行服務器端IO就一直處於阻塞狀態:
Java代碼  
  1. //ByteArray存放數據   
  2. var message:ByteArray = new ByteArray();   
  3. //使用UTF形式防止中文亂碼   
  4. message.writeUTFBytes(txt_socket.text+"\r\n");   
  5. //數據寫入緩衝區   
  6. socket.writeBytes(message);  
 Flex有多種事件用於監聽Socket的狀態:
Java代碼  
  1. Event.CONNECT:Socket與服務器成功鏈接時觸發的事件   
  2. Event.CLOSE:Socket與服務器斷開鏈接時觸發的事件   
  3. IOErrorEvent.IO_ERROR:Socket通訊時發生IO錯誤時觸發的事件   
  4. ProgressEvent.SOCKET_DATA:服務器返回數據時觸發的事件  
 
新建一個Flex普通項目,入口文件定義爲index.mxml,在mxml文件中新建一個textinput文本框用於獲取用戶輸入的內容,button按鈕用戶發送內容到java socket服務器,一個label用戶顯示向前socket狀態,另外一個label用於顯示從服務器返回的信息。 index.mxml代碼以下:
Xml代碼  
  1. <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"    
  2.                xmlns:s="library://ns.adobe.com/flex/spark"    
  3.                xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">  
  4.   
  5.     <fx:Script>  
  6.         <![CDATA[  
  7.               
  8.               
  9.             private var socket:Socket = null;  
  10.             protected function button1_clickHandler(event:MouseEvent):void  
  11.             {  
  12.                 if(socket==null || !socket.connected){  
  13.                     //鏈接服務器(ip,port)  
  14.                     socket = new Socket("localhost",10086);  
  15.                       
  16.                     //成功鏈接狀態事件  
  17.                     socket.addEventListener(Event.CONNECT,function connFun(e:Event):void{  
  18.                         l_status.text = "Connect to server success...";  
  19.                     });  
  20.                     //鏈接中斷事件  
  21.                     socket.addEventListener(Event.CLOSE,function closeFun(e:Event):void{  
  22.                         l_status.text = "Connect to server closed...";  
  23.                     });  
  24.                     //鏈接異常事件  
  25.                     socket.addEventListener(IOErrorEvent.IO_ERROR,function closeFun(e:IOErrorEvent):void{  
  26.                         l_status.text = "Connect exception ..."+e.toString();  
  27.                     });  
  28.                     //服務器信息事件  
  29.                     socket.addEventListener(ProgressEvent.SOCKET_DATA,function dataFun(e:ProgressEvent):void{  
  30.                         var getMsg:ByteArray = new ByteArray;  
  31.                         socket.readBytes(getMsg);  
  32.                         l_result.text = getMsg.toString();  
  33.                     });  
  34.                 }  
  35.                   
  36.                 //ByteArray存放數據  
  37.                 var message:ByteArray = new ByteArray();  
  38.                 //使用UTF形式防止中文亂碼  
  39.                 message.writeUTFBytes(txt_socket.text+"\r\n");  
  40.                 //數據寫入緩衝區  
  41.                 socket.writeBytes(message);  
  42.                 //將緩衝區數據發送出去  
  43.                 socket.flush();  
  44.                 //清空文本框內容  
  45.                 txt_socket.text = "";  
  46.             }  
  47.         ]]>  
  48.     </fx:Script>  
  49.   
  50.     <fx:Declarations>  
  51.         <!-- 將非可視元素(例如服務、值對象)放在此處 -->  
  52.     </fx:Declarations>  
  53.     <s:Button x="156" y="56" label="按鈕" click="button1_clickHandler(event)"/>  
  54.     <s:TextInput x="20" y="56" id="txt_socket"/>  
  55.     <s:Label x="20" y="104" id="l_status"/>  
  56.     <s:Label x="234" y="65" id="l_result"/>  
  57. </s:Application>  
 
代碼編寫完成後運行index.mxml文件,最後執行效果就如前面【實例效果】所示。
 
 
  • 安全沙箱
下面這段是從網上抄的:
----------------------------------------------------------------------------
在 Adobe Flash Player 升級到 9.0.124 後,因爲安全策略更改,原來 Socket 或 XmlSocket 的應用裏的 http 方式加載安全策略的手段不能繼續使用。更改以下:
1, 首先檢測目標服務器的 843 端口是否提供安全策略 

2, 若是 1 沒有檢測到策略,則檢測 actionscript 是否使用了 Security.loadPolicyFile(xmlsocket://)手段提供安全策略,若是還沒檢測到,則使用第 3 步檢測
3, 檢測目標服務器目標端口是否提供安全策略。

在說具體處理方式前,我先描述一下 Flash Player 的驗證過程。在 Flex 程序發出 Socket 或 XmlSocket( 如下統稱爲 Socket) 請求前, FlashPlayer 會先判斷是否爲本地調用,若是不是。即用一個 Socket 去連接到你的服務端,三次握手成功後一方面發出字符串「 <policy-file-request/>\0 「另外一方面監聽返回的安全策略。安全策略接收成功後, FlashPlayer 就斷開驗證的 Socket ,而後再運行程序自己的 Socket 。在整個 SWF 運行期間,不管你請求多少次,只要域相同, FlashPlayer 就只驗證一次。這裏有兩個重點:
 
第一個是驗證的 Socket 和程序的 Socket 是兩個 Socket 。因此你在本地測試時,服務端監聽到 N 個 Socket 請求,但佈置到服務端後,服務端會監聽到 N+1 個請求。
第二是驗證的 Socket 發送「 <policy-file-request/>\0 「請求和接收你的策略文件是沒有前後關係的,因此你不必接收完「 <policy-file-request/>\0 「後才發策略文件。個人作法是隻要監聽到請求,就把策略字符串發過去。
-----------------------------------------------------------------------------------
 
那麼簡單的說,若是Flex項目依賴其它語言的服務器的話(好比依賴J2EE服務器),在flex的socket客戶端向JavaSocket服務器發送請求以前,Flex會優先發送一個安全驗證消息,若是java服務器不返回驗證消息則當前socket通訊失敗。
解決辦法有不少種,我在網上也看了不少,可是不少寫得有問題。
根據我多方調查,我的以爲這種方案比較靠譜:
在Java服務器端建立一個端口號爲843的ServerSocket監聽Flex安全沙箱驗證消息,若是接收到<policy-file-request/>文件信息,則向客戶端返回XMl驗證內容:「<?xml version=\"1.0\"?><cross-domain-policy><site-control permitted-cross-domain-policies=\"all\"/><allow-access-from domain=\"*\" to-ports=\"*\"/></cross-domain-policy>\0」
具體代碼以下:
Java代碼  
  1. /**  
  2.  * 處理與Flex認證的線程類  
  3.  * @author Administrator  
  4.  */  
  5. public class PolicyThread implements Runnable {   
  6.     private final String policy_xml = "<policy-file-request/>";   
  7.     private final String cross_xml = "<?xml version=\"1.0\"?><cross-domain-policy><site-control permitted-cross-domain-policies=\"all\"/><allow-access-from domain=\"*\" to-ports=\"*\"/></cross-domain-policy>\0";   
  8.     private Socket socket;   
  9.        
  10.     public PolicyThread(Socket socket){   
  11.         this.socket = socket;   
  12.     }   
  13.        
  14.     @Override  
  15.     public void run() {   
  16.         try {   
  17.             //接收併發送Flex安全驗證請求   
  18.             BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));   
  19.             PrintWriter pw = new PrintWriter(socket.getOutputStream());   
  20.             char[] by = new char[22];   
  21.             br.read(by, 022);   
  22.             String s = new String(by);   
  23.             if(s.equals(policy_xml)){   
  24.                 System.out.println("接收policy-file-request認證");   
  25.                 pw.print(cross_xml);   
  26.                 pw.flush();   
  27.                 br.close();   
  28.                 pw.close();   
  29.                 socket.close();   
  30.                 System.out.println("完成policy-file-request認證");   
  31.             }   
  32.         } catch (IOException e) {   
  33.             if(!socket.isClosed()){   
  34.                 try {   
  35.                     socket.close();   
  36.                 } catch (IOException e1) {   
  37.                     e1.printStackTrace();   
  38.                 }   
  39.             }   
  40.             throw new RuntimeException("執行policy認證時發生異常",e);   
  41.         }   
  42.     }   
  43.   
  44. }  
Java代碼  
  1. public class PolicyServer implements Runnable{   
  2.        
  3.     private final int policy_port = 843;   
  4.     private boolean status = true;   
  5.        
  6.     private ServerSocket server = null;   
  7.     @Override  
  8.     public void run() {   
  9.         //建立安全驗證服務器   
  10.         server = SocketUtil.getServerSocket(policy_port);   
  11.            
  12.         while(status){   
  13.             Socket socket = SocketUtil.getSocket(server);   
  14.             new Thread(new PolicyThread(socket)).start();   
  15.         }   
  16.     }   
  17.        
  18.        
  19.     /**  
  20.      * 啓動服務器  
  21.      */  
  22.     public void startPolicy(){   
  23.         new Thread(this).start();   
  24.     }   
  25.        
  26.     /**  
  27.      * 關閉服務器  
  28.      */  
  29.     public void stopPolicy(){   
  30.         status = false;   
  31.         if(server!=null && !server.isClosed()){   
  32.             try {   
  33.                 server.close();   
  34.             } catch (IOException e) {   
  35.                 e.printStackTrace();   
  36.             }   
  37.         }   
  38.     }   
  39. }  
 
Flex客戶端向Java發送第一次Socket請求(例子裏的端口號是10086)時,ServerSocket843端口會收到安全沙箱驗證,隨後server將正確的驗證消息返回給Flex客戶端,Flex認證成功後真正的10086端口Socket連結就已經搭建了,隨後雙方就能夠暢通無阻通訊了(一次會話只進行一次沙箱驗證)。
 
 
  • Servlet啓動ServerSocket
我一般比較喜歡建立一個servlet,在web.xml中配置容器啓動時運行servlet的init方法,這樣端口號爲10086和843的serverSocket就會啓動:
Java代碼  
  1. public class InitServers extends HttpServlet {   
  2.     private static final long serialVersionUID = 1L;   
  3.           
  4.     /**  
  5.      * @see HttpServlet#HttpServlet()  
  6.      */  
  7.     public InitServers() {   
  8.         super();   
  9.     }   
  10.   
  11.     /**  
  12.      * @see Servlet#init(ServletConfig)  
  13.      */  
  14.     public void init(ServletConfig config) throws ServletException {   
  15.         new PolicyServer().startPolicy();   
  16.         new SocketServer().startSocket();   
  17.     }   
  18.   
  19.     /**  
  20.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)  
  21.      */  
  22.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   
  23.         // TODO Auto-generated method stub   
  24.     }   
  25.   
  26.     /**  
  27.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)  
  28.      */  
  29.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   
  30.         // TODO Auto-generated method stub   
  31.     }   
  32.   
  33. }  
 web.xml
 
Xml代碼  
  1. <servlet>  
  2.         <display-name>InitServers</display-name>  
  3.         <servlet-name>InitServers</servlet-name>  
  4.         <servlet-class>socket.InitServers</servlet-class>  
  5.         <load-on-startup>1</load-on-startup>  
  6.     </servlet>  
 
以上一個完整的Flex+Java的Socket通訊就完成了。
相關文章
相關標籤/搜索