Android(java)学习笔记152:采用get请求提交数据到服务器(qq登录案例)
1.GET请求:
组拼url的路径,把提交的数据拼装url的后面,提交给服务器。
缺点:(1)安全性(Android下提交数据组拼隐藏在代码中,不存在安全问题) (2)长度有限不能超过4K(http协议限制),IE浏览器限制至1K
优点:代码方便编写
2.我们首先在电脑模拟下GET请求访问服务器的场景
(1)使用Eclipse 新建一个 " 动态web项目 ",如下:
(2)然后编写一个servlet程序(运行在服务端),命名为" LoginServlet ",如下:
代码内容如下:
1 package com.himi.web; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 import javax.servlet.annotation.WebServlet; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 /** 11 * Servlet implementation class LoginServlet 12 */ 13 @WebServlet("/LoginServlet") 14 public class LoginServlet extends HttpServlet { 15 private static final long serialVersionUID = 1L; 16 17 /** 18 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) 19 */ 20 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 21 String qq = request.getParameter("qq"); 22 String password = request.getParameter("password"); 23 System.out.println("qq:"+qq); 24 System.out.println("password:"+password); 25 26//模拟服务器操作,查询数据库,看qq和密码是否正确. response.getOutputStream()获得一个输出流,向浏览器写入数据(提示数据).这里是输出Output,服务器输出到浏览器 27 if("10086".equals(qq) && "123456".equals(password)) { 28 response.getOutputStream().write("Login Success".getBytes()); 29 }else { 30 response.getOutputStream().write("Login Failed".getBytes()); 31 } 32 33 } 34 35 }
刚刚我们说过GET请求方法,把提交的数据组拼到url路径上提交到服务器上,所以这里request.getParameter();
(3)接下来编写一个jsp动态页面(区别于html静态页面),首先我们说一下jsp和html区别,如下:
jsp和html区别:
•HTML页面是静态页面,也就是事先由用户写好放在服务器上,由web服务器向客户端发送;JSP页面是动态页面,有JSP容器执行该页面的Java代码部分然后实时生成的页面.
•定义上HTML页面是静态页面可以直接运行,JSP页面是动态页它运行时需要转换成servlet。
•HTML能直接打开,jsp只能发布到Tomact等服务器上才能打开
•他们的表头不同,这个是JSP的头“ <%@ page language="java" import="java.util.*" pageEncoding="gbk"%>”在表头中有编码格式和倒入包等。
•很好区分的在jsp中用<%%>就可以写Java代码了,而html没有<%%>。
编写一个login.jsp代码,在WebContent文件夹下:
login.jsp代码如下:
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 2 <html> 3 <head> 4 <%@ page language="java" contentType="text/html; charset=utf-8" 5 pageEncoding="utf-8"%> 6 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 7 <title>???????</title> 8 </head> 9 <body> 10 11 <form action="LoginServlet" method="get"> 12 请输入QQ账号: <input type="text" name="qq"> <br/> 13 请输入QQ密码: <input type="password" name="password"> <br/> 14 <input type="submit" value="登录"> 15 </form> 16 </body> 17 </html>
右击login.jsp文件," 运行方式 "--> " 在服务器上运行 ";运行的结果如下:
(4)打开360浏览器抓包分析http通信的详细过程:
• 打开360浏览器,地址栏输入:http://localhost:8080/web/login.jsp,结果如下:
•随便输入一个QQ账号和QQ密码,输入QQ账号:10000,输入QQ密码:123123,如下:
观察下面,我们可以观察到我们输入的QQ账号和QQ密码已经传输到Tomcat服务器上:
详细的抓取数据包如下:
上面提到的Response的数据长度是12(字符数),如下Response数据内容:
• 输入正确的QQ账号和QQ密码,如下:
由下面我们知道了我们输入的QQ账号和QQ密码已经传输到Tomcat服务器上面:
同样抓取的数据包为:
上面提到的Response的数据长度是13(字符数),如下Response数据内容:
(5)小结:
•首先我们让编写的Servlet的程序在LoginServlet在Tomcat运行,Tomcat则处于一直监听状态(监听外界Http--GET请求),当我们在login.jsp输入QQ账号和QQ密码时候,点击"登录"之后,浏览器会把QQ账号和QQ密码组拼到url路径上,把这些数据提交给Tomcat服务器;
•此时处于监听状态的Tomcat服务器则根据接收到http的请求数据包中提取到QQ账号和QQ密码,比对正确的QQ账号和QQ密码(现实则是比对数据库的QQ账号和QQ密码);
•根据QQ账号和QQ密码匹配状况,反馈不同的信息,匹配成功返回"Login Success" ,反之匹配失败返回"Login Failed";
•返回的数据包被电脑浏览器获取,然后提取出返回数据,显示在浏览上,这样浏览器上就出现了"Login Success"或"Login Failed";
3.Android下模拟出手机GET请求访问远端服务器场景:
(1)新建一个Android工程,命名为”qq登录",下面首先是MainActivity.java:
1 package com.itheima.qqlogin; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.FileOutputStream; 7 import java.io.InputStream; 8 import java.io.InputStreamReader; 9 import java.net.HttpURLConnection; 10 import java.net.MalformedURLException; 11 import java.net.URL; 12 13 import android.app.Activity; 14 import android.os.Bundle; 15 import android.text.TextUtils; 16 import android.util.Log; 17 import android.view.View; 18 import android.widget.CheckBox; 19 import android.widget.EditText; 20 import android.widget.Toast; 21 22 public class MainActivity extends Activity { 23 private static final String Tag = "MainActivity"; 24 private EditText et_qq; 25 private EditText et_pwd; 26 private CheckBox cb_remember; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 //查询关心的控件 33 et_qq = (EditText) findViewById(R.id.et_qq); 34 et_pwd = (EditText) findViewById(R.id.et_pwd); 35 cb_remember = (CheckBox) findViewById(R.id.cb_remember); 36 Log.i(Tag,"oncreate 被调用"); 37 //完成数据的回显。 38 readSavedData(); 39 } 40 //读取保存的数据 41 private void readSavedData() { 42 // getFilesDir() == /data/data/包名/files/ 获取文件的路径 一般系统是不会清理的。 用户手工清理,系统会有提示。 43 // getCacheDir()== /data/data/包名/cache/ 缓存文件的路径 当系统内存严重不足的时候 系统会自动的清除缓存 用户手工清理系统没有提示 44 File file = new File(getFilesDir(),"info.txt"); 45 if(file.exists()&&file.length()>0){ 46 try { 47 //FileInputStream fis = new FileInputStream(file); 48 FileInputStream fis =this.openFileInput("info.txt"); 49 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); 50 //214342###abcdef 51 String info = br.readLine(); 52 String qq = info.split("###")[0]; 53 String pwd = info.split("###")[1]; 54 et_qq.setText(qq); 55 et_pwd.setText(pwd); 56 fis.close(); 57 } catch (Exception e) { 58 e.printStackTrace(); 59 } 60 } 61 } 62 /** 63 * 登陆按钮的点击事件,在点击事件里面获取数据 64 * @param view 65 */ 66 public void login(View view){ 67 final String qq = et_qq.getText().toString().trim(); 68 final String pwd = et_pwd.getText().toString().trim(); 69 if(TextUtils.isEmpty(qq)||TextUtils.isEmpty(pwd)){ 70 Toast.makeText(this, "qq号码或者密码不能为空", 0).show(); 71 return; 72 } 73 //判断用户是否勾选记住密码。 74 if(cb_remember.isChecked()){ 75 //保存密码 76 Log.i(Tag,"保存密码"); 77 try { 78 // File file = new File(getFilesDir(),"info.txt"); 79 // FileOutputStream fos = new FileOutputStream(file); 80 FileOutputStream fos = this.openFileOutput("info.txt", 0); 81 //214342###abcdef 82 fos.write((qq+"###"+pwd).getBytes()); 83 fos.close(); 84 Toast.makeText(this, "保存成功", 0).show(); 85 } catch (Exception e) { 86 e.printStackTrace(); 87 Toast.makeText(this, "保存失败", 0).show(); 88 } 89 }else{ 90 //无需保存密码 91 Log.i(Tag,"无需保存密码"); 92 } 93 94 //登录的操作,网络的请求 95 new Thread() { 96 public void run() { 97 //String path = "http://localhost:8080/web/LoginServlet";这里不能写成localhost 98 try { 99 String path = getString(R.string.serverip); 100 URL url = new URL(path+"?qq="+qq+"&password="+pwd); 101 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 102 conn.setRequestMethod("GET"); 103 int code = conn.getResponseCode(); 104 if(code == 200) { 105 InputStream is = conn.getInputStream(); 106 String result = StreamTools.readStream(is); 107 showToastInAnyThread(result); 108 }else { 109 showToastInAnyThread("请求失败"); 110 } 111 } catch (Exception e) { 112 e.printStackTrace(); 113 showToastInAnyThread("请求失败"); 114 } 115 }; 116 }.start(); 117 118 } 119 120 /** 121 * 显示土司 在主线程更新UI 122 * @param text 123 */ 124 public void showToastInAnyThread(final String text) { 125 runOnUiThread(new Runnable() { 126 127 @Override 128 public void run() { 129 Toast.makeText(MainActivity.this, text, 0).show(); 130 131 } 132 }); 133 } 134 }
其中使用到的工具类StreamTools如下:
1 package com.itheima.qqlogin; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.InputStream; 5 6 /** 7 * 流的工具类 8 * @author Administrator 9 * 10 */ 11 public class StreamTools { 12 /** 13 * 把输入流的内容转换成字符串 14 * @param is 15 * @return null解析失败, string读取成功 16 */ 17 public static String readStream(InputStream is) throws Exception { 18 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 19 byte[] buffer = new byte[1024]; 20 int len = 0; 21 while((len = is.read(buffer)) != -1) { 22 baos.write(buffer, 0, len); 23 } 24 is.close(); 25 String result = baos.toString(); 26 baos.close(); 27 return result; 28 29 30 } 31 }
ByteArrayOutputStream类:是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区
ByteArrayInputStream:的实例向数组中写入或读出byte型数据。
具体用法:
ByteArrayOutputStream: 可以捕获内存缓冲区的数据,转换成字节数组
ByteArrayInputStream: 可以将字节数组转化为输入流
(2)布局文件activity_main.xml:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:gravity="center_horizontal" 5 android:paddingLeft="10dp" 6 android:paddingRight="10dp" 7 android:orientation="vertical" > 8 9 <ImageView 10 android:layout_width="200dip" 11 android:layout_height="200dip" 12 android:src="@drawable/ic_launcher" /> 13 14 <EditText 15 android:id="@+id/et_qq" 16 android:inputType="text" 17 android:layout_width="match_parent" 18 android:layout_height="wrap_content" 19 android:hint="请输入qq号码" /> 20 21 <EditText 22 android:id="@+id/et_pwd" 23 android:layout_width="match_parent" 24 android:layout_height="wrap_content" 25 android:hint="请输入密码" 26 android:inputType="textPassword" /> 27 28 <CheckBox 29 android:id="@+id/cb_remember" 30 android:layout_width="match_parent" 31 android:layout_height="wrap_content" 32 android:text="记住密码" 33 /> 34 35 <Button 36 android:onClick="login" 37 android:layout_width="match_parent" 38 android:layout_height="wrap_content" 39 android:text="登陆" 40 41 /> 42 </LinearLayout>
布局效果图如下:
(3) 布署程序到模拟器上,我们进行演示:
当我们输入QQ账号:10000,QQ密码:123123,效果如下:
点击登录,出现”Login Failed",如下:
与此同时,我们的Tomcat服务器端也接收到了账号和密码:
(4)当我们输入正确的QQ账号和密码时候:
同时我们看到了Tomcat服务器上面,也收到QQ账号和QQ密码的信息,如下:
(5)小结:
我们点击"登录",程序中提取到QQ账号 和 QQ密码,组拼成url网络访问路径,访问Tomcat服务器;
Tomcat服务器得到账号和密码,会在LoginServlet程序中进行比对,如果匹配成功就返回"Login Success",匹配失败就是返回"Login Failed";
返回的信息数据,手机端程序获取之后,以土司的形式打印出来,提示用户登录的情况。