• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tbwshc

            tbw

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              95 Posts :: 8 Stories :: 3 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(4)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

            作網頁的過程中,你有時候需要知道某個元素在網頁上的確切位置。

            下面的教程總結了Javascript在網頁定位方面的相關知識。

            一、網頁的大小和瀏覽器窗口的大小

            首先,要明確兩個基本概念。

            一張網頁的全部面積,就是它的大小。通常情況下,網頁的大小由內容和CSS樣式表決定。

            瀏覽器窗口的大小,則是指在瀏覽器窗口中看到的那部分網頁面積,又叫做viewport(視口)。

            很顯然,如果網頁的內容能夠在瀏覽器窗口中全部顯示(也就是不出現滾動條),那么網頁的大小和瀏覽器窗口的大小是相等的。如果不能全部顯示,則滾動瀏覽器窗口,可以顯示出網頁的各個部分。

            二、獲取網頁的大小

            網頁上的每個元素,都有clientHeight和clientWidth屬性。這兩個屬性指元素的內容部分再加上padding的所占據的視覺面積,不包括border和滾動條占用的空間。

            (圖一 clientHeight和clientWidth屬性)

            因此,document元素的clientHeight和clientWidth屬性,就代表了網頁的大小。

              function getViewport(){
                if (document.compatMode == "BackCompat"){
                  return {
                    width: document.body.clientWidth,
                    height: document.body.clientHeight
                  }
                } else {
                  return {
                    width: document.documentElement.clientWidth,
                    height: document.documentElement.clientHeight
                  }
                }
              }

            上面的getViewport函數就可以返回瀏覽器窗口的高和寬。使用的時候,有三個地方需要注意:

            1)這個函數必須在頁面加載完成后才能運行,否則document對象還沒生成,瀏覽器會報錯。

            2)大多數情況下,都是document.documentElement.clientWidth返回正確值。但是,在IE6的quirks模式中,document.body.clientWidth返回正確的值,因此函數中加入了對文檔模式的判斷。

            3)clientWidth和clientHeight都是只讀屬性,不能對它們賦值。

            三、獲取網頁大小的另一種方法

            網頁上的每個元素還有scrollHeight和scrollWidth屬性,指包含滾動條在內的該元素的視覺面積。

            那么,document對象的scrollHeight和scrollWidth屬性就是網頁的大小,意思就是滾動條滾過的所有長度和寬度。

            仿照getViewport()函數,可以寫出getPagearea()函數。

              function getPagearea(){
                if (document.compatMode == "BackCompat"){
                  return {
                    width: document.body.scrollWidth,
                    height: document.body.scrollHeight
                  }
                } else {
                  return {
                    width: document.documentElement.scrollWidth,
                    heightb: document.documentElement.scrollHeight
                  }
                }
              }

            但是,這個函數有一個問題。如果網頁內容能夠在瀏覽器窗口中全部顯示,不出現滾動條,那么網頁的clientWidth和scrollWidth應該相等。但是實際上,不同瀏覽器有不同的處理,這兩個值未必相等。所以,我們需要取它們之中較大的那個值,因此要對getPagearea()函數進行改寫。

              function getPagearea(){
                if (document.compatMode == "BackCompat"){
                  return {
                    width: Math.max(document.body.scrollWidth,
                            document.body.clientWidth),
                    height: Math.max(document.body.scrollHeight,
                            document.body.clientHeight)
                  }
                } else {
                  return {
                    width: Math.max(document.documentElement.scrollWidth,
                            document.documentElement.clientWidth),
                    height: Math.max(document.documentElement.scrollHeight,
                            document.documentElement.clientHeight)
                  }
                }
              }

            四、獲取網頁元素的絕對位置

            網頁元素的絕對位置,指該元素的左上角相對于整張網頁左上角的坐標。這個絕對位置要通過計算才能得到。

            首先,每個元素都有offsetTop和offsetLeft屬性,表示該元素的左上角與父容器(offsetParent對象)左上角的距離。所以,只需要將這兩個值進行累加,就可以得到該元素的絕對坐標。

            (圖二 offsetTop和offsetLeft屬性)

            下面兩個函數可以用來獲取絕對位置的橫坐標和縱坐標。

              function getElementLeft(element){
                var actualLeft = element.offsetLeft;
                var current = element.offsetParent;

                while (current !== null){
                  actualLeft += current.offsetLeft;
                  current = current.offsetParent;
                }

                return actualLeft;
              }

              function getElementTop(element){
                var actualTop = element.offsetTop;
                var current = element.offsetParent;

                while (current !== null){
                  actualTop += current.offsetTop;
                  current = current.offsetParent;
                }

                return actualTop;
              }

            由于在表格和iframe中,offsetParent對象未必等于父容器,所以上面的函數對于tb表格和iframe中的元素不適用。

            五、獲取網頁元素的相對位置

            網頁元素的相對位置,指該元素左上角相對于瀏覽器窗口左上角的坐標。

            有了絕對位置以后,獲得相對位置就很容易了,只要將絕對坐標減去頁面的滾動條滾動的距離就可以了。滾動條滾動的垂直距離,是document對象的scrollTop屬性;滾動條滾動的水平距離是document對象的scrollLeft屬性。

            (圖三 scrollTop和scrollLeft屬性)

            對上一節中的兩個函數進行相應的改寫:

              function getElementViewLeft(element){
                var actualLeft = element.offsetLeft;
                var current = element.offsetParent;

                while (current !== null){
                  actualLeft += current.offsetLeft;
                  current = current.offsetParent;
                }

                if (document.compatMode == "BackCompat"){
                  var elementScrollLeft=document.body.scrollLeft;
                } else {
                  var elementScrollLeft=document.documentElement.scrollLeft; 
                }

                return actualLeft-elementScrollLeft;
              }

              function getElementViewTop(element){
                var actualTop = element.offsetTop;
                var current = element.offsetParent;

                while (current !== null){
                  actualTop += current. offsetTop;
                  current = current.offsetParent;
                }

                 if (document.compatMode == "BackCompat"){
                  var elementScrollTop=document.body.scrollTop;
                } else {
                  var elementScrollTop=document.documentElement.scrollTop; 
                }

                return actualTop-elementScrollTop;
              }

            scrollTop和scrollLeft屬性是可以賦值的,并且會立即自動滾動網頁到相應位置,因此可以利用它們改變網頁元素的相對位置。另外,element.scrollIntoView()方法也有類似作用,可以使網頁元素出現在瀏覽器窗口的左上角。

            六、獲取元素位置的快速方法

            除了上面的函數以外,還有一種快速方法,可以立刻獲得網頁元素的位置。

            那就是使用getBoundingClientRect()方法。它返回一個對象,其中包含了left、right、top、bottom四個屬性,分別對應了該元素的左上角和右下角相對于瀏覽器窗口(viewport)左上角的距離。

            所以,網頁元素的相對位置就是

              var X= this.getBoundingClientRect().left;

              var Y =this.getBoundingClientRect().top;

            再加上滾動距離,就可以得到絕對位置

              var X= this.getBoundingClientRect().left+document.documentElement.scrollLeft;

              var Y =this.getBoundingClientRect().top+document.documentElement.scrollTop;

            目前,IE、Firefox 3.0+、Opera 9.5+都支持該方法,而Firefox 2.x、Safari、Chrome、Konqueror不支持

            posted @ 2012-06-22 12:55 tbwshc 閱讀(971) | 評論 (0)編輯 收藏

            中新社舊金山5月19日電 (記者 劉丹)北京大學常務副校長吳志攀19日在美國舊金山表示,靈活的創造力是中國大學生最缺少的才能,中國的大學沒能夠為學生提供這種幫助。

              吳志攀當天在舊金山出席北京大學北加州校友會舉辦的20周年年會暨未名論壇,就“傳承與發展:你心目中的北大人、北大精神和社會責任感”主題與300多位tb校友交流。

              吳志攀表示,目前中國高等教育面臨兩個問題。一是世界進入了亞太時代,許多學者均認同亞太時代的特點就是多元化。“高等教育如何適應并培養多元化人才,如何重新設計、教以后10年都用得著的課程,如何滿足學生的期待,是我們遇到的很大問題。”

              如何應對世界的迅速變化,培養學生適應經濟轉型,是中國高等教育遇到的第二個問題。對于學術的敏感性、靈活性、創新型能否適應加速發展的社會需求,吳志攀表示擔憂。

              他指出,“學術自由,兼容并包”的北大精神與亞太時代的精神是融合的,就是多元化、寬容和包容。“蔡元培校長老早就提出‘兼容并包’,在今天更要求我們有包容心態,借鑒國外先進經驗,理解他們的文化和做法,培養多元化人才。”

              吳志攀認為,靈活的創造力是中國大學生最缺少的才能。“我們注意到,美國最成功的IT人士,比如比爾?蓋茨、扎克伯格等,大學沒畢業就創業,顯然他們的成功與大學的多元化相關。”

              吳志攀指出,中國的大學沒能給學生提供這種幫助,或者幫助得不夠,同時,要中國的社會和家庭接納也需要時間。例如,美國允許大學生休學一個、兩個學期,有人利用這個機會做自己有興趣的事情。“而我們的大學只有學生生病才可以休學。”

              當天, 北京大學北加州校友會還向在硅谷培訓企業領導能力的徐玲和發起海外華人互助會的林世東頒發了杰出校友“社區貢獻獎”。
            posted @ 2012-06-13 15:06 tbwshc 閱讀(142) | 評論 (0)編輯 收藏

            package downloadMap;

            import java.io.BufferedInputStream;
            import java.io.IOException;
            import java.io.BufferedOutputStream;
            import java.io.BufferedReader;
            import java.io.File;
            import java.io.FileInputStream;
            import java.io.FileOutputStream;
            import java.io.InputStream;
            import java.io.InputStreamReader;
            import java.io.OutputStream;
            import java.text.DateFormat;
            import java.text.SimpleDateFormat;
            import java.util.ArrayList;
            import java.util.Date;
            import java.util.Enumeration;
            import java.util.List;
            import java.util.zip.ZipEntry;
            import java.util.zip.ZipFile;
            import java.util.zip.ZipOutputStream;
            import javax.swing.JOptionPane;
            import javax.swing.ProgressMonitorInputStream;
            import TIp.Jframet;
            import doFile.ZipToFile;
            import sun.net.TelnetInputStream;
            import sun.net.TelnetOutputStream;
            import sun.net.ftp.FtpClient;
            public class FtpZipOption {
            /**
              *
              * 從ftp服務器下載zip文件
              *
              * @param
              *
              *@throws Exception
              **/
            public static void downLoadZipFile(String wantFileName ) throws Exception {
            /*new Thread(){
              public void run(){
               Jframet jf = new Jframet("正在鏈接ftp,qing'shao'h", "確定");
               jf.jb.setVisible(false);
              }
            }.start();*/
              String[] localFileNameArray = getLocalFileNameArray("c:\\BaseMap");// 得到所有已經下載的tb文件名
              File downFile=new File("c:\\BaseMap");
              if(!downFile.exists()) downFile.mkdir();
              
              if (ifToDownLoadFile(wantFileName, localFileNameArray)) {// 判斷是否需要下載
               String str; // 輸出信息字符串
               /**
                *
                * 和服務器建立連接
                */
               FtpClient ftp = new FtpClient("172.18.2.66"); // 根據服務器ip建立連接
               
               //JOptionPane.showMessageDialog(null, "");
               str = ftp.getResponseString(); // 獲得響應信息
               System.out.println("連接服務器:" + str);
               
               /**
                *
                * 登陸到Ftp服務器
                */
               ftp.login("admin", "1"); // 根據用戶名和密碼登錄服務器
               str = ftp.getResponseString();
               System.out.println("登錄:" + str);
               
               
               /**
                *
                * 打開并定位到服務器目錄
                */
               ftp.cd("mapdata2"); // 打開服務器上的文件目錄
               str = ftp.getResponseString();
               System.out.println("打開服務器目錄:" + str);
               ftp.binary();// 轉化為二進制的文件
               TelnetInputStream ftpIn = ftp.get(wantFileName + ".zip");// 找到要讀取的文件
               
               /*Jframet jf=new Jframet("正在下載地圖,請稍候...","確定");
               jf.jb.setVisible(false);*/
               long fileLength=FtpZipOption.getFileSize("wantFileName.zip");
               System.out.println(fileLength);
               byte[] buf = new byte[204800];
               int bufsize = 0;
               File f=new File("c:\\BaseMap");
               if(!f.exists()) f.mkdirs();
               String toLocalPath = "c:\\BaseMap\\" + wantFileName + ".zip";
               /*File f=new File(toLocalPath);
               f.mkdirs();*/
               FileOutputStream ftpOut = new FileOutputStream(toLocalPath);
               int readLine=0;
               while ((bufsize = ftpIn.read(buf, 0, buf.length)) != -1) {
                //System.out.println(bufsize);
                ftpOut.write(buf, 0, bufsize);
                readLine=+bufsize;
               
               }
               System.out.println(readLine);
               ftpOut.close();
               ftpIn.close();
               ZipToFile ziptofile=new ZipToFile();
               
                     String  PostFilePath = "c:\\BaseMap";   
                      ziptofile.zipToFile(toLocalPath, PostFilePath);
              // JOptionPane.showMessageDialog(null, "下載" + wantFileName + ".zip完成!");
               System.out.println("下載" + wantFileName + ".zip完成!");
              /* jf.jl.setText("下載地圖成功");
               jf.jb.setVisible(true);*/
              } else {
               
              }
            }
            public static long getFileSize(String filename) throws IOException {
              String str; // 輸出信息字符串
              /**
               *
               * 和服務器建立連接
               */
              FtpClient ftp = new FtpClient("172.18.2.66"); // 根據服務器ip建立連接
              str = ftp.getResponseString(); // 獲得響應信息
              System.out.println("連接服務器:" + str);
              /**
               *
               * 登陸到Ftp服務器
               */
              ftp.login("admin", "1"); // 根據用戶名和密碼登錄服務器
              str = ftp.getResponseString();
              System.out.println("登錄:" + str);
              /**
               *
               * 打開并定位到服務器目錄
               */
              ftp.cd("mapdata2"); // 打開服務器上的文件目錄
              str = ftp.getResponseString();
              System.out.println("打開服務器目錄:" + str);
              ftp.binary();// 轉化為二進制的文件
              long fileSize = -1;
              String s = "SIZE " + filename + "\r\n";
              ftp.sendServer(s);
              try {
              int status = ftp.readServerResponse();
              if (status == 213) {
              String msg = ftp.getResponseString();
              fileSize = Long.parseLong(msg.substring(3).trim());
              }
              } catch (IOException e) {
              e.printStackTrace();
              }
              return fileSize;
              }
            // 上傳文件;并返回上傳文件的信息
            /*private static String upLoadZipToServer(String filename) throws Exception {
              String str; // 輸出信息字符串
              String timeStr = getNowTime();// 獲得當前時間
              String recordStr = "上傳時間:" + timeStr + "\r\n";// 信息記錄字符串
              *//**
               *
               * 和服務器建立連接
               *//*
              FtpClient ftp = new FtpClient("192.168.39.189"); // 根據服務器ip建立連接
              str = ftp.getResponseString(); // 獲得響應信息
              System.out.println(str);
              recordStr += "連接服務器:" + str + "\r\n";
              *//**
               *
               * 登陸到Ftp服務器
               *//*
              ftp.login("test", "test"); // 根據用戶名和密碼登錄服務器
              str = ftp.getResponseString();
              System.out.println(str);
              recordStr += "登錄:" + str + "\r\n";
              *//**
               *
               * 打開并定位到test目錄
               *//*
              ftp.cd("uptest"); // 打開服務器上的test文件夾
              ftp.binary();// 轉化為二進制的文件
              str = ftp.getResponseString();
              System.out.println(str);
              FileInputStream is = null;
              TelnetOutputStream os = null;
              try {
               // "upftpfile"用ftp上傳后的新文件名
               os = ftp.put("uptest.zip");
               File file_in = new java.io.File(filename);
               if (file_in.length() == 0) {
                return "上傳文件為空!";
               }
               is = new FileInputStream(file_in);
               byte[] bytes = new byte[1024];
               int c;
               while ((c = is.read(bytes)) != -1) {
                os.write(bytes, 0, c);
               }
              } finally {
               if (is != null) {
                is.close();
               }
               if (os != null) {
                os.close();
               }
              }
              return "上傳文件成功!";
            }*/
            /**
              *
              * zip壓縮功能,壓縮sourceFile(文件夾目錄)下所有文件,包括子目錄
              *
              * @param sourceFile
              *            ,待壓縮目錄; toFolerName,壓縮完畢生成的目錄
              *
              * @throws Exception
              */
            /*public static void fileToZip(String sourceFile, String toFolerName)
               throws Exception {
              List fileList = getSubFiles(new File(sourceFile)); // 得到待壓縮的文件夾的所有內容
              ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(
              toFolerName));
              ZipEntry ze = null;
              byte[] buf = new byte[1024];
              int readLen = 0;
              for (int i = 0; i < fileList.size(); i++) { // 遍歷要壓縮的所有子文件
               File file = (File) fileList.get(i);
               System.out.println("壓縮到的文件名:" + file.getName());
               ze = new ZipEntry(getAbsFileName(sourceFile, file));
               ze.setSize(file.length());
               ze.setTime(file.lastModified());
               zos.putNextEntry(ze);
               InputStream is = new BufferedInputStream(new FileInputStream(file));
               while ((readLen = is.read(buf, 0, 1024)) != -1) {
                zos.write(buf, 0, readLen);
               }
               is.close();
              }
              zos.close();
              System.out.println("壓縮完成!");
            }
            *//**
              *
              * 解壓zip文件
              *
              * @param sourceFile
              *            ,待解壓的zip文件; toFolder,解壓后的存放路徑
              *
              * @throws Exception
              **//*
            public static void zipToFile(String sourceFile, String toFolder)
               throws Exception {
              String toDisk = toFolder;// 接收解壓后的存放路徑
              ZipFile zfile = new ZipFile(sourceFile);// 連接待解壓文件
              System.out.println("要解壓的文件是:" + zfile.getName());
              Enumeration zList = zfile.entries();// 得到zip包里的所有元素
              ZipEntry ze = null;
              byte[] buf = new byte[1024];
              while (zList.hasMoreElements()) {
               ze = (ZipEntry) zList.nextElement();
               if (ze.isDirectory()) {
                System.out.println("打開zip文件里的文件夾:" + ze.getName()
                + " skipped...");
                continue;
               }
               System.out.println("zip包里的文件: " + ze.getName() + "\t" + "大小為:"
               + ze.getSize() + "KB");
               // 以ZipEntry為參數得到一個InputStream,并寫到OutputStream中
               OutputStream outputStream = new BufferedOutputStream(
               new FileOutputStream(getRealFileName(toDisk, ze.getName())));
               InputStream inputStream = new BufferedInputStream(zfile
               .getInputStream(ze));
               int readLen = 0;
               while ((readLen = inputStream.read(buf, 0, 1024)) != -1) {
                outputStream.write(buf, 0, readLen);
               }
               inputStream.close();
               outputStream.close();
               System.out.println("已經解壓出:" + ze.getName());
              }
              zfile.close();
            }
            *//**
              *
              * 給定根目錄,返回另一個文件名的相對路徑,用于zip文件中的路徑.
              *
              * @param baseDir
              *            java.lang.String 根目錄
              *
              * @param realFileName
              *            java.io.File 實際的文件名
              *
              * @return 相對文件名
              *//*
            private static String getAbsFileName(String baseDir, File realFileName) {
              File real = realFileName;
              File base = new File(baseDir);
              String ret = real.getName();
              while (true) {
               real = real.getParentFile();
               if (real == null)
                break;
               if (real.equals(base))
                break;
               else
                ret = real.getName() + "/" + ret;
              }
              return ret;
            }
            *//**
              *
              * 取得指定目錄下的所有文件列表,包括子目錄.
              *
              * @param baseDir
              *            File 指定的目錄
              *
              * @return 包含java.io.File的List
              *//*
            private static List<File> getSubFiles(File baseDir) {
              List<File> ret = new ArrayList<File>();
              File[] tmp = baseDir.listFiles();
              for (int i = 0; i < tmp.length; i++) {
               if (tmp.isFile())
                ret.add(tmp);
               if (tmp.isDirectory())
                ret.addAll(getSubFiles(tmp));
              }
              return ret;
            }
            *//**
              *
              * 給定根目錄,返回一個相對路徑所對應的實際文件名.
              *
              * @param zippath
              *            指定根目錄
              *
              * @param absFileName
              *            相對路徑名,來自于ZipEntry中的name
              *
              * @return java.io.File 實際的文件
              *//*
            private static File getRealFileName(String zippath, String absFileName) {
              String[] dirs = absFileName.split("/", absFileName.length());
              File ret = new File(zippath);// 創建文件對象
              if (dirs.length > 1) {
               for (int i = 0; i < dirs.length - 1; i++) {
                ret = new File(ret, dirs);
               }
              }
              if (!ret.exists()) {// 檢測文件是否存在
               ret.mkdirs();// 創建此抽象路徑名指定的目錄
              }
              ret = new File(ret, dirs[dirs.length - 1]);// 根據 ret 抽象路徑名和 child
                         // 路徑名字符串創建一個新 File 實例
              return ret;
            }
            */
            /**
              *
              * 取得ftp服務器上某個目錄下的所有文件名
              *
              * @param ftp
              *            , FtpClient類實例; folderName,服務器的文件夾名
              *
              * @throws Exception
              *
              * @return list 某目錄下文件名列表
              **/
            private static List getServerFileNameList(FtpClient ftp, String folderName)
               throws Exception {
              BufferedReader dr = new BufferedReader(new InputStreamReader(ftp
                .nameList(folderName)));
              List<String> list = new ArrayList<String>();
              String s;
              while ((s = dr.readLine()) != null) {
               list.add(s);
              }
              return list;
            }
            /**
              *
              * 得到已經下載的目錄下的所有文件名的數組
              *
              * @param localPath
              *            本地的下載文件保存路徑
              *
              * @return 該路徑下所有tb文件名
              *
              * **/
            private static String[] getLocalFileNameArray(String localPath) {
              File diskFile = new File(localPath);
              if (diskFile != null) {
               String[] fileNameList = diskFile.list();
               return fileNameList;
              } else {
               return null;
              }
            }
            /**
              *
              *獲得當前系統時間
              */
            /*public static String getNowTime() {
              String timeStr;
              DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
              Date currentTime = new Date(System.currentTimeMillis());
              timeStr = format.format(currentTime);
              return timeStr;
            }
            public static String getWantFileName() throws Exception {
              *//** 得到當前的系統精確時間 **//*
              Date currentTime = new Date(System.currentTimeMillis());
              *//** 接下來得到系統當前的年月日 **//*
              DateFormat df1 = new SimpleDateFormat("yyyyMMdd");
              Date todayDate = new Date(System.currentTimeMillis());
              String todayStr = df1.format(todayDate);// 得到當前的年月日
              *//** 接下來得到四個比較時間的String類型;分別在00點,06點,12點和18點 **//*
              String compareTimeStr1 = todayStr + "00";
              String compareTimeStr2 = todayStr + "06";
              String compareTimeStr3 = todayStr + "12";
              String compareTimeStr4 = todayStr + "18";
              *//** 接下來得到四個比較時間的date類型 **//*
              DateFormat df2 = new SimpleDateFormat("yyyyMMddHH");
              Date compareTime1 = df2.parse(compareTimeStr1);
              Date compareTime2 = df2.parse(compareTimeStr2);
              Date compareTime3 = df2.parse(compareTimeStr3);
              Date compareTime4 = df2.parse(compareTimeStr4);
              *//** 接下來由當前系統時間和四個參照時間進行比較,找出該下載的文件名 **//*
              if (currentTime.after(compareTime1) && currentTime.before(compareTime2)) {
               // 此時應該下載00點的文件,文件名為:compareTimeStr1
               System.out.println("此時要下載的文件名為:" + compareTimeStr1 + ".zip");
               return compareTimeStr1;
              } else if (currentTime.after(compareTime2)
                && currentTime.before(compareTime3)) {
               // 此時應該下載06點的文件,文件名為:compareTimeStr2
               System.out.println("此時要下載的文件名為:" + compareTimeStr2 + ".zip");
               return compareTimeStr2;
              } else if (currentTime.after(compareTime3)
                && currentTime.before(compareTime4)) {
               // 此時應該下載12點的文件,文件名為:compareTimeStr3
               System.out.println("此時要下載的文件名為:" + compareTimeStr3 + ".zip");
               return compareTimeStr3;
              } else if (currentTime.after(compareTime4)) {
               // 此時應該下載18點的文件,文件名為:compareTimeStr4
               System.out.println("此時要下載的文件名為:" + compareTimeStr4 + ".zip");
               return compareTimeStr4;
              } else {
               // nothing to do
               return null;
              }
            }
            */
            /**
              *
              * 判斷此時是否需要下載文件
              *
              * @param wantFileName
              *            ,此時該下載的文件名; localFileNameArray ,本地已經有的文件名
              *
              * @return ture--需要下載; false--本地已經有了,不需要下載
              *
              * **/
            public static boolean ifToDownLoadFile(String wantFileName,
               String[] localFileNameArray) {
              if (wantFileName == null && localFileNameArray == null) {// 當想要下載的文件名獲得失敗
               return false;
              } else if (wantFileName == null && localFileNameArray != null) {// 當想要下載的文件名獲得失敗
               return false;
              } else if (wantFileName != null && localFileNameArray == null) {// 當本地沒有已下載的文件
               return true;
              } else if (wantFileName != null && localFileNameArray != null) {// 當要下載的文件在本地還沒有
               if (localFileNameArray.length > 0) {
                for (int i = 0; i < localFileNameArray.length; i++) {
                 if (localFileNameArray.equals(wantFileName + ".zip")) {
                  return false;
                 }
                }
                return true;
               } else {
                return true;
               }
              } else {
               return false;
              }
            }
            public static void main(String args[]) {
              try {
               long aa=FtpZipOption.getFileSize("2.zip");
               System.out.println(aa);
               FtpZipOption.downLoadZipFile("2");
               
              } catch (Exception e) {
               e.printStackTrace();
              }
            }
            }

            這個是我從網上找的,可是下載2個G的壓縮包打不開,幾百兆的可以!求解
            posted @ 2012-06-13 15:01 tbwshc 閱讀(2196) | 評論 (0)編輯 收藏

            1.如何學習程序設計?

            JAVA是一種平臺,也是一種程序設計語言,如何學好程序設計不僅僅適用于JAVA,TB對C++等其他程序設計語言也一樣管用。有編程高手認為,JAVA也好C也好沒什么分別,拿來就用。為什么他們能達到如此境界?我想是因為編程語言之間有共通之處,領會了編程的精髓,自然能夠做到一通百通。如何學習程序設計理所當然也有許多共通的地方。

            1.1 培養興趣

            興趣是能夠讓你堅持下去的動力。如果只是把寫程序作為謀生的手段的話,你會活的很累,也太對不起自己了。多關心一些行業趣事,多想想蓋茨。不是提倡天天做白日夢,但人要是沒有了夢想,你覺得有味道嗎?可能像許多深圳本地農民一樣,打打麻將,喝喝功夫茶,拜拜財神爺;每個月就有幾萬十幾萬甚至更多的進帳,憑空多出個"食利階層"。你認為,這樣有味道嗎?有空多到一些程序員論壇轉轉,你會發現,他們其實很樂觀幽默,時不時會冒出智慧的火花。

            1.2 慎選程序設計語言

            男怕入錯行,女怕嫁錯郎。初學者選擇程序設計語言需要謹慎對待。軟件開發不僅僅是掌握一門編程語言了事,它還需要其他很多方面的背景知識。軟件開發也不僅僅局限于某幾個領域,而是已經滲透到了各行各業幾乎每一個角落。

            如果你對硬件比較感興趣,你可以學習C語言/匯編語言,進入硬件開發領域。如果你對電信的行業知識及網絡比較熟悉,你可以在C/C++等之上多花時間,以期進入電信軟件開發領域。如果你對操作系統比較熟悉,你可以學習C/Linux等等,為Linux內核開發/驅動程序開發/嵌入式開發打基礎。如果你想介入到應用范圍最廣泛的應用軟件開發(包括電子商務電子政務系統)的話,你可以選擇J2EE或.NET,甚至LAMP組合。每個領域要求的背景知識不一樣。做應用軟件需要對數據庫等很熟悉??傊阈枰鶕约旱奶攸c來選擇合適你的編程語言。

            1.3 要腳踏實地,快餐式的學習不可取

            先分享一個故事。

            有一個小朋友,他很喜歡研究生物學,很想知道那些蝴蝶如何從蛹殼里出來,變成蝴蝶便會飛。有一次,他走到草原上面看見一個蛹,便取了回家,然后看著,過了幾天以后,這個蛹出了一條裂痕,看見里面的蝴蝶開始掙扎,想抓破蛹殼飛出來。這個過程達數小時之久,蝴蝶在蛹里面很辛苦地拼命掙扎,怎么也沒法子走出來。這個小孩看著看著不忍心,就想不如讓我幫幫它吧,便隨手拿起剪刀在蛹上剪開,使蝴蝶破蛹而出。但蝴蝶出來以后,因為翅膀不夠力,變得很臃腫,飛不起來。

            這個故事給我們的啟示是:欲速則不達。

            浮躁是現代人最普遍的心態,能怪誰?也許是貧窮落后了這么多年的緣故,就像當年的大躍進一樣,都想大步跨入共產主義社會?,F在的軟件公司、客戶、政府、學校、培訓機構等等到處彌漫著浮躁之氣。就拿筆者比較熟悉的深圳IT培訓行業來說吧,居然有的打廣告宣稱"參加培訓,100%就業",居然報名的學生不少,簡直是藐視天下程序員。社會環境如是,我們不能改變,只能改變自己,鬧市中的安寧,彌足珍貴。許多初學者C++/JAVA沒開始學,立馬使用VC/JBuilder,會使用VC/JBuilder開發一個HelloWorld程序,就忙不迭的向世界宣告,"我會軟件開發了",簡歷上也大言不慚地寫上"精通VC/JAVA"。結果到軟件公司面試時要么被三兩下打發走了,要么被駁的體無完膚,無地自容。到處碰壁之后才知道捧起《C++編程思想》《JAVA編程思想》仔細鉆研,早知如此何必當初呀。

            "你現在講究簡單方便,你以后的路就長了",好象也是佛經中的勸戒。

            1.4 多實踐,快實踐

            彭端淑的《為學一首示子侄》中有窮和尚與富和尚的故事。

            從前,四川邊境有兩個和尚,一個貧窮,一個有錢。一天,窮和尚對富和尚說:"我打算去南海朝圣,你看怎么樣?"富和尚說:"這里離南海有幾千里遠,你靠什么去呢?"窮和尚說:"我只要一個水缽,一個飯碗就夠了。"富和尚為難地說:"幾年前我就打算買條船去南海,可至今沒去成,你還是別去吧!"一年以后,富和尚還在為租賃船只籌錢,窮和尚卻已經從南海朝圣回來了。

            這個故事可解讀為:任何事情,一旦考慮好了,就要馬上上路,不要等到準備周全之后,再去干事情。假如事情準備考慮周全了再上路的話,別人恐怕捷足先登了。軟件開發是一門工程學科,注重的就是實踐,"君子動口不動手"對軟件開發人員來講根本就是錯誤的,他們提倡"動手至上",但別害怕,他們大多溫文爾雅,沒有暴力傾向,雖然有時候蓬頭垢面的一副"比爾蓋茨"樣。有前輩高人認為,學習編程的秘訣是:編程、編程、再編程,筆者深表贊同。不僅要多實踐,而且要快實踐。我們在看書的時候,不要等到你完全理解了才動手敲代碼,而是應該在看書的同時敲代碼,程序運行的各種情況可以讓你更快更牢固的掌握知識點。

            1.5 多參考程序代碼

            程序代碼是軟件開發最重要的成果之一,其中滲透了程序員的思想與靈魂。許多人被《仙劍奇俠傳》中凄美的愛情故事感動,悲劇的結局更有一種缺憾美。為什么要以悲劇結尾?據說是因為寫《仙劍奇俠傳》的程序員失戀而安排了這樣的結局,他把自己的感覺融入到游戲中,卻讓眾多的仙劍迷扼腕嘆息。

            多多參考代碼例子,對JAVA而言有參考文獻[4.3],有API類的源代碼(JDK安裝目錄下的src.zip文件),也可以研究一些開源的軟件或框架。

            1.6 加強英文閱讀能力

            對學習編程來說,不要求英語,但不能一點不會,。最起碼像JAVAAPI文檔(參考文獻[4.4])這些東西還是要能看懂的,連猜帶懵都可以;旁邊再開啟一個"金山詞霸"??炊嗔司蜁絹碓绞炀?。在學JAVA的同時學習英文,一箭雙雕多好。另外好多軟件需要到英文網站下載,你要能夠找到它們,這些是最基本的要求。英語好對你學習有很大的幫助??谡Z好的話更有機會進入管理層,進而可以成為剝削程序員的"周扒皮"。

            1.7 萬不得已才請教別人

            筆者在ChinaITLab網校的在線輔導系統中解決學生問題時發現,大部分的問題學生稍做思考就可以解決。請教別人之前,你應該先回答如下幾個問題。

            你是否在google中搜索了問題的解決辦法?

            你是否查看了JAVAAPI文檔?

            你是否查找過相關書籍?

            你是否寫代碼測試過?

            如果回答都是"是"的話,而且還沒有找到解決辦法,再問別人不遲。要知道獨立思考的能力對你很重要。要知道程序員的時間是很寶貴的。

            1.8 多讀好書

            書中自有顏如玉。比爾?蓋茨是一個飽讀群書的人。雖然沒有讀完大學,但九歲的時候比爾?蓋茨就已經讀完了所有的百科全書,所以他精通天文、歷史、地理等等各類學科,可以說比爾?蓋茨不僅是當今世界上金錢的首富,而且也可以稱得上是知識的巨富。

            筆者在給學生上課的時候經常會給他們推薦書籍,到后來學生實在忍無可忍開始抱怨,"天吶,這么多書到什么時候才能看完了","學軟件開發,感覺上了賊船"。這時候,我的回答一般是,"別著急,什么時候帶你們去看看我的書房,到現在每月花在技術書籍上的錢400元,這在軟件開發人員之中還只能夠算是中等的",學生當場暈倒。(注:這一部分學生是剛學軟件開發的)

            對于在JAVA開發領域的好書在筆者另外一篇文章中會專門點評。該文章可作為本文的姊妹篇。

            1.9 使用合適的工具

            工欲善其事必先利其器。軟件開發包含各種各樣的活動,需求收集分析、建立用例模型、建立分析設計模型、編程實現、調試程序、自動化測試、持續集成等等,沒有工具幫忙可以說是寸步難行。工具可以提高開發效率,使軟件的質量更高BUG更少。組合稱手的武器。到飛花摘葉皆可傷人的境界就很高了,無招勝有招,手中無劍心中有劍這樣的境界幾乎不可企及。在筆者另外一篇文章中會專門闡述如何選擇合適的工具(該文章也可作為本文的姊妹篇)。

            2.軟件開發學習路線

            兩千多年的儒家思想孔孟之道,中庸的思想透入骨髓,既不冒進也不保守并非中庸之道,而是找尋學習軟件開發的正確路線與規律。

            從軟件開發人員的生涯規劃來講,我們可以大致分為三個階段,軟件工程師→軟件設計師→架構設計師或項目管理師。不想當元帥的士兵不是好士兵,不想當架構設計師或項目管理師的程序員也不是好的程序員。我們應該努力往上走。讓我們先整理一下開發應用軟件需要學習的主要技術。

            A.基礎理論知識,如操作系統、編譯原理、數據結構與算法、計算機原理等,它們并非不重要。如不想成為計算機科學家的話,可以采取"用到的時候再來學"的原則。

            B.一門編程語言,現在基本上都是面向對象的語言,JAVA/C++/C#等等。如果做WEB開發的話還要學習HTML/Jav**ript等等。

            C.一種方法學或者說思想,現在基本都是面向對象思想(OOA/OOD/設計模式)。由此而衍生的基于組件開發CBD/面向方面編程AOP等等。

            D.一種關系型數據庫,ORACLE/SqlServer/DB2/MySQL等等

            E.一種提高生產率的IDE集成開發環境JBuilder/Eclipse/VS.NET等。

            F.一種UML建模工具,用ROSE/VISIO/鋼筆進行建模。

            G.一種軟件過程,RUP/XP/CMM等等,通過軟件過程來組織軟件開發的眾多活動,使開發流程專業化規范化。當然還有其他的一些軟件工程知識。

            H.項目管理、體系結構、框架知識。

            正確的路線應該是:B→C→E→F→G→H。

            還需要補充幾點:

            1).對于A與C要補充的是,我們應該在實踐中逐步領悟編程理論與編程思想。新技術雖然不斷涌現,更新速度令人眼花燎亂霧里看花;但萬變不離其宗,編程理論與編程思想的變化卻很慢。掌握了編程理論與編程思想你就會有撥云見日之感。面向對象的思想在目前來講是相當關鍵的,是強勢技術之一,在上面需要多投入時間,給你的回報也會讓你驚喜。

            2).對于數據庫來說是獨立學習的,這個時機就由你來決定吧。

            3).編程語言作為學習軟件開發的主線,而其余的作為輔線。

            4).軟件工程師著重于B、C、E、D;軟件設計師著重于B、C、E、D、F;架構設計師著重于C、F、H。

            3.如何學習JAVA?

            3.1 JAVA學習路線

            3.1.1 基礎語法及JAVA原理

            基礎語法和JAVA原理是地基,地基不牢靠,猶如沙地上建摩天大廈,是相當危險的。學習JAVA也是如此,必須要有扎實的基礎,你才能在J2EE、J2ME領域游刃有余。參加SCJP(SUN公司認證的JAVA程序員)考試不失為一個好方法,原因之一是為了對得起你交的1200大洋考試費,你會更努力學習,原因之二是SCJP考試能夠讓你把基礎打得很牢靠,它要求你跟JDK一樣熟悉JAVA基礎知識;但是你千萬不要認為考過了SCJP就有多了不起,就能夠獲得軟件公司的青睞,就能夠獲取高薪,這樣的想法也是很危險的。獲得"真正"的SCJP只能證明你的基礎還過得去,但離實際開發還有很長的一段路要走。

            3.1.2 OO思想的領悟

            掌握了基礎語法和JAVA程序運行原理后,我們就可以用JAVA語言實現面向對象的思想了。面向對象,是一種方法學;是獨立于語言之外的編程思想;是CBD基于組件開發的基礎;屬于強勢技術之一。當以后因工作需要轉到別的面向對象語言的時候,你會感到特別的熟悉親切,學起來像喝涼水這么簡單。

            使用面向對象的思想進行開發的基本過程是:

            ●調查收集需求。

            ●建立用例模型。

            ●從用例模型中識別分析類及類與類之間的靜態動態關系,從而建立分析模型。

            ●細化分析模型到設計模型。

            ●用具體的技術去實現。

            ●測試、部署、總結。

            3.1.3 基本API的學習

            進行軟件開發的時候,并不是什么功能都需要我們去實現,也就是經典名言所說的"不需要重新發明輪子"。我們可以利用現成的類、組件、框架來搭建我們的應用,如SUN公司編寫好了眾多類實現一些底層功能,以及我們下載過來的JAR文件中包含的類,我們可以調用類中的方法來完成某些功能或繼承它。那么這些類中究竟提供了哪些方法給我們使用?方法的參數個數及類型是?類的構造器需不需要參數?總不可能SUN公司的工程師打國際長途甚至飄洋過海來告訴你他編寫的類該如何使用吧。他們只能提供文檔給我們查看,JAVADOC文檔(參考文獻4.4)就是這樣的文檔,它可以說是程序員與程序員交流的文檔。

            基本API指的是實現了一些底層功能的類,通用性較強的API,如字符串處理/輸入輸出等等。我們又把它成為類庫。熟悉API的方法一是多查JAVADOC文檔(參考文獻4.4),二是使用JBuilder/Eclipse等IDE的代碼提示功能。

            3.1.4 特定API的學習

            JAVA介入的領域很廣泛,不同的領域有不同的API,沒有人熟悉所有的API,對一般人而言只是熟悉工作中要用到的API。如果你做界面開發,那么你需要學習Swing/AWT/SWT等API;如果你進行網絡游戲開發,你需要深入了解網絡API/多媒體API/2D3D等;如果你做WEB開發,就需要熟悉Servlet等API啦??傊?,需要根據工作的需要或你的興趣發展方向去選擇學習特定的API。

            3.1.5 開發工具的用法

            在學習基礎語法與基本的面向對象概念時,從鍛煉語言熟練程度的角度考慮,我們推薦使用的工具是Editplus/JCreator+JDK,這時候不要急于上手JBuilder/Eclipse等集成開發環境,以免過于關注IDE的強大功能而分散對JAVA技術本身的注意力。過了這一階段你就可以開始熟悉IDE了。

            程序員日常工作包括很多活動,編輯、編譯及構建、調試、單元測試、版本控制、維持模型與代碼同步、文檔的更新等等,幾乎每一項活動都有專門的工具,如果獨立使用這些工具的話,你將會很痛苦,你需要在堆滿工具的任務欄上不斷的切換,效率很低下,也很容易出錯。在JBuilder、Eclipse等IDE中已經自動集成編輯器、編譯器、調試器、單元測試工具JUnit、自動構建工具ANT、版本控制工具CVS、DOC文檔生成與更新等等,甚至可以把UML建模工具也集成進去,又提供了豐富的向導幫助生成框架代碼,讓我們的開發變得更輕松。應該說IDE發展的趨勢就是集成軟件開發中要用到的幾乎所有工具。

            從開發效率的角度考慮,使用IDE是必經之路,也是從一個學生到一個職業程序員轉變的里程碑。

            JAVA開發使用的IDE主要有Eclipse、JBuilder、JDeveloper、NetBeans等幾種;而Eclipse、JBuilder占有的市場份額是最大的。JBuilder在近幾年來一直是JAVA集成開發環境中的霸主,它是由備受程序員尊敬的Borland公司開發,在硝煙彌漫的JAVAIDE大戰中,以其快速的版本更新擊敗IBM的VisualAgeforJAVA等而成就一番偉業。IBM在VisualAgeforJAVA上已經無利可圖之下,干脆將之貢獻給開源社區,成為Eclipse的前身,真所謂"柳暗花明又一村"。浴火重生的Eclipse以其開放式的插件擴展機制、免費開源獲得廣大程序員(包括幾乎所有的骨灰級程序員)的青睞,極具發展潛力。

            3.1.6 學習軟件工程

            對小型項目而言,你可能認為軟件工程沒太大的必要。隨著項目的復雜性越來越高,軟件工程的必要性才會體現出來。參見"軟件開發學習路線"小節。

            3.2 學習要點

            確立的學習路線之后,我們還需要總結一下JAVA的學習要點,這些要點在前文多多少少提到過,只是筆者覺得這些地方特別要注意才對它們進行匯總,不要嫌我婆婆媽媽啊。

            3.2.1 勤查API文檔

            當程序員編寫好某些類,覺得很有成就感,想把它貢獻給各位苦難的同行。這時候你要使用"javadoc"工具(包含在JDK中)生成標準的JAVADOC文檔,供同行使用。J2SE/J2EE/J2ME的DOC文檔是程序員與程序員交流的工具,幾乎人手一份,除了菜鳥之外。J2SEDOC文檔官方下載地址:,你可以到google搜索CHM版本下載。也可以在線查看:

            對待DOC文檔要像毛主席語錄,早上起床念一遍,吃飯睡覺前念一遍。

            當需要某項功能的時候,你應該先查相應的DOC文檔看看有沒有現成的實現,有的話就不必勞神費心了直接用就可以了,找不到的時候才考慮自己實現。使用步驟一般如下:

            ●找特定的包,包一般根據功能組織。

            ●找需要使用類,類命名規范的話我們由類的名字可猜出一二。

            ●選擇構造器,大多數使用類的方式是創建對象。

            ●選擇你需要的方法。

            3.2.2 查書/google->寫代碼測試->查看源代碼->請教別人

            當我們遇到問題的時候該如何解決?

            這時候不要急著問別人,太簡單的問題,沒經過思考的問題,別人會因此而瞧不起你??梢韵日艺視絞oogle中搜一下看看,絕大部分問題基本就解決了。而像"某些類/方法如何使用的問題",DOC文檔就是答案。對某些知識點有疑惑是,寫代碼測試一下,會給你留下深刻的印象。而有的問題,你可能需要直接看API的源代碼驗證你的想法。萬不得已才去請教別人。

            3.2.3 學習開源軟件的設計思想

            JAVA領域有許多源代碼開放的工具、組件、框架,JUnit、ANT、Tomcat、Struts、Spring、Jive論壇、PetStore寵物店等等多如牛毛。這些可是前輩給我們留下的瑰寶呀。入寶山而空手歸,你心甘嗎?對這些工具、框架進行分析,領會其中的設計思想,有朝一日說不定你也能寫一個XXX框架什么的,風光一把。分析開源軟件其實是你提高技術、提高實戰能力的便捷方法。

            3.2.4 規范的重要性

            沒有規矩,不成方圓。這里的規范有兩層含義。第一層含義是技術規范,多到下載JSRXXX規范,多讀規范,這是最權威準確最新的教材。第二層含義是編程規范,如果你使用了大量的獨特算法,富有個性的變量及方法的命名方式;同時,沒給程序作注釋,以顯示你的編程功底是多么的深厚。這樣的代碼別人看起來像天書,要理解談何容易,更不用說維護了,必然會被無情地掃入垃圾堆。JAVA編碼規范到此查看或下載,中文的也有,啊,還要問我在哪,請參考3.2.2節。

            3.2.5 不局限于JAVA

            很不幸,很幸運,要學習的東西還有很多。不幸的是因為要學的東西太多且多變,沒時間陪老婆家人或女朋友,導致身心疲憊,嚴重者甚至導致抑郁癥。幸運的是別人要搶你飯碗絕非易事,他們或她們需要付出很多才能達成心愿。

            JAVA不要孤立地去學習,需要綜合學習數據結構、OOP、軟件工程、UML、網絡編程、數據庫技術等知識,用橫向縱向的比較聯想的方式去學習會更有效。如學習JAVA集合的時候找數據結構的書看看;學JDBC的時候復習數據庫技術;采取的依然是"需要的時候再學"的原則。

            posted @ 2012-06-11 13:03 tbwshc 閱讀(206) | 評論 (0)編輯 收藏

            一、術語session

              在我的經驗里,session這個詞被濫用的程度大概僅次于transaction,更加有趣的是transaction與session在某些語境下的含義是相同的。

              session,中文經常翻譯為會話,其本來的含義是指有始有終的一系列動作/消息,比如打電話時從拿起電話撥號到掛斷電話這中間的一系列過程可以稱之為一個session。有時候我們可以看到這樣的話“在一個瀏覽器會話期間,...”,這里的會話一詞用的就是其本義,是指從一個瀏覽器窗口打開到關閉這個期間①。最混亂的是“用戶(客戶端)在一次會話期間”這樣一句話,它可能指用戶的一系列動作(一般情況下是同某個具體目的相關的一系列動作,比如從登錄到選購商品到結賬登出這樣一個網上購物的過程,有時候也被稱為一個transaction),然而有時候也可能僅僅是指一次連接,也有可能是指含義①,其中的差別只能靠上下文來推斷②。

              然而當session一詞與網絡協議相關聯時,它又往往隱含了“面向連接”和/或“保持狀態”這樣兩個含義,“面向連接”指的是在通信雙方在通信之前要先建立一個通信的渠道,比如打電話,直到對方接了電話通信才能開始,與此相對的是寫信,在你把信發出去的時候你并不能確認對方的地址是否正確,通信渠道不一定能建立,但對發信人來說,通信已經開始了。“保持狀態”則是指通信的一方能夠把一系列的消息關聯起來,使得消息之間可以互相依賴,比如一個服務員能夠認出再次光臨的老顧客并且記得上次這個顧客還欠店里一塊錢。這一類的例子有“一個TCP session”或者“一個POP3 session”③。

              而到了web服務器蓬勃發展的時代,session在web開發語境下的語義又有了新的擴展,它的含義是指一類用來在客戶端與服務器之間保持狀態的解決方案④。有時候session也用來指這種解決方案的存儲結構,如“把xxx保存在session里”⑤。由于各種用于web開發的語言在一定程度上都提供了對這種解決方案的支持,所以在某種特定語言的語境下,session也被用來指代該語言的解決方案,比如經常把Java里提供的javax.servlet.http.HttpSession簡稱為session⑥。

              鑒于這種混亂已不可改變,本文中session一詞的運用也會根據上下文有不同的含義,請大家注意分辨。

              在本文中,使用中文“瀏覽器會話期間”來表達含義①,使用“session機制”來表達含義④,使用“session”表達含義⑤,使用具體的“HttpSession”來表達含義⑥

              二、HTTP協議與狀態保持

              HTTP協議本身是無狀態的,這與HTTP協議本來的目的是相符的,客戶端只需要簡單的向服務器請求下載某些文件,無論是客戶端還是服務器都沒有必要紀錄彼此過去的行為,每一次請求之間都是獨立的,好比一個顧客和一個自動售貨機或者一個普通的(非會員制)大賣場之間的關系一樣。

              然而聰明(或者貪心?)的人們很快發現如果能夠提供一些按需生成的動態信息會使web變得更加有用,就像給有線電視加上點播功能一樣。這種需求一方面迫使HTML逐步添加了表單、腳本、DOM等客戶端行為,另一方面在服務器端則出現了CGI規范以響應客戶端的動態請求,作為傳輸載體的HTTP協議也添加了文件上載、cookie這些特性。其中cookie的作用就是為了解決HTTP協議無狀態的缺陷所作出的努力。至于后來出現的session機制則是又一種在客戶端與服務器之間保持狀態的解決方案。

              讓我們用幾個例子來描述一下cookie和session機制之間的區別與聯系。筆者曾經常去的一家咖啡店有喝5杯咖啡免費贈一杯咖啡的優惠,然而一次性消費5杯咖啡的機會微乎其微,這時就需要某種方式來紀錄某位顧客的消費數量。想象一下其實也無外乎下面的幾種方案:

              1、該店的店員很厲害,能記住每位顧客的消費數量,只要顧客一走進咖啡店,店員就知道該怎么對待了。這種做法就是協議本身支持狀態。

              2、發給顧客一張卡片,上面記錄著消費的數量,一般還有個有效期限。每次消費時,如果顧客出示這張卡片,則此次消費就會與以前或以后的消費相聯系起來。這種做法就是在客戶端保持狀態。

              3、發給顧客一張會員卡,除了卡號之外什么信息也不紀錄,每次消費時,如果顧客出示該卡片,則店員在店里的紀錄本上找到這個卡號對應的紀錄添加一些消費信息。這種做法就是在服務器端保持狀態。

              由于HTTP協議是無狀態的,而出于種種考慮也不希望使之成為有狀態的,因此,后面兩種方案就成為現實的選擇。具體來說cookie機制采用的是在客戶端保持狀態的方案,而session機制采用的是在服務器端保持狀態的方案。同時我們也看到,由于采用服務器端保持狀態的方案在客戶端也需要保存一個標識,所以session機制可能需要借助于cookie機制來達到保存標識的目的,但實際上它還有其他選擇。

              三、理解cookie機制

              cookie機制的基本原理就如上面的例子一樣簡單,但是還有幾個問題需要解決:“會員卡”如何分發;“會員卡”的內容;以及客戶如何使用“會員卡”。

              正統的cookie分發是通過擴展HTTP協議來實現的,服務器通過在HTTP的響應頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應的cookie。然而純粹的客戶端腳本如JavaScript或者VBScript也可以生成cookie。

              而cookie的使用是由瀏覽器按照一定的原則在后臺自動發送給服務器的。瀏覽器檢查所有存儲的cookie,如果某個cookie所聲明的作用范圍大于等于將要請求的資源所在的位置,則把該cookie附在請求資源的HTTP請求頭上發送給服務器。意思是麥當勞的會員卡只能在麥當勞的店里出示,如果某家分店還發行了自己的會員卡,那么進這家店的時候除了要出示麥當勞的會員卡,還要出示這家店的會員卡。

              cookie的內容主要包括:名字,值,過期時間,路徑和域。

              其中域可以指定某一個域比如.google.com,相當于總店招牌,比如寶潔公司,也可以指定一個域下的具體某臺機器比如www.google.com或者froogle.google.com,可以用飄柔來做比。

              路徑就是跟在域名后面的URL路徑,比如/或者/foo等等,可以用某飄柔專柜做比。

              路徑與域合在一起就構成了cookie的作用范圍。
            如果不設置過期時間,則表示這個cookie的生命期為瀏覽器會話期間,只要關閉瀏覽器窗口,cookie就消失了。這種生命期為瀏覽器會話期的cookie被稱為會話cookie。會話cookie一般不存儲在硬盤上而是保存在內存里,當然這種行為并不是規范規定的。如果設置了過期時間,瀏覽器就會把cookie保存到硬盤上,關閉后再次打開瀏覽器,這些cookie仍然有效直到超過設定的過期時間。

              存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享,比如兩個IE窗口。而對于保存在內存里的cookie,不同的瀏覽器有不同的處理方式。對于IE,在一個打開的窗口上按Ctrl-N(或者從文件菜單)打開的窗口可以與原窗口共享,而使用其他方式新開的IE進程則不能共享已經打開的窗口的內存cookie;對于Mozilla Firefox0.8,所有的進程和標簽頁都可以共享同樣的cookie。一般來說是用javascript的window.open打開的窗口會與原窗口共享內存cookie。瀏覽器對于會話cookie的這種只認cookie不認人的處理方式經常給采用session機制的web應用程序開發者造成很大的困擾。

              下面就是一個goolge設置cookie的響應頭的例子

            HTTP/1.1 302 Found
            Location: http://www.google.com/intl/zh-CN/
            Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
            Content-Type: text/html


              這是使用HTTPLook這個HTTP Sniffer軟件來俘獲的HTTP通訊紀錄的一部分

             

              瀏覽器在再次訪問goolge的資源時自動向外發送cookie


              使用Firefox可以很容易的觀察現有的cookie的值

              使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。

             

              IE也可以設置在接受cookie前詢問


              這是一個詢問接受cookie的對話框。

              四、理解session機制

             session機制是一種服務器端的機制,服務器使用一種類似于散列表的結構(也可能就是使用散列表)來保存信息。

              當程序需要為某個客戶端的請求創建一個session的時候,服務器首先檢查這個客戶端的請求里是否已包含了一個session標識 - 稱為session id,如果已包含一個session id則說明以前已經為此客戶端創建過session,服務器就按照session id把這個session檢索出來使用(如果檢索不到,可能會新建一個),如果客戶端請求不包含session id,則為此客戶端創建一個session并且生成一個與此session相關聯的session id,session id的值應該是一個既不會重復,又不容易被找到規律以仿造的字符串,這個session id將被在本次響應中返回給客戶端保存。

              保存這個session id的方式可以采用cookie,這樣在交互過程中瀏覽器可以自動的按照規則把這個標識發揮給服務器。一般這個cookie的名字都是類似于SEEESIONID,而。比如weblogic對于web應用程序生成的cookie,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。

              由于cookie可以被人為的禁止,必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞回服務器。經常被使用的一種技術叫做URL重寫,就是把session id直接附加在URL路徑的后面,附加方式也有兩種,一種是作為URL路徑的附加信息,表現形式為http://...../xxx;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764另一種是作為查詢字符串附加在URL后面,表現形式為http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
            這兩種方式對于用戶來說是沒有區別的,只是服務器在解析的時候處理的方式不同,采用第一種方式也有利于把session id的信息和正常程序參數區分開來。

              為了在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑后面都包含這個session id。

              另一種技術叫做表單隱藏字段。就是服務器會自動修改表單,添加一個隱藏字段,以便在表單提交時能夠把session id傳遞回服務器。比如下面的表單

             

             

             

              在被傳遞給客戶端之前將被改寫成

             

             
             
              這種技術現在已較少應用,筆者接觸過的很古老的iPlanet6(SunONE應用服務器的前身)就使用了這種技術。實際上這種技術可以簡單的用對action應用URL重寫來代替。

              在談論session機制的時候,常常聽到這樣一種誤解“只要關閉瀏覽器,session就消失了”。其實可以想象一下會員卡的例子,除非顧客主動對店家提出銷卡,否則店家絕對不會輕易刪除顧客的資料。對session來說也是一樣的,除非程序通知服務器刪除一個session,否則服務器會一直保留,程序一般都是在用戶做log off的時候發個指令去刪除session。然而瀏覽器從來不會主動在關閉之前通知服務器它將要關閉,因此服務器根本不會有機會知道瀏覽器已經關閉,之所以會有這種錯覺,是大部分session機制都使用會話cookie來保存session id,而關閉瀏覽器后這個session id就消失了,再次連接服務器時也就無法找到原來的session。如果服務器設置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發出的HTTP請求頭,把原來的session id發送給服務器,則再次打開瀏覽器仍然能夠找到原來的session。

              恰恰是由于關閉瀏覽器不會導致session被刪除,迫使服務器為seesion設置了一個失效時間,當距離客戶端上一次使用session的時間超過這個失效時間時,服務器就可以認為客戶端已經停止了活動,才會把session刪除以節省存儲空間。

              五、理解javax.servlet.http.HttpSession

              HttpSession是Java平臺對session機制的實現規范,因為它僅僅是個接口,具體到每個web應用服務器的提供商,除了對規范支持之外,仍然會有一些規范里沒有規定的細微差異。這里我們以BEA的Weblogic Server8.1作為例子來演示。

              首先,Weblogic Server提供了一系列的參數來控制它的HttpSession的實現,包括使用cookie的開關選項,使用URL重寫的開關選項,session持久化的設置,session失效時間的設置,以及針對cookie的各種設置,比如設置cookie的名字、路徑、域,cookie的生存時間等。

              一般情況下,session都是存儲在內存里,當服務器進程被停止或者重啟的時候,內存里的session也會被清空,如果設置了session的持久化特性,服務器就會把session保存到硬盤上,當服務器進程重新啟動或這些信息將能夠被再次使用,Weblogic Server支持的持久性方式包括文件、數據庫、客戶端cookie保存和復制。

              復制嚴格說來不算持久化保存,因為session實際上還是保存在內存里,不過同樣的信息被復制到各個cluster內的服務器進程中,這樣即使某個服務器進程停止工作也仍然可以從其他進程中取得session。

              cookie生存時間的設置則會影響瀏覽器生成的cookie是否是一個會話cookie。默認是使用會話cookie。有興趣的可以用它來試驗我們在第四節里提到的那個誤解。

              cookie的路徑對于web應用程序來說是一個非常重要的選項,Weblogic Server對這個選項的默認處理方式使得它與其他服務器有明顯的區別。后面我們會專題討論。

              關于session的設置參考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869

              六、HttpSession常見問題

              (在本小節中session的含義為⑤和⑥的混合)

              1、session在何時被創建

              一個常見的誤解是以為session在有客戶端訪問時就被創建,然而事實是直到某server端程序調用HttpServletRequest.getSession(true)這樣的語句時才被創建,注意如果JSP沒有顯示的使用 <%@page session="false"%>關閉session,則JSP文件在編譯成Servlet時將會自動加上這樣一條語句HttpSession session = HttpServletRequest.getSession(true);這也是JSP中隱含的session對象的來歷。

              由于session會消耗內存資源,因此,如果不打算使用session,應該在所有的JSP中關閉它。

              2、session何時被刪除

              綜合前面的討論,session在下列情況下被刪除a.程序調用HttpSession.invalidate();或b.距離上一次收到客戶端發送的session id時間間隔超過了session的超時設置;或c.服務器進程被停止(非持久session)

              3、如何做到在瀏覽器關閉時刪除session

              嚴格的講,做不到這一點。可以做一點努力的辦法是在所有的客戶端頁面里使用javascript代碼window.oncolose來監視瀏覽器的關閉動作,然后向服務器發送一個請求來刪除session。但是對于瀏覽器崩潰或者強行殺死進程這些非常規手段仍然無能為力。

            4、有個HttpSessionListener是怎么回事

              你可以創建這樣的listener去監控session的創建和銷毀事件,使得在發生這樣的事件時你可以做一些相應的工作。注意是session的創建和銷毀動作觸發listener,而不是相反。類似的與HttpSession有關的listener還有HttpSessionBindingListener,HttpSessionActivationListener和HttpSessionAttributeListener。

            5、存放在session中的對象必須是可序列化的嗎

              不是必需的。要求對象可序列化只是為了session能夠在集群中被復制或者能夠持久保存或者在必要時server能夠暫時把session交換出內存。在Weblogic Server的session中放置一個不可序列化的對象在控制臺上會收到一個警告。我所用過的某個iPlanet版本如果session中有不可序列化的對象,在session銷毀時會有一個Exception,很奇怪。

              6、如何才能正確的應付客戶端禁止cookie的可能性

              對所有的URL使用URL重寫,包括超鏈接,form的action,和重定向的URL,具體做法參見[6]
            http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770

              7、開兩個瀏覽器窗口訪問應用程序會使用同一個session還是不同的session

              參見第三小節對cookie的討論,對session來說是只認id不認人,因此不同的瀏覽器,不同的窗口打開方式以及不同的cookie存儲方式都會對這個問題的答案有影響。

              8、如何防止用戶打開兩個瀏覽器窗口操作導致的session混亂

              這個問題與防止表單多次提交是類似的,可以通過設置客戶端的令牌來解決。就是在服務器每次生成一個不同的id返回給客戶端,同時保存在session里,客戶端提交表單時必須把這個id也返回服務器,程序首先比較返回的id與保存在session里的值是否一致,如果不一致則說明本次操作已經被提交過了。可以參看《J2EE核心模式》關于表示層模式的部分。需要注意的是對于使用javascript window.open打開的窗口,一般不設置這個id,或者使用單獨的id,以防主窗口無法操作,建議不要再window.open打開的窗口里做修改操作,這樣就可以不用設置。

              9、為什么在Weblogic Server中改變session的值后要重新調用一次session.setValue
            做這個動作主要是為了在集群環境中提示Weblogic Server session中的值發生了改變,需要向其他服務器進程復制新的session值。

              10、為什么session不見了

              排除session正常失效的因素之外,服務器本身的可能性應該是微乎其微的,雖然筆者在iPlanet6SP1加若干補丁的Solaris版本上倒也遇到過;瀏覽器插件的可能性次之,筆者也遇到過3721插件造成的問題;理論上防火墻或者代理服務器在cookie處理上也有可能會出現問題。

              出現這一問題的大部分原因都是程序的錯誤,最常見的就是在一個應用程序中去訪問另外一個應用程序。我們在下一節討論這個問題。

              七、跨應用程序的session共享

              常常有這樣的情況,一個大項目被分割成若干小項目開發,為了能夠互不干擾,要求每個小項目作為一個單獨的web應用程序開發,可是到了最后突然發現某幾個小項目之間需要共享一些信息,或者想使用session來實現SSO(single sign on),在session中保存login的用戶信息,最自然的要求是應用程序間能夠訪問彼此的session。

              然而按照Servlet規范,session的作用范圍應該僅僅限于當前應用程序下,不同的應用程序之間是不能夠互相訪問對方的session的。各個應用服務器從實際效果上都遵守了這一規范,但是實現的細節卻可能各有不同,因此解決跨應用程序session共享的方法也各不相同。

              首先來看一下Tomcat是如何實現web應用程序之間session的隔離的,從Tomcat設置的cookie路徑來看,它對不同的應用程序設置的cookie路徑是不同的,這樣不同的應用程序所用的session id是不同的,因此即使在同一個瀏覽器窗口里訪問不同的應用程序,發送給服務器的session id也可以是不同的。

             

              根據這個特性,我們可以推測Tomcat中session的內存結構大致如下。

             

              筆者以前用過的iPlanet也采用的是同樣的方式,估計SunONE與iPlanet之間不會有太大的差別。對于這種方式的服務器,解決的思路很簡單,實際實行起來也不難。要么讓所有的應用程序共享一個session id,要么讓應用程序能夠獲得其他應用程序的session id。

              iPlanet中有一種很簡單的方法來實現共享一個session id,那就是把各個應用程序的cookie路徑都設為/(實際上應該是/NASApp,對于應用程序來講它的作用相當于根)。

            /NASApp


              需要注意的是,操作共享的session應該遵循一些編程約定,比如在session attribute名字的前面加上應用程序的前綴,使得setAttribute("name", "neo")變成setAttribute("app1.name", "neo"),以防止命名空間沖突,導致互相覆蓋。


              在Tomcat中則沒有這么方便的選擇。在Tomcat版本3上,我們還可以有一些手段來共享session。對于版本4以上的Tomcat,目前筆者尚未發現簡單的辦法。只能借助于第三方的力量,比如使用文件、數據庫、JMS或者客戶端cookie,URL參數或者隱藏字段等手段。

              我們再看一下Weblogic Server是如何處理session的。

             


              從截屏畫面上可以看到Weblogic Server對所有的應用程序設置的cookie的路徑都是/,這是不是意味著在Weblogic Server中默認的就可以共享session了呢?然而一個小實驗即可證明即使不同的應用程序使用的是同一個session,各個應用程序仍然只能訪問自己所設置的那些屬性。這說明Weblogic Server中的session的內存結構可能如下

             

              對于這樣一種結構,在session機制本身上來解決session共享的問題應該是不可能的了。除了借助于第三方的力量,比如使用文件、數據庫、JMS或者客戶端cookie,URL參數或者隱藏字段等手段,還有一種較為方便的做法,就是把一個應用程序的session放到ServletContext中,這樣另外一個應用程序就可以從ServletContext中取得前一個應用程序的引用。示例代碼如下,

              應用程序A

            context.setAttribute("appA", session);

              應用程序B

            contextA = context.getContext("/appA");
            HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");

              值得注意的是這種用法不可移植,因為根據ServletContext的JavaDoc,應用服務器可以處于安全的原因對于context.getContext("/appA");返回空值,以上做法在Weblogic Server 8.1中通過。

              那么Weblogic Server為什么要把所有的應用程序的cookie路徑都設為/呢?原來是為了SSO,凡是共享這個session的應用程序都可以共享認證的信息。一個簡單的實驗就可以證明這一點,修改首先登錄的那個應用程序的描述符weblogic.xml,把cookie路徑修改為/appA訪問另外一個應用程序會重新要求登錄,即使是反過來,先訪問cookie路徑為/的應用程序,再訪問修改過路徑的這個,雖然不再提示登錄,但是登錄的用戶信息也會丟失。注意做這個實驗時認證方式應該使用FORM,因為瀏覽器和web服務器對basic認證方式有其他的處理方式,第二次請求的認證不是通過session來實現的。具體請參看[7] secion 14.8 Authorization,你可以修改所附的示例程序來做這些試驗。

              八、總結

              session機制本身并不復雜,然而其實現和配置上的靈活性卻使得具體情況復雜多變。這也要求我們不能把僅僅某一次的經驗或者某一個瀏覽器,服務器的經驗當作普遍適用的經驗,而是始終需要具體情況具體分析。

            +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

             

            WEB頁面工具語言XML(一)產生背景 10大城市2萬個熱門IT職位 注冊有獎
              XML同HTML一樣,都來自tandard Generalized Markup Language, 即標準通用標記語言,簡稱SGML.早在Web未發明之前,SGML就早已存在。正如它的名稱所言,SGML是一種用標記來描述文檔資料的通用語言,它包含了一系列的文檔類型定義(簡稱DTD),DTD 中定義了標記的含義,因而 SGML 的語法是可以擴展的。SGML十分龐大,既不容易學,又不容易使用,在計算機上實現也十分困難。鑒于這些因素,Web的發明者——歐洲核子物理研究中心的研究人員根據當時(1989年)計算機技術的能力,提出了HTML語言。

              HTML只使用SGML中很小一部分標記,例如HTML 3.2定義了70種標記。為了便于在計算機上實現,HTML規定的標記是固定的,即HTML語法是不可擴展的,它不需包含DTD.HTML這種固定的語法使它易學易用,在計算機上開發 HTML的瀏覽器也十分容易。正是由于HTML的簡單性,使 Web 技術從計算機界走向全社會,走向千家萬戶,Web的發展如日中天。

              近年來,隨著 Web的應用越來越廣泛和深入,人們漸漸覺得HTML不夠用了,HTML過于簡單的語法嚴重地阻礙了用它來表現復雜的形式。盡管HTML推出了一個又一個新版本,已經有了腳本、表格、幀等表達功能,但始終滿足不了不斷增長的需求。另一方面,這幾年來計算機技術的發展也十分迅速,已經可以實現比當初發明創造HTML時復雜得多的Web瀏覽器,所以開發一種新的Web頁面語言既是必要的,也是可能的。

              有人建議直接使用SGML 作為Web語言,這固然能解決HTML遇到的困難。但是SGML太龐大了,用戶學習和使用不方便尚且不說,要全面實現SGML的瀏覽器就非常困難,于是自然會想到僅使用SGML的子集,使新的語言既方便使用又實現容易。正是在這種形勢下,Web標準化組織W3C建議使用一種精簡的SGML版本——XML應運而生了

             

            WEB頁面工具語言XML(二)定義
              XML是一個精簡的SGML,它將SGML的豐富功能與HTML的易用性結合到Web的應用中。XML保留了SGML的可擴展功能,TB這使XML從根本上有別于HTML.XML要比HTML強大得多,它不再是固定的標記,而是允許定義數量不限的標記來描述文檔中的資料,允許嵌套的信息結構。HTML只是Web顯示數據的通用方法,而XML提供了一個直接處理 Web 數據的通用方法。HTML著重描述Web頁面的顯示格式,而XML著重描述的是Web頁面的內容。

              XML中包括可擴展格式語言XSL(Extensible Style Language) 和可擴展鏈接語言XLL(Extensible Linking Language)。

              XSL用于將XML數據翻譯為HTML或其他格式的語言。XSL提供了一種疊式頁面CSS的功能,使開發者構造出具有表達層結構的Web頁面來,以有別于XML的數據結構。XSL也能和HTML一起構造疊式頁面。XSL可以解釋數量不限的標記,它使Web的版面更豐富多彩,例如動態的文本、跑馬式的文字。此外,XSL還處理多國文字、雙字節的漢字顯示、網格的各種各樣的處理等。

              XLL是XML的鏈接語言,它與HTML的鏈接相似,但功能更強大。XLL支持可擴展的鏈接和多方向的鏈接。它打破了HTML只支持超級文本概念下最簡單的鏈接限制,能支持獨立于地址的域名、雙向鏈路、環路、多個源的集合鏈接等。XLL鏈接可不受文檔制約,完全按用戶要求來指定和管理。

              為了使XML易學易用,XML精簡了一大片SGML難得用一次的功能。正如幾十萬漢字中常用的只不過八千,SGML常用的部分只占20%,XML拋棄了SGML中不常用的部分,使它一下就精簡了80%。這樣一來,XML的語法說明書只有30頁,而SGML卻有500頁。

              XML設計中也考慮了它的易用性,易用性來自兩個方面:一方面用戶編寫Web頁面方便,另一方面設計人員實現XML瀏覽器也不太困難。

              總之,XML使用一個簡單而有靈活的標準格式,為基于Web的應用提供了一個描述數據和交換數據的有效手段。HTML描述了顯示全球數據的通用方法,而XML提供了直接處理全球數據的通用方法

            posted @ 2012-06-11 13:00 tbwshc 閱讀(279) | 評論 (0)編輯 收藏

            僅列出標題
            共10頁: First 2 3 4 5 6 7 8 9 10 
            国产欧美久久久精品影院| 国内精品久久九九国产精品| 狠狠人妻久久久久久综合蜜桃| 亚洲国产精品热久久| 久久婷婷色综合一区二区| 久久人与动人物a级毛片| 奇米综合四色77777久久| 国产亚洲色婷婷久久99精品91| 伊人久久大香线蕉精品不卡| 久久夜色精品国产网站| 91精品国产综合久久香蕉 | 奇米影视7777久久精品| 伊人久久综在合线亚洲2019| 久久精品一区二区三区AV| 99久久亚洲综合精品成人| 久久久久波多野结衣高潮| 久久久久亚洲AV成人网| 久久99精品久久久久子伦| 久久精品视频一| 久久久久久国产精品无码下载 | 婷婷久久综合九色综合98| 狠狠色噜噜色狠狠狠综合久久| 精品国产热久久久福利| 久久久久亚洲AV无码永不| 精品国产乱码久久久久软件| 久久精品国产WWW456C0M| 亚洲一本综合久久| 久久精品国产亚洲沈樵| 成人免费网站久久久| 久久久久人妻精品一区| 久久夜色精品国产噜噜亚洲AV | 亚洲&#228;v永久无码精品天堂久久| 精品国产VA久久久久久久冰| 99久久夜色精品国产网站| 伊人久久大香线蕉无码麻豆| 热RE99久久精品国产66热| 久久婷婷五月综合成人D啪| 国产精品欧美亚洲韩国日本久久| 91精品国产高清久久久久久io| 久久天天躁狠狠躁夜夜网站| 亚洲国产欧洲综合997久久|