程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> cookie和session,cookiesession

cookie和session,cookiesession

編輯:JAVA綜合教程

cookie和session,cookiesession


學習目標

會話及其會話技術:

1、會話

 什麼是會話?

  用戶打開浏覽器,訪問一個網站進行一系列操作,關閉浏覽器離開,完整過程 就是會話

 會話過程中要解決的一些問題:

  • 每個用戶與服務器進行交互的過程中,各自會有一些數據,程序要想辦法保存每個用戶的數據;
  • 例如:用戶點擊超鏈接通過一個servlet購買了一個商品,程序應該保存用戶購買的商品,以便用戶點擊結賬servlet時,結賬servlet可以得到用戶商品為用戶結賬;
  • 思考:用戶購買的商品保存在request或servletContext中行不行?
    不可以,原理分析:

2、cookie技術

 一種將用戶信息保存在客戶端技術 ,客戶端會將cookie信息自動發送給服務器。

  

 2.1、cookieAPI介紹

  

  • 代碼示例:

     1 import java.io.IOException;
     2 
     3 import javax.servlet.ServletException;
     4 import javax.servlet.http.Cookie;
     5 import javax.servlet.http.HttpServlet;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 
     9 public class CookieDemo1 extends HttpServlet {
    10 
    11     public void doGet(HttpServletRequest request, HttpServletResponse response)
    12             throws ServletException, IOException {
    13         doPost(request, response);
    14 
    15     }
    16 
    17     public void doPost(HttpServletRequest request, HttpServletResponse response)
    18             throws ServletException, IOException {
    19         //   開始coding................
    20         /**
    21          * 向浏覽器發送cookie 信息 api 演示
    22          */
    23         // 1: 創建一個cookie 對象
    24         // 第一個參數 cookie 名稱 第二個參數 保存該cookie 的值 cookie是不能存儲中文信息
    25         Cookie cookie = new Cookie("cc", "hellocookieCC");
    26         // 默認cookie 會話結束 cookie 失效 會話機制的cookie
    27         cookie.setMaxAge(3600 * 24 * 7);// 設置浏覽器保存cookie 信息的有效時間 持久化cookie
    28         // 2: 發送給浏覽器 response 對象發送cookie
    29         // 3: 設置cookie 訪問的有效路徑
    30         cookie.setPath("/");// cookie 的有效路徑 表示 /day11/aa
    31                                     // 下面的所有目錄都可以訪問該cookie
    32 
    33         // 結論 cookie path 設置 /
    34         // 發送cookie 到 浏覽器
    35         response.addCookie(cookie);
    36 
    37     }
    38 
    39 }

  

 2.2、cookieAPI詳解

  • 讀取cookie
    request.getCookies() 返回Cookie[]
      首先判斷cookies數組是否存在 cookies == null,如果cookies存在,根據cookie的name去查找指定cookie
    代碼示例:
     1 import java.io.IOException;
     2 
     3 import javax.servlet.ServletException;
     4 import javax.servlet.http.Cookie;
     5 import javax.servlet.http.HttpServlet;
     6 import javax.servlet.http.HttpServletRequest;
     7 import javax.servlet.http.HttpServletResponse;
     8 
     9 public class GetCookie extends HttpServlet {
    10 
    11     public void doGet(HttpServletRequest request, HttpServletResponse response)
    12             throws ServletException, IOException {
    13         doPost(request, response);
    14 
    15     }
    16 
    17     public void doPost(HttpServletRequest request, HttpServletResponse response)
    18             throws ServletException, IOException {
    19         //   開始coding................
    20         /**
    21          * 獲取浏覽器保存的cookie 信息 浏覽器發送 .... 浏覽器每次發送請求,都攜帶cookie信息,這是浏覽器本身的機制
    22          */
    23         // 1: 獲取cookie信息 ... 對象 request
    24         Cookie[] cookies = request.getCookies();// 獲取cookie 數組 空?
    25 
    26         for (Cookie cookie : cookies) {
    27             System.out.println(cookie.getName() + "----" + cookie.getValue()
    28                     + "-----" + cookie.getPath());
    29         }
    30 
    31     }
    32 
    33 }
     1 import javax.servlet.http.Cookie;
     2 
     3 public class CookieUtils {
     4 
     5     public static Cookie getCookie(String name, Cookie[] cookies) {
     6         // 通過cookie name 在所有cookie 返回指定 的cookie
     7         if (cookies == null) {
     8             return null;
     9         } else {
    10             for (Cookie cookie : cookies) {
    11                 if (cookie.getName().equals(name)) {
    12                     return cookie;
    13                 }
    14             }
    15         }
    16         return null;
    17     }
    18 
    19 }
  • 服務器向客戶端發送cookie

    cookie對象創建 new Cookie(name,value)

    response.addCookie(cookie) 將cookie發送客戶端 保存到浏覽器中

    * cookie有name和value

     提供三個方法:getName 獲取cookie的名稱

                     getValue 獲取cookie 的值

                     setValue 設置cookie 的值

  • cookie從持久性上分為兩類:會話cookie和持久cookie

    會話cookie:保存在浏覽器內存中,當會話結束浏覽器關閉,會話cookie信息就是丟失;

    持久cookie:保存在浏覽器臨時文件緩存區中cookie(硬盤上) ,當關閉浏覽器結束會話,持久cookie不會被刪除

    * 持久cookie存在過期時間,過期後會自動刪除

    * 持久cookie 需要設置cookie 有效期 setMaxAge
  • cookie訪問有效路徑

    攜帶cookie 必須path一致

    默認 http://localhost/day11/visit 生成cookie ----

    默認path 就是/day11/ (visit資源所在目錄就是默認path)

    * 只有在訪問 http://localhost/day11/ 目錄和子目錄情況下 才會攜帶cookie 信息

    cookie 路徑: 設置Cookie 路徑  /day11/aa 

    表示在此路徑下的所有子目錄下的 都可以獲取該cookie信息

    所以cc目錄下的cc.jsp可以獲取cookie信息 而 bb目錄下的獲取不到cookie 信息

  • 刪除持久cookie
    刪除持久cookie,可以將cookie最大時效設為0,注意,刪除cookie時,path必須一致,否則不會刪除

 2.3、cookie案例之——記錄上次訪問時間案例

  CookieUtils.java——class【參考上面工具類:CookieUtils】

  VisitServlet.java——servlet

 1 import java.io.IOException;
 2 import java.util.Date;
 3 
 4 import javax.servlet.ServletException;
 5 import javax.servlet.http.Cookie;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 import cn.itcast.day11.utils.CookieUtils;
11 
12 public class VisitServlet extends HttpServlet {
13 
14     public void doGet(HttpServletRequest request, HttpServletResponse response)
15             throws ServletException, IOException {
16         doPost(request, response);
17 
18     }
19 
20     public void doPost(HttpServletRequest request, HttpServletResponse response)
21             throws ServletException, IOException {
22         //   開始coding................
23         response.setContentType("text/html;charset=utf-8");
24         // 1: 先獲取 cookie
25         Cookie cookie = CookieUtils
26                 .getCookie("lastvisit", request.getCookies());
27         if (cookie == null) {
28             // 第一次訪問
29             response.getWriter().print("你是第一次訪問!");
30         } else {
31             // cookie 不為空 已經訪問過...
32             String visitTime = cookie.getValue();
33 
34             response.getWriter().print(
35                     "您上次訪問的時間:"
36                             +
37                     new Date(Long.parseLong(visitTime)).toLocaleString());
38 
39         }
40         long time = System.currentTimeMillis();//獲取當前訪問時間
41         cookie = new Cookie("lastvisit", time + "");//設置cookie的值
42         cookie.setMaxAge(3600 * 24 * 3);//設置cookie的有效時間
43         cookie.setPath("/");//設置cookie的有效路徑
44         response.addCookie(cookie);//將cookie發送給客戶端,保存在浏覽器中
45 
46     }
47 
48 }

  *服務端向客戶端寫出一個新的cookie

  默認cookie都是浏覽器內存中進行緩存的 ,當浏覽器關閉,會話結束,內存釋放

 2.4、cookie應用——顯示商品浏覽記錄

  

  • 代碼實現:Product.jsp
 1 <%@page import="cn.itcast.day11.utils.CookieUtils"%>
 2 <%@ page language="java" contentType="text/html; charset=UTF-8"
 3     pageEncoding="UTF-8"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 8 <title>Insert title here</title>
 9 </head>
10 <body>
11 
12   <h2>商品列表</h2>
13   <a href="/day11/look?id=1">mac 電腦</a>
14   <a href="/day11/look?id=2">小米手機3</a>
15   <a href="/day11/look?id=3">雕牌洗衣液2瓶裝</a>
16   <a href="/day11/look?id=4">百事可樂一聽</a>
17   <hr>
18   <h3>商品浏覽記錄</h3>
19   <%
20        //   java  code  .....
21         Cookie cookie =   CookieUtils.getCookie("ids", request.getCookies());
22         if(cookie==null){
23             out.print("你尚未浏覽商品");
24             return;
25         }
26         String[] pros = {"mac 電腦","小米手機3","雕牌洗衣液2瓶裝","百事可樂一聽"};
27         String[] ids  = cookie.getValue().split(",");// 1 3 4
28         for(int i=0;i<ids.length;i++){
29               out.print(pros[Integer.parseInt(ids[i])-1]+"<br>");
30         }
31   %>
32 </body>
33 </html>
  • LookProductServlet.java
 1 import java.io.IOException;
 2 
 3 import javax.servlet.ServletException;
 4 import javax.servlet.http.Cookie;
 5 import javax.servlet.http.HttpServlet;
 6 import javax.servlet.http.HttpServletRequest;
 7 import javax.servlet.http.HttpServletResponse;
 8 
 9 import cn.itcast.day11.utils.CookieUtils;
10 
11 public class LookProductServlet extends HttpServlet {
12 
13     public void doGet(HttpServletRequest request, HttpServletResponse response)
14             throws ServletException, IOException {
15         doPost(request, response);
16 
17     }
18 
19     public void doPost(HttpServletRequest request, HttpServletResponse response)
20             throws ServletException, IOException {
21         //   開始coding................
22         response.setContentType("text/html;charset=utf-8");
23         /**
24          * servlet 存放用戶浏覽商品的id 到cookie 1,2 ,3
25          */
26         String id = request.getParameter("id");// 1
27         // 先判斷 id
28         Cookie cookie = CookieUtils.getCookie("ids", request.getCookies());
29         if (cookie == null) {
30             // 第一查看
31             cookie = new Cookie("ids", id);
32             cookie.setMaxAge(3600 * 24 * 7);
33             cookie.setPath("/");
34         } else {
35             // 商品浏覽 過 判斷 id 在不在已知的ids 裡面
36             String ids[] = cookie.getValue().split(",");// 1,2,3,4,5
37             if (!check(id, ids)) {
38                 // 不 存在 ...
39                 cookie.setValue(cookie.getValue() + "," + id);
40                 cookie.setMaxAge(3600 * 24 * 7);
41                 cookie.setPath("/");
42             }
43 
44         }
45         response.addCookie(cookie);
46         response.getWriter().print("查看成功<a href='/day11/product.jsp'>返回</a>");
47 
48     }
49 
50     private boolean check(String id, String[] ids) {
51         for (String eachId : ids) {
52             if (eachId.equals(id)) {
53                 return true;
54             }
55         }
56         return false;
57     }
58 
59 }

3、session技術 

  Session :一種將用戶信息保存在服務器端技術 ,客戶端會持有Session信息對應key,通過key找到session信息

  * Session 占用服務器空間,安全性更高 ,Cookie節省服務器資源,安全性差一些

 

  Session 將用戶相關信息保存服務器端,服務器會為每個浏覽器創建單獨Session對象,每個用戶各自數據 保存各自浏覽器對應Session對象中。不同用戶獲取到各自浏覽器對應Session 保存數據。

  

  

  

  session依賴cookie機制 實現用戶信息的保存.

  通過JSESSIONID  將sessionId()  發送給浏覽器 使得浏覽器可以保存session id信息

  獲取用戶的session 綁定數據.

  *注意:  默認級別,發送的cookie 機制是會話級別的  浏覽器關閉session對象存在,但是cookie 保存的sessionId 會消失.

 3.1、Session對象創建

  HttpSession 何時創建?  session對象可以在servlet 和jsp 中創建

  •  如果是在jsp  session對象會由容器自動創建
  •  servlet 容器不會自動創建 需要自己手動創建
    HttpSession session =  request,getSession();

  創建好session  容器會自動將seesionId通過cookie 機制  發送給浏覽器保存

   * Session 通過cookie 傳輸 jsessionid 用來在服務器端查找對應Session對象

 3.2、問題:如何實現關掉浏覽器後,再開浏覽器,上次購買的商品還在?

  如何實現關閉浏覽器保留sessionid 信息 ?

  解決方案:修改服務器默認的cookie機制  變成持久化cookie

1 Cookie cookie = new Cookie("JSESSIONID", session.getId());// session對應的cookie信息
2 cookie.setPath("/");
3 cookie.setMaxAge(3600 * 24);//設置sessionId 的有效時間
4 response.addCookie(cookie);//發送給浏覽器保存sessionid 信息
5 //浏覽器獲取的cookie持久化的cookie,下次打開浏覽器就可以在服務器端找到sessio對象 (前提是session對象沒有被銷毀!)

 3.3、Session的生命周期及對象銷毀

  • session對象創建

    *servlet 代碼request.getSession() 執行時創建(當前會話第一次執行)

    *jsp  容器自動創建session 對象

  • session對象銷毀

    1: 自然銷毀,默認30分鐘過期(連續不使用Session對象時間。即:session 如果一直處於休息狀態,容器主動銷毀session)

      默認過期時間在tomcat/conf/web.xml 配置:

    1 <session-config>
    2         <session-timeout>30</session-timeout>
    3 </session-config>

      也可手動設置:

    1 setMaxInactiveInterval(int interval)

    2: 程序員主動銷毀session 對象

    1 session.invalidate();//session 對象存放的數據就沒有了

    3: 非正常關閉服務器(正常關閉服務器Session信息會被序列化到硬盤中 保存tomcat/work目錄)

  • 浏覽器關閉,session對象是否就銷毀了?
    答案:不是,Session保存在服務器端,和浏覽器是否關閉沒有關系 , 關閉浏覽器時刪除會話cookie,丟失jsessionid,沒有jsession無法找到服務器端對應Session

 3.4、問題:session.removeAttribute 和 session.invalidate區別 ?

  • 1 session.removeAttribute();// 刪除當前Session對象中一個屬性值
    2 session.invalidate();// 銷毀當前Session對象,刪除所有屬性

4、session 案例——采用一次性驗證碼(用戶登錄)

  • 驗證碼分析圖:

  

  • 代碼示例:Register.jsp 
 1 <%@ page language="java" contentType="text/html; charset=utf-8"
 2     pageEncoding="utf-8"%>
 3 <html>
 4 <head>
 5 <meta charset="UTF-8">
 6 <title>Insert title here</title>
 7 <script type="text/javascript">
 8     function change() {
 9         document.getElementById('img').src = '/day11/check?'
10                 + new Date().getTime();
11     }
12 </script>
13 </head>
14 <body>
15     <h3>注冊頁面</h3>
16     <h3 >${error }</h3>
17     <form action="/day11/register" method="post">
18         用戶名<input type="text" name="name"><br>
19                 密碼<input type="password" name="password"><br>
20                 驗證碼<input type="text" name="checkCode"><img src="/day11/check"
21      onclick="change();" id="img"><br>
22         <input type="submit" value="注冊">
23     </form>
24 </body>
25 </html>            
  • RegisterServlet.java
 1 import java.io.IOException;
 2 
 3 import javax.servlet.ServletException;
 4 import javax.servlet.http.HttpServlet;
 5 import javax.servlet.http.HttpServletRequest;
 6 import javax.servlet.http.HttpServletResponse;
 7 
 8 public class RegisterServlet extends HttpServlet {
 9 
10     public void doGet(HttpServletRequest request, HttpServletResponse response)
11             throws ServletException, IOException {
12         doPost(request, response);
13 
14     }
15 
16     public void doPost(HttpServletRequest request, HttpServletResponse response)
17             throws ServletException, IOException {
18         //   開始coding................
19         // 獲取表單提交的信息
20         request.setCharacterEncoding("utf-8");
21         String name = request.getParameter("name");
22         String password = request.getParameter("password");
23         String inputCode = request.getParameter("checkCode");
24 
25         // 第一步 驗證驗證碼信息是否正確
26         // 獲取之前session 綁定驗證碼信息 比對
27         String codeSession = (String) request.getSession()
28                 .getAttribute("check");
29         // 一次驗證碼 用完就銷毀...
30         request.getSession().removeAttribute("check");
31         // 比對 用戶輸入的code 和 session code 如果一致...
32         if (codeSession.equals(inputCode)) {
33             // 注冊事情.....
34             response.sendRedirect("/day11/login.jsp");
35 
36         } else {
37             // 驗證碼錯誤 跳回來
38             request.setAttribute("error", "驗證碼錯誤");
39             request.getRequestDispatcher("/register.jsp").forward(request,
40                     response);
41         }
42 
43     }
44 
45 }

  * 一次性驗證碼,當驗證碼生成後,只使用一次,不管成功或者失敗,驗證碼都將失效

  注意:一次驗證碼,用完就銷毀!

5、session 案例——購物車

  • 購物車邏輯圖

  

  • 代碼示例:Product_cart.jsp
 1 <%@page import="cn.itcast.day11.utils.CookieUtils"%>
 2 <%@ page language="java" contentType="text/html; charset=UTF-8"
 3     pageEncoding="UTF-8"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 8 <title>Insert title here</title>
 9 </head>
10 <body>
11   <h2>商品列表</h2>
12   mac電腦<a href="/day11/cart?id=1">&nbsp;購買</a><br>
13  小米手機3 <a href="/day11/cart?id=2">購買</a><br>
14   雕牌洗衣液2瓶裝<a href="/day11/cart?id=3">購買</a><br>
15  百事可樂一聽 <a href="/day11/cart?id=4">購買</a><br>
16   
17   <hr>
18 <a href="/day11/cart.jsp">查看購物車</a>
19 </body>
20 </html>
  • CartServlet.java
 1 import java.io.IOException;
 2 import java.util.HashMap;
 3 import java.util.Map;
 4 
 5 import javax.servlet.ServletException;
 6 import javax.servlet.http.HttpServlet;
 7 import javax.servlet.http.HttpServletRequest;
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 public class CartServlet extends HttpServlet {
11 
12     public void doGet(HttpServletRequest request, HttpServletResponse response)
13             throws ServletException, IOException {
14         doPost(request, response);
15 
16     }
17 
18     public void doPost(HttpServletRequest request, HttpServletResponse response)
19             throws ServletException, IOException {
20         //   開始coding................
21         response.setContentType("text/html;charset=utf-8");
22         String id = request.getParameter("id");// 3 2 3
23         // 通過id --->product
24         String pros[] = { "mac電腦", "小米手機3", "雕牌洗衣液2瓶裝", "百事可樂一聽" };
25         String pro = pros[Integer.parseInt(id) - 1];
26 
27         // 做一個 購物車
28         Map<String, Integer> cart = (Map<String, Integer>) request.getSession()
29                 .getAttribute("cart");
30         // 2: 判斷購物車存在與否?
31         if (cart == null) {
32             // 購物車空
33             cart = new HashMap<String, Integer>();
34             cart.put(pro, 1);
35         } else {
36             // 已經存在購物車
37             if (cart.containsKey(pro)) {
38                 // 表示該購物車已經含有此商品
39                 int count = cart.get(pro);// 得到該購物車商品的數量
40                 // 數量+1
41                 cart.put(pro, count += 1);
42                 // 將已有的商品數量+1
43             } else {
44                 cart.put(pro, 1);
45             }
46 
47         }
48         request.getSession().setAttribute("cart", cart);
49         response.getWriter().print(
50                 "購買成功!<a href='/day11/product_cart.jsp'>返回主頁</a>");
51     }
52 
53 }
  • cart.jsp
 1 <%@page import="java.util.*"%>
 2 <%@ page language="java" contentType="text/html; charset=UTF-8"
 3     pageEncoding="UTF-8"%>
 4 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 5 <html>
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 8 <title>Insert title here</title>
 9 </head>
10 <body>
11    <h3>購物車列表</h3>
12    <!--   如何從session  獲取購物車信息   -->
13      <%
14         //  1session  獲取數據 
15         Map<String,Integer>  cart  = (Map<String,Integer>)session.getAttribute("cart");
16        if(cart!=null){
17                //  2:  如何迭代數據?
18            Set<String>  set =  cart.keySet();
19            Iterator<String> it = set.iterator();
20             while(it.hasNext()){
21                 String  proName = it.next();
22                 int count  = cart.get(proName);
23                 out.print("商品名稱:"+proName+"---數量"+count+"<br>");
24             }
25        }else{
26                out.print("購物車無商品");
27        }
28      %>
29 </body>
30 </html>

6、session、request、ServletContext對象的比較

  • ServletContext 每個web工程對應唯一對象,全局的Servlet上下文對象,允許將數據保存      
    ServletContext --- 所有Servlet共享
  • HttpServletRequest 每次請求產生一個HttpServletRequest對象,允許將數據保存request對象中,結合請求轉發一起使用
    * 當請求結束後,數據就會刪除
  • HttpSession 服務器會為每個客戶端創建單獨Session對象,允許將數據保存session中 ,session保存的是每個用戶各自的數據
    * 用戶之間沒有影響 (用戶購物車、用戶登錄信息 )
  • 小結
    Servlet三種數據訪問范圍:ServletContext 、HttpSession、HttpServletRequest 
    1、保存ServletContext數據 ,在服務器關閉時才會刪除,生命周期最長,全局都可以訪問 (最少使用)
    * 應用場景:網站訪問次數、全局數據庫連接池 需要保存ServletContext
    2、保存HttpSession數據 ,三種情況下丟失 ,主要保存用戶相關數據 
       (不建議存放大規模數據)
    * 應用場景: 用戶登錄信息、購物信息 保存HttpSession
    
    3、保存HttpServletRequest,當前請求發生時產生,響應結束數據立刻釋放 
       (生命周期最短,最建議使用)
    *應用場景: Servlet獲得數據處理結果,通過請求轉發 傳遞信息給JSP顯示 
    
    三種數據范圍提供相同幾個方法
    setAttribute
    getAttribute
    removeAttribute 

本章小結

  1、Cookie和Session區別 ?

     Cookie保存會話信息在客戶端,Session保存會話信息在服務器端,Session基於Cookie實現的

  2、浏覽器關閉後,是不是Session對象就銷毀了 ?

     不是,Session保存在服務器端,關閉浏覽器丟失jsessionid,

     無法找到服務器對應Session對象了,需要等到Session過期後才會銷毀

  3、會話cookie和持久cookie區別 ?

   會話cookie 保存浏覽器內存緩存區中,關閉浏覽器後,會話cookie就會刪除

   持久cookie 保存浏覽器臨時文件區 硬盤上,存在過期時間,當過期後會自動刪除,通過設置maxage為0刪除持久cookie

  4、session的三種銷毀原因?

   服務器非正常關閉、session過期、invalidate

  5、如何實現關閉浏覽器再次打開,Session仍然可以訪問?

   將jsessionid 對應cookie 持久化

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved