<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    當柳上原的風吹向天際的時候...

    真正的快樂來源于創造

      BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
      368 Posts :: 1 Stories :: 201 Comments :: 0 Trackbacks

    #

    此版是 http://www.tkk7.com/heyang/archive/2010/12/31/342057.html  的改進版本。

    改善功能:
    1.XML文件存儲。
    2.在英文操作系統下亂碼的問題。

    已有功能:
    1.基于TCP的通信功能。
    2.基于RSA+AES的對信息的混合加密。
    3.通過服務器轉發客戶端間消息。
    4.用戶的注冊,登錄,登出等功能。

    注意:
    本程序用到了AES加密,請確認http://www.tkk7.com/heyang/archive/2010/12/01/339515.html 一文中提到的兩個文件是否已經拷貝到了對應的兩處目錄。

    下載地址:
    http://www.box.net/shared/o8z5rcrff5


    posted @ 2011-01-21 17:17 何楊 閱讀(180) | 評論 (1)編輯 收藏

    以前,我們習慣用以下方式在XML中保存中文:
            try {
                OutputFormat format 
    = OutputFormat.createPrettyPrint();
                format.setEncoding(
    "GBK");    // 指定XML編碼        
                XMLWriter writer = new XMLWriter(new FileWriter("c:\\1.xml"
    ),format);
                
                Document document 
    = DocumentHelper.createDocument();
                Element root 
    = document.addElement("users");

                
    for(String user:userMap.keySet()){
                    Element userElm
    =root.addElement("user");
                    userElm.addElement(
    "name").addText(user);
                    userElm.addElement(
    "pswd").addText(userMap.get(user));
                }
               
                writer.write(document);
                writer.close();
            } 
    catch (Exception e) {
                System.out.println(
    "無法將注冊用戶信息存儲到文件中,原因為"+e.getMessage());
                e.printStackTrace();
            }

    在中文操作系統下,這樣的方案是可行的,但是在英文操作系統下中文就會變成問號。最好使用如下的修正方案:
            try {
                OutputStreamWriter osw 
    = new OutputStreamWriter(new FileOutputStream("c:\\1.xml"),"UTF-8");   
                OutputFormat format 
    = OutputFormat.createPrettyPrint();
                format.setEncoding(
    "UTF-8");    // 指定XML編碼       
                XMLWriter writer = new
     XMLWriter(osw,format);
                
                Document document 
    = DocumentHelper.createDocument();
                Element root 
    = document.addElement("users");

                
    for(String user:userMap.keySet()){
                    Element userElm
    =root.addElement("user");
                    userElm.addElement(
    "name").addText(user);
                    userElm.addElement(
    "pswd").addText(userMap.get(user));
                }
               
                writer.write(document);
                writer.close();
            } 
    catch (Exception e) {
                System.out.println(
    "無法將注冊用戶信息存儲到文件中,原因為"+e.getMessage());
                e.printStackTrace();
            }

    兩端代碼的變化區域在粗體部分,請注意區分。
    posted @ 2011-01-21 11:35 何楊 閱讀(769) | 評論 (0)編輯 收藏

    注:下文代碼主要來自參考書籍,本人稍稍修改了一下。

    泛型棧類:
    package com.heyang;

    /**
     * 棧數據結構
     * 說明:
     * 作者:何楊(heyang78@gmail.com)
     * 創建時間:2011-1-15 上午07:51:09
     * 修改時間:2011-1-15 上午07:51:09
     
    */
    public class MyStack<T>{
        
    private int size;    // 大小
        private T[] datas;    // 數據
        private int top;    // 棧頂元素下標
        
        @SuppressWarnings(
    "unchecked")
        
    public MyStack(int size){
            
    this.size=size;
            datas
    = (T[])new Object[this.size]; 
            top
    =-1;
        }
        
        
    /**
         * 壓棧
         
    */
        
    public void push(T t){
            datas[
    ++top]=t;
        }
        
        
    /**
         * 出棧
         * 
         * 說明:
         * 
    @return
         * 創建時間:2011-1-15 上午08:01:15
         
    */
        
    public T pop(){
            
    return datas[top--];
        }
        
        
    /**
         * 取得棧頂元素
         * 
         * 說明:
         * 
    @return
         * 創建時間:2011-1-15 上午08:02:02
         
    */
        
    public T getTopItem(){
            
    return datas[top];
        }
        
        
    /**
         * 查看棧是否為空
         * 
         * 說明:
         * 
    @return
         * 創建時間:2011-1-15 上午08:02:38
         
    */
        
    public boolean isEmpty(){
            
    return top==-1;
        }
    }

    括號檢查類:
    package com.heyang;

    /**
     * 表達式中括號檢查類
     * 說明:
     * 作者:何楊(heyang78@gmail.com)
     * 創建時間:2011-1-15 上午08:05:25
     * 修改時間:2011-1-15 上午08:05:25
     
    */
    public class BracketChecker{
        
    private String input;        // 輸入:待檢查的表達式
        private boolean isValid;    // 是否檢查通過
        private String checkResult;        // 輸出:檢查結果
        
        
    /**
         * 構造函數
         * 
    @param input
         
    */
        
    public BracketChecker(String input){
            
    this.input=input;
            
    this.isValid=false;
            
    this.checkResult="";
            
            check();
        }
        
        
    /**
         * 執行檢查
         * 
         * 說明:
         * 創建時間:2011-1-15 上午08:09:25
         
    */
        
    private void check(){
            
    int length=input.length();
            MyStack
    <Character> stack=new MyStack<Character>(length);
            
            
    for(int i=0;i<length;i++){
                
    char c=input.charAt(i);
                
                
    switch(c){
                    
    case '{':
                    
    case '[':
                    
    case '(':
                        stack.push(c);
                        
    break;
                    
    case '}':
                    
    case ']':
                    
    case ')':
                        
    if(stack.isEmpty()==false){
                            
    char top=stack.pop();
                            
                            
    if( (c=='}' && top!='{'|| (c==']' && top!='['|| (c==')' && top!='(') ){
                                isValid
    =false;
                                checkResult
    ="經檢查,表達式'"+input+"'中,位于第"+(i+1)+"的字符‘"+c+"’沒有對應的匹配項";
                                
    return;
                            }
                        }
                        
    else{
                            isValid
    =false;
                            checkResult
    ="經檢查,表達式'"+input+"'中,位于第"+(i+1)+"的字符‘"+c+"’沒有對應的匹配項";
                            
    return;
                        }
                        
    break;
                    
    default:
                        
    break;
                }
            }
            
            
    if(stack.isEmpty()==false){
                isValid
    =false;
                checkResult
    ="經檢查,表達式'"+input+"'中右括號缺失,匹配不完整";
                
    return;
            }
            
    else{
                isValid
    =true;
                checkResult
    ="經檢查,表達式'"+input+"'中括號匹配無誤.";
                
    return;
            }
        }

        
    /**
         * 括號是否匹配
         * 
         * 說明:
         * 
    @return
         * 創建時間:2011-1-15 上午08:39:38
         
    */
        
    public boolean isValid() {
            
    return isValid;
        }

        
    /**
         * 取得檢查結果
         * 
         * 說明:
         * 
    @return
         * 創建時間:2011-1-15 上午08:39:27
         
    */
        
    public String getCheckResult() {
            
    return checkResult;
        }
        
        
    public static void main(String[] args){
            String[] arr
    ={"1+(2/3-4","[1+(2/3-4)]*5","{[1+(2/3-4)]*5+(6+2*3)}*7","{[1+(2/3-4]*5+(6+2*3)}*7"};
            
            
    for(String str:arr){
                BracketChecker c
    =new BracketChecker(str);
                System.out.println(c.getCheckResult());
            }
        }
    }

    檢查結果:
    經檢查,表達式'1+(2/3-4'中右括號缺失,匹配不完整
    經檢查,表達式
    '[1+(2/3-4)]*5'中括號匹配無誤.
    經檢查,表達式
    '{[1+(2/3-4)]*5+(6+2*3)}*7'中括號匹配無誤.
    經檢查,表達式
    '{[1+(2/3-4]*5+(6+2*3)}*7'中,位于第11的字符‘]’沒有匹配項


    參考書籍:SAMS的《Java數據結構與算法》第四章

    posted @ 2011-01-15 08:49 何楊 閱讀(732) | 評論 (0)編輯 收藏

    注:以下是Swing GUI處理的一個小小技巧,對此無興趣者請退散。

    在Swing中有這樣一種狀況:即長時間運行的事件回調,當它運行時,其余的GUI是沒有響應的。如果這會持續較長的一段時間,它可能會讓使用者感到挫折和困惑。下面一段程序就展示了這一現象,其粗體部分的本意是每隔一秒刷新標簽中的文字,但是結果是按鈕事件響應完畢后,標簽上顯示最后一段文字:
    package com.heyang;

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;

    public class MyFrame extends JFrame{
        
    private static final long serialVersionUID = -5100794608937579830L;
        
        
    private JLabel msgLbl;
        
    private JButton cmdBtn;
        
        
    public MyFrame(){
            setTitle(
    "MyFrame");
            
            msgLbl
    =new JLabel("提示文字");
            cmdBtn
    =new JButton("刷新文本");
            
            
    this.setLayout(new BorderLayout());
            
    this.add(msgLbl,BorderLayout.NORTH);
            
    this.add(cmdBtn,BorderLayout.CENTER);
            
            
    // 設置大小,位置
            setSizeAndCentralizeMe(300200);
            
            
    // 點擊窗口右上角的關閉按鈕關閉窗口,直接退出程序
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            
    // 按鈕事件注冊
            cmdBtn.addActionListener(new ActionListener() {
                
    public void actionPerformed(ActionEvent e) {
                    runCmd();
                }
            });
            
            setVisible(
    true);
        }
        
        
    private void runCmd(){
            msgLbl.setText(
    "溫故而知新,可以為師矣。");
            longTimeProcess(
    10);
            
            msgLbl.setText(
    "由,汝知之乎!知之為知之,不知為不知,是知也。");
            longTimeProcess(
    10);
            
            msgLbl.setText(
    "見賢思齊焉,見不賢而內自省也");
            longTimeProcess(
    10);
            
            msgLbl.setText(
    "士不可以不弘毅,任重而道遠。");
            longTimeProcess(
    10);
            
            msgLbl.setText(
    "歲寒,然后知松柏之后凋也。");
            longTimeProcess(
    10
    );
        }
        
        
    /**
         * 模擬一個長時處理,以100毫秒為單位
         * 
         * 說明:
         * 
    @param mSeconds
         * 創建時間:2011-1-9 下午12:02:28
         
    */
        
    private void longTimeProcess(int mSeconds){
            
    try{
                Thread.sleep(mSeconds
    *100);
            }
            
    catch(Exception e){
                
            }
        }
        
        
    private void setSizeAndCentralizeMe(int width, int height) {
            Dimension screenSize 
    = Toolkit.getDefaultToolkit().getScreenSize();
            
    this.setSize(width, height);
            
    this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
                    
    / 2 - height / 2);
        }
        
        
    public static void main(String[] args){
            
    new MyFrame();
        }
    }

    要達到預期的效果,Swing建議:讓長時間運行的任務在獨立的線程中運行會好很多,這樣能夠讓GUI有適當響應。修改后的代碼如下:
    package com.heyang;

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;

    public class MyFrame extends JFrame{
        
    private static final long serialVersionUID = -5100794608937579830L;
        
        
    private JLabel msgLbl;
        
    private JButton cmdBtn;
        
        
    public MyFrame(){
            setTitle(
    "MyFrame");
            
            msgLbl
    =new JLabel("提示文字");
            cmdBtn
    =new JButton("刷新文本");
            
            
    this.setLayout(new BorderLayout());
            
    this.add(msgLbl,BorderLayout.NORTH);
            
    this.add(cmdBtn,BorderLayout.CENTER);
            
            
    // 設置大小,位置
            setSizeAndCentralizeMe(300200);
            
            
    // 點擊窗口右上角的關閉按鈕關閉窗口,直接退出程序
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            
    // 按鈕事件注冊
            cmdBtn.addActionListener(new ActionListener() {
                
    public void actionPerformed(ActionEvent e) {
                    runCmd();
                }
            });
            
            setVisible(
    true);
        }
        
        
    private void runCmd(){
            new Thread(){
                
    public void
     run(){
                    msgLbl.setText(
    "溫故而知新,可以為師矣。");
                    longTimeProcess(
    10);
                    
                    msgLbl.setText(
    "由,汝知之乎!知之為知之,不知為不知,是知也。");
                    longTimeProcess(
    10);
                    
                    msgLbl.setText(
    "見賢思齊焉,見不賢而內自省也");
                    longTimeProcess(
    10);
                    
                    msgLbl.setText(
    "士不可以不弘毅,任重而道遠。");
                    longTimeProcess(
    10);
                    
                    msgLbl.setText(
    "歲寒,然后知松柏之后凋也。");
                    longTimeProcess(
    10);
                }
            }.start();

        }
        
        
    /**
         * 模擬一個長時處理,以100毫秒為單位
         * 
         * 說明:
         * 
    @param mSeconds
         * 創建時間:2011-1-9 下午12:02:28
         
    */
        
    private void longTimeProcess(int mSeconds){
            
    try{
                Thread.sleep(mSeconds
    *100);
            }
            
    catch(Exception e){
                
            }
        }
        
        
    private void setSizeAndCentralizeMe(int width, int height) {
            Dimension screenSize 
    = Toolkit.getDefaultToolkit().getScreenSize();
            
    this.setSize(width, height);
            
    this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
                    
    / 2 - height / 2);
        }
        
        
    public static void main(String[] args){
            
    new MyFrame();
        }
    }

    以上代碼達到了預期效果,其中起關鍵作用的代碼就是以上粗體部分,它將長時處理放到了另一個線程中運行。
    這種做法不是唯一解決之道,Sun提供的SwingWorker類可以幫你達到目的,只是要繁瑣一些。這樣的技巧在耗時檢查,與服務器交互和復雜圖形處理中都能有所應用。

    參考書籍:
    O'REILLY 《Java 線程》一書。

    最后感謝您看到這里。

    何楊,2011年1月9日14:20:37


    posted @ 2011-01-09 14:08 何楊 閱讀(1049) | 評論 (3)編輯 收藏

    注意:
    1.NIO主要用于服務端,若用來客戶端相對于它帶來的額外復雜性有點不值得。
    2.用于編碼解碼字符集請根據您的機器缺省字符集進行調整,如GB2312,GBK,UTF-8,UTF-16等,若出現亂碼請更換之。
    3.BufferSize請根據您可能發送接收的最大字符串長度進行相應調整。

    代碼:
    package com.heyang.biz.server.test.nio;

    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.Iterator;


    /**
     * NIO 服務器
     * 說明:
     * 作者:何楊(heyang78@gmail.com)
     * 創建時間:2011-1-3 下午12:09:02
     * 修改時間:2011-1-3 下午12:09:02
     
    */
    public class NIOServer{
        
    // 本地字符集
        private static final String LocalCharsetName = "gb2312";

        
    // 本地服務器監聽的端口
        private static final int Listenning_Port=8888;
        
        
    // 緩沖區大小
        private static final int Buffer_Size=1024;
        
        
    // 超時時間,單位毫秒
        private static final int TimeOut=3000;
        
        
    public static void main(String[] args) throws Exception{
            
    // 創建一個在本地端口進行監聽的服務Socket信道.并設置為非阻塞方式
            ServerSocketChannel serverChannel=ServerSocketChannel.open();
            serverChannel.socket().bind(
    new InetSocketAddress(Listenning_Port));
            serverChannel.configureBlocking(
    false);
            
            
    // 創建一個選擇器并將serverChannel注冊到它上面
            Selector selector=Selector.open();
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            
            
    while(true){
                
    // 等待某個信道就緒
                if(selector.select(TimeOut)==0){
                    System.out.println(
    ".");
                    
    continue;
                }
                
                
    // 獲得就緒信道的鍵迭代器
                Iterator<SelectionKey> keyIter=selector.selectedKeys().iterator();
                
                
    // 使用迭代器進行遍歷就緒信道
                while(keyIter.hasNext()){
                    SelectionKey key
    =keyIter.next();
                    
                    
    // 這種情況是有客戶端連接過來,準備一個clientChannel與之通信
                    if(key.isAcceptable()){
                        SocketChannel clientChannel
    =((ServerSocketChannel)key.channel()).accept();
                        clientChannel.configureBlocking(
    false);
                        clientChannel.register(key.selector(), SelectionKey.OP_READ,ByteBuffer.allocate(Buffer_Size));
                    }
                    
                    
    // 客戶端有寫入時
                    if(key.isReadable()){
                        
    // 獲得與客戶端通信的信道
                        SocketChannel clientChannel=(SocketChannel)key.channel();
                        
                        
    // 得到并重置緩沖區的主要索引值
                        ByteBuffer buffer=(ByteBuffer)key.attachment();
                        buffer.clear();
                        
                        
    // 讀取信息獲得讀取的字節數
                        long bytesRead=clientChannel.read(buffer);
                        
                        
    if(bytesRead==-1){
                          
    // 沒有讀取到內容的情況
                          clientChannel.close();
                        }
                        
    else{
                          
    // 將緩沖區準備為數據傳出狀態
                          buffer.flip();
                          
                          
    // 將獲得字節字符串(使用Charset進行解碼)   
                          String receivedString=Charset.forName(LocalCharsetName).newDecoder().decode(buffer).toString();
                          
                          
    // 控制臺打印出來
                          System.out.println("接收到信息:"+receivedString);
                          
                          
    // 準備發送的文本
                          String sendString="你好,客戶端. 已經收到你的信息"+receivedString;
                          
                          
    // 將要發送的字符串編碼(使用Charset進行編碼)后再進行包裝
                          buffer=ByteBuffer.wrap(sendString.getBytes(LocalCharsetName));
                          
                          
    // 發送回去
                          clientChannel.write(buffer);
                          
                          
    // 設置為下一次讀取或是寫入做準備
                          key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                        }
                    }
                    
                    keyIter.remove();
                }
            }
            
        }
    }

    與之配合的客戶端代碼,沒有采用NIO方式。
    package com.heyang.biz.server.test.nio;

    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Scanner;



    public class TestClient{
        
    public static void main(String[] args) throws Exception{
            Socket s
    =new Socket("127.0.0.1",8888);
            
            InputStream  inStram
    =s.getInputStream();
            OutputStream outStream
    =s.getOutputStream();
            
            
    // 輸出
            PrintWriter out=new PrintWriter(outStream,true);
            
            out.print(
    "getPublicKey你好!");
            out.flush();

            s.shutdownOutput();
    // 輸出結束
            
            
    // 輸入
            Scanner in=new Scanner(inStram);
            StringBuilder sb
    =new StringBuilder();
            
    while(in.hasNextLine()){
                String line
    =in.nextLine();
                sb.append(line);
            }
            String response
    =sb.toString();
            System.out.println(
    "response="+response);
        }
    }

    服務器端的輸出:
    .
    接收到信息:getPublicKey你好!
    .

    客戶端的輸出:
    response=你好,客戶端. 已經收到你的信息getPublicKey你好!
    posted @ 2011-01-03 14:36 何楊 閱讀(4710) | 評論 (0)編輯 收藏

    一.目錄互斥方案:這種互斥要求同一目錄下不能運行兩個應用程序的實例,通常用文件解決之,即程序啟動時看看所在目錄下有沒有一個特定文件的存在,在的話就退出,不在的話就自己創建一個,退出時再刪除之。這種情況下,這個臨時文件便成了同一目錄下程序已經啟動實例的標志。為了避免誤刪除導致此功能失常,可以給文件加上隱藏屬性。

    二.本機互斥方案:這種互斥要求在一臺機器上只能運行一個實例。一種解決辦法是在注冊表的特定位置進行記錄,原理和文件互斥類似;另一個解決辦法是利用端口,即程序啟動后在某一端口進行監聽,這樣第二個實例啟動時再去監聽就不可行了,這種方式很巧妙,無須顧忌程序異常退出問題,只是會無端占用一個端口,有時客觀環境不允許這么干。

    三.單用戶互斥方案:這種互斥要求系統只允許同名用戶登錄一次,因此服務器端必須存放用戶對應的數據,當用戶登上時檢查這部分數據就可以了,這種方式最棘手的是客戶端程序的異常退出導致再也無法登錄,但讓客戶端發心跳包或是服務器端反連客戶端來解決。

    綜上,互斥功能要成功,就要找到互斥對象共同的環境或是上下文。如果找到了,互斥就變成了一個技術問題。

    posted @ 2010-12-31 13:47 何楊 閱讀(269) | 評論 (0)編輯 收藏

    按:以下是一個老舊的IM服務器客戶機例子,網絡間傳遞的信息采用混合加密方式以保護客戶隱私,熟知這兩細節者就不用往下看了,以免浪費寶貴時間。

    下載地址
    http://www.box.net/shared/jcrbps2hk3

    下載包說明:
    從上述地址下載完畢后是一個Eclispe工程,載入即可使用,可以執行RunServer運行服務器端,執行RunClient運行客戶端。

    使用說明:
    先啟動服務器端,讓其開始監聽;再由各客戶端連到服務器上,注冊好用戶后各個客戶機之間即可通信,此時服務器起一個消息轉發器的作用。

    截圖:


    幾句啰嗦的話:
    1.本程序是為了配合說明混合加密方式在IM程序中的使用,目標不是做一個商用IM軟件。
    2.其中的混合加密方式的使用可以參考(http://www.tkk7.com/heyang/archive/2010/12/25/341518.html)和(http://www.tkk7.com/heyang/archive/2010/12/26/341556.html)這兩篇文章。

    posted @ 2010-12-31 13:06 何楊 閱讀(1873) | 評論 (6)編輯 收藏

    一般來說,“.”在正則表達式中是匹配任意字符,但是,實際上,它是不能匹配換行符的,因為它的真實定義是“ 匹配除換行符號之外的任意字符”,這點要特別注意一下。
    要匹配任意字符,可用"(.|\\n)+"或是"(.|\\s)+".

    posted @ 2010-12-27 18:04 何楊 閱讀(597) | 評論 (0)編輯 收藏

         摘要: 按:前面(http://www.tkk7.com/heyang/archive/2010/12/25/341518.html)已經提到過混合加密方式,今天這里再來贅述一下完成的代碼和流程,熟悉者就不用往下看了。完整的IM程序在(http://www.box.net/shared/jcrbps2hk3)可以下載。 整個流程的UML Sequence圖(Amaterus UML用得還不熟...  閱讀全文
    posted @ 2010-12-26 11:41 何楊 閱讀(7351) | 評論 (4)編輯 收藏

         摘要: 按:下面的文字涉及早已在工程中廣泛采用的混合加密方式,對此熟知者就不用往下看了,以免浪費時間。 我們知道,現代加密方式有兩大類:一類是對稱加密方式,其優點是加密速度快,缺點是密鑰不便于傳遞,其中典型代表是AES;一類是非對稱加密方式,優點是交換鑰匙方便,缺點是加密時間長,代表是RSA。在實際應用,我們可以取其所長,棄其所短,這就是混合加密方式,有的場合也成為Hybrid方式。 具體來說...  閱讀全文
    posted @ 2010-12-25 16:02 何楊 閱讀(4324) | 評論 (6)編輯 收藏

    僅列出標題
    共28頁: First 上一頁 12 13 14 15 16 17 18 19 20 下一頁 Last 
    主站蜘蛛池模板: 亚洲精品成人区在线观看| 免费播放在线日本感人片| 精品成在人线AV无码免费看 | 国产亚洲综合一区柠檬导航| 亚洲另类无码专区丝袜| 波多野结衣免费在线观看| 久久久久久亚洲AV无码专区| 国产午夜精品久久久久免费视 | 亚洲热妇无码AV在线播放| 精品熟女少妇aⅴ免费久久| 亚洲熟女乱综合一区二区| 国产精品视频全国免费观看| 亚洲夜夜欢A∨一区二区三区 | 久久久久亚洲精品天堂| 91精品免费观看| 亚洲成人免费网站| 国产成人A在线观看视频免费| 亚洲伦理一二三四| 午夜视频在线在免费| 国产亚洲成在线播放va| 青青草原亚洲视频| 久久国产精品萌白酱免费| 亚洲国产精品成人综合色在线婷婷| 成人毛片免费观看视频| 在线观看亚洲专区| 久久亚洲AV无码西西人体| 久久99热精品免费观看动漫 | 亚洲avav天堂av在线不卡| 91精品成人免费国产片| 亚洲av无一区二区三区| 亚洲午夜福利精品无码| 一区二区三区福利视频免费观看| 色在线亚洲视频www| 亚洲国产精品无码久久久久久曰| 精品视频一区二区三区免费| 亚洲国产日韩在线人成下载| 国产成人精品免费直播| 免费萌白酱国产一区二区三区| 亚洲av成人综合网| 久久乐国产精品亚洲综合| 亚洲人成免费电影|