首先给一个需求:用手写的安卓客户端与服务器进行交互,实现登录,登出,修改密码的功能。
在手写Android客户端之前,我们首先要知道客户端是用来向服务端发送请求报文,处理服务端返回的响应报文。
所以,手写一个Android客户端要做的是:
1、获取服务端的地址和接口
2、向服务端发送请求
3、接收服务端返回的响应
一、写出相关的Android页面(android-studio)
activity_login页面(登录页面)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_1"
android:padding="50dp">
<RelativeLayout
android:layout_width="285dp"
android:layout_height="288dp"
android:layout_marginTop="70dp"
android:background="@drawable/selector_orange_radiobutton"
android:padding="20dp">
<EditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="50dp"
android:background="@drawable/bg_username"
android:drawableLeft="@drawable/user"
android:drawablePadding="5dp"
android:hint="用户名"
android:maxLines="1"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="#FFAD33"
android:textColorHint="#AA6600"
android:textSize="16sp" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@id/username"
android:layout_marginTop="15dp"
android:background="@drawable/bg_username"
android:drawableLeft="@drawable/unlock"
android:drawablePadding="5dp"
android:hint="密码"
android:inputType="textPassword"
android:maxLines="1"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="#FFAD33"
android:textColorHint="#AA6600"
android:textSize="16sp" />
<Button
android:id="@+id/btn_1"
android:layout_width="150dp"
android:layout_height="30dp"
android:layout_below="@id/password"
android:layout_marginLeft="50dp"
android:layout_marginTop="37dp"
android:background="@drawable/selector_orange_radiobutton"
android:text="登入"
android:textColor="#FA0002" />
</RelativeLayout>
</RelativeLayout>
activity_main页面(登录后页面)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_1">
<RelativeLayout
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_marginTop="120dp"
android:layout_marginLeft="50dp"
android:padding="20dp"
android:background="@drawable/selector_orange_radiobutton">
<Button
android:id="@+id/btn_1"
android:layout_width="173dp"
android:layout_height="68dp"
android:layout_marginLeft="50dp"
android:layout_marginTop="40dp"
android:background="@drawable/selector_orange_radiobutton"
android:text="修改密码"
android:textColor="#FA0002"
android:textSize="30dp"/>
<Button
android:id="@+id/btn_2"
android:layout_width="174dp"
android:layout_height="67dp"
android:layout_below="@id/btn_1"
android:layout_marginLeft="50dp"
android:layout_marginTop="40dp"
android:background="@drawable/selector_orange_radiobutton"
android:text="登出"
android:textColor="#FA0002"
android:textSize="30dp"/>
</RelativeLayout>
</RelativeLayout>
activity_change(修改密码界面)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_1"
android:padding="50dp">
<RelativeLayout
android:layout_width="285dp"
android:layout_height="288dp"
android:layout_marginTop="70dp"
android:background="@drawable/selector_orange_radiobutton"
android:padding="20dp">
<EditText
android:id="@+id/et_2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:background="@drawable/bg_username"
android:drawableLeft="@drawable/unlock"
android:drawablePadding="5dp"
android:hint="密码"
android:inputType="textPassword"
android:maxLines="1"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:textColor="#FFAD33"
android:textColorHint="#AA6600"
android:textSize="16sp" />
<Button
android:id="@+id/btn_1"
android:layout_width="150dp"
android:layout_height="30dp"
android:layout_below="@id/et_2"
android:layout_marginLeft="50dp"
android:layout_marginTop="37dp"
android:background="@drawable/selector_orange_radiobutton"
android:text="修改密码"
android:textColor="#FA0002" />
</RelativeLayout>
</RelativeLayout>
二、手写http服务器来接收来自客户端的请求,处理请求,将处理结果响应给客户端。
具体步骤可以参考我之前写的 手写一个简易的HTTP服务器 的文章https://segmentfault.com/a/11...
这里给出服务端相关代码:
Server类
public class Server {
/*
* 抛出异常
* */
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
/*
* 启动监听端口
* */
public Server(int port) throws IOException {
if (port < 1 || port >65535){
throw new DemoApplication("端口错误");
}
ServerSocket serverSocket = new ServerSocket(port);
ExecutorService pool = Executors.newFixedThreadPool(50);
System.out.println("已经启动,开始监听端口"+port);
while (true){
Socket clientSocket = serverSocket.accept();
if (!clientSocket.isClosed()){
//首先服务端输出内容到客户端的输入流
Runnable r = () -> {
try {
acceptToClient(clientSocket);
} catch (IOException e) {
throw new RuntimeException(e);
}
};
pool.submit(r);
}
}
}
private void acceptToClient(Socket clientSocket) throws IOException {
//使用 InputStream 来读取浏览器的请求报文
InputStream clientIn = clientSocket.getInputStream();
//使用 BufferedReader 将InputStream强转成 BufferedReader。(InputStream读取到的是字节,BufferedReader读取到的是字符,我们需要的是字符,所以强转成BufferedReader)
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
System.out.println("进入服务器");
OutputStream clientOut = clientSocket.getOutputStream();
String content = "";
String cookieName = "";
String cookieValue = "";
String cookieTime = "";
/*
* 读取第一行要进行特殊处理,从中获取到请求报文中的 方法 和 URL
* */
String firstLine = bufferedReader.readLine();
System.out.println("读取到的第一行是"+firstLine);
String requestUri = firstLine.split(" ")[1];
System.out.println("读取数组中的URL"+requestUri);
String method = firstLine.split(" ")[0];
System.out.println("这次请求的方法是"+method);
String str = "";
String result = "";
/*
* 判断路径是否是addUser2,如果是则进入这个处理逻辑。
* */
if(requestUri.equals("/addUser2")){
System.out.println("进入addUser2");
if(method.equals("POST")){
String d = "";
int e = 0;
//将读取到的请求报文按行读取
while ((str=bufferedReader.readLine())!= null){
d = str.split(" ")[0];
//将Content-Length中的长度提取出来
if(d.equals("Content-Length:")){
e = Integer.parseInt(str.split(" ")[1]);
System.out.println("e"+e);
}
System.out.println("str="+str);
//读取到空行说明请求报文中首部字段已经读取完毕(请求首部字段和内容实体中间隔了一个空行),即将进入内容实体(body)
if (str.equals("")){
System.out.println("读取到空行");
break;
}
}
char[] f = new char[e];
int g = 0;
int h = 0;
int j = e;
//read(暂存区,偏移量,读取的最大长度) 是读取字符放入f缓冲区
/*使用read方法而不是readLine是应为readLean在这里会出现堵塞,会一直卡在这里。
所以使用read方法将剩余的body长度(Content-Length中的长度)读取出来*/
while ((g = bufferedReader.read(f,h,e))!=-1){
h = h +g;
System.out.println(h);
break;
}
//将f暂存区的内容一一输出
for(int i = 0 ; i < h ; i++){
result += f[i];
}
System.out.println("读取到的body是"+result);
}
System.out.println("进入addUser2逻辑处理");
//获取body中的JSON数据
ObjectMapper mapper = new ObjectMapper();
People people = mapper.readValue(result,People.class);
System.out.println(people.userName);
System.out.println(people.passWord);
String userName = people.userName;
String passWord = people.passWord;
//查询数据库
//2.获取SqlSession对象
Sql sql = new Sql();
SqlSession sqlSession = sql.Sql().openSession();
//3.获取Mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//4.执行方法,将数据库中的账号密码与获取到的账号密码进行比对
User users =userMapper.selectAll(userName, passWord);
System.out.println(users);
//释放资源
sqlSession.close();
//即将响应给浏览器的处理结果
if (users != null) {
//将用户名密码通过Set-Cookie的报头告诉浏览器储存这个cookie值
cookieName = "userName";
cookieValue = userName;
cookieTime ="-1";
content = "true";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
}else {
cookieName="NULL";
cookieValue="NULL";
cookieTime="0";
content ="false";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
}
//返回一个Boolean
}
/*
* 判断是否是addUser3,如果是则进入
* */
System.out.println(cookieName);
System.out.println(cookieValue);
if(requestUri.equals("/addUser3")){
System.out.println("进入addUser3方法");
String k = "";
String d = "";
String l = "";
int e = 0;
if(method.equals("POST")){
while ((str=bufferedReader.readLine())!= null){
d = str.split(" ")[0];
//获取Cookie中的值,用于后期判断
if (d.equals("Cookie:")) {
k = str.split(" ")[2];
System.out.println("k" + k);
}
if(d.equals("Content-Length:")){
e = Integer.parseInt(str.split(" ")[1]);
System.out.println("e"+e);
}
System.out.println(str);
if (str.equals("")){
System.out.println("读取到空行");
break;
}
}
char[] f = new char[e];
int g = 0;
int h = 0;
int j = e;
//read 是读取字符放入f缓冲区
while ((g = bufferedReader.read(f,h,e))!=-1){
h = h +g;
System.out.println(h);
break;
}
for(int i = 0 ; i < e ; i++){
result += f[i];
}
System.out.println("读取到的body是"+result);
}
l = k.split("=")[0];
k = k.split("=")[1];
System.out.println("l"+l);
System.out.println("k"+k);
//将获取到的cookie值进行比对,如果一致则进入逻辑
if ("userName".equals(l)) {
System.out.println("成功进入");
String value = k;
System.out.println(l + ":" + value);
//提取JSON中的值
ObjectMapper mapper = new ObjectMapper();
People people = mapper.readValue(result,People.class);
String userName = value;
String passWord = people.passWord;
System.out.println("passWord"+passWord);
//修改密码
User user = new User();
user.setUserName(userName);
user.setPassWord(passWord);
//2.获取SqlSession对象
Sql sql = new Sql();
SqlSession sqlSession = sql.Sql().openSession();
//3.获取Mapper接口的代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//4.执行方法,将数据库中的值进行更改
int update = userMapper.update(user);
System.out.println(update);
//提交事务
sqlSession.commit();
//释放资源
sqlSession.close();
System.out.println(result);
//即将响应给浏览器的内容
if (update !=0) {
content = "true";
cookieName = "userName";
cookieValue = userName;
cookieTime ="-1";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
} else {
content = "false";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
}
}
}
/*
* 判断是否是addUser4,如果是则进入
* */
if(requestUri.equals("/addUser4")){
System.out.println("进入addUser4方法");
String d = "";
String e = "";
if(method.equals("POST")) {
while ((str = bufferedReader.readLine()) != null) {
d = str.split(" ")[0];
if (d.equals("Cookie:")) {
e = str.split(" ")[2];
System.out.println("e" + e);
break;
}
}
}
//从请求首部字段中获取cookie值。
d = e.split("=")[0];
//e = e.split("=")[1];
System.out.println("cookieName="+d);
//如果浏览器中已经存放了这个cookie值
if ("userName".equals(d)) {
System.out.println("响应浏览器,true");
content = "true";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
}else {
System.out.println("响应浏览器,false");
content = "false";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
}
}
/*
* 判断是否是addUser5,如果是则进入
* */
if(requestUri.equals("/addUser5")){
System.out.println("进入addUser5");
String d = "";
String e = "";
if(method.equals("POST")) {
while ((str = bufferedReader.readLine()) != null) {
d = str.split(" ")[0];
if (d.equals("Cookie:")) {
e = str.split(" ")[2];
System.out.println("e" + e);
break;
}
}
}
d = e.split("=")[0];
e = e.split("=")[1];
System.out.println("cookieName="+d);
System.out.println("cookieValue="+e);
cookieName = d;
cookieValue = e;
//将cookie中的生命周期(Max-age)设置成0,让这个cookie值过期。
cookieTime ="0";
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
return;
}
if(requestUri.equals("/favicon.ico")){
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK","favicon.ico");
return;
}
System.out.println("=========================================>>>>>>>>>>>");
//判断是否带路径搜索,没带路径的默认搜索ajax-demo
String resourcePath = requestUri.equals("/") ? "ajax-demo" : requestUri.substring(1);
System.out.println(resourcePath);
//打印获取的html
String a = "";
String htmlStr = "";
try {
BufferedReader in = new BufferedReader(new FileReader("src/webapp/"+resourcePath +".html"));
while ((a = in.readLine()) != null) {
htmlStr =htmlStr + "\n" +a;
}
} catch (IOException e) {
System.out.print("错误");
}
//读取资源的内容
System.out.println(htmlStr);
//找不到资源直接返回404
if (htmlStr == null){
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,404,"Not Found","<h1>404 FILE NOT FOUND</h1>");
return;
}
content = htmlStr;
writeToClient(clientOut,"text/html",cookieName,cookieValue,cookieTime,200,"OK",content);
}
/*
* 封装响应报头(报文)
* */
private void writeToClient(OutputStream clientOut, String ContentType ,String cookieName,String cookieValue,String cookieTime, int responseCode, String responseDes, String content) throws IOException {
clientOut.write(("HTTP/1.1 "+responseCode+ " " +responseDes+ "\r\n").getBytes());
clientOut.write(("Date: " + (new Date()).toString() + "\r\n").getBytes());
clientOut.write(("Content-Type: "+ ContentType +"; charset=UTF-8\r\n").getBytes());
clientOut.write(("Set-Cookie: "+cookieName+"="+cookieValue+";"+"max-age="+cookieTime+"\r\n").getBytes(StandardCharsets.UTF_8));
clientOut.write("\r\n".getBytes());//空行
clientOut.write(content.getBytes());
clientOut.flush();
clientOut.close();
}
public static void main(String[] args) throws IOException {
new Server(12345);
}
}
三、手写Android客户端来向服务端发送请求,处理服务端的响应。
1、给出服务器的端口号12345(port)。和服务器的IP地址(host)
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
}
2、通过构造方法将host和port传入到clientServer方法中,与服务器进行连接。
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
}
3、抛出异常
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
}
4、给出main()方法
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
//主方法
public static void main(String[] args) throws IOException {
new Client().clientServer();
}
}
5、封装请求报文
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
//封装请求报文
public void writeToServerPOST(OutputStream clientOut,String url ,String host,int port,String userName,String cookie) throws IOException {
clientOut.write(("POST" +" "+url+" "+"HTTP/1.1"+"\r\n").getBytes());
clientOut.write(("HOST:"+" "+ host+":"+port+"\r\n").getBytes());
clientOut.write(("Connection: "+"keep-alive"+"\r\n").getBytes());
clientOut.write(("Content-Length: "+userName.getBytes().length+"\r\n").getBytes());
clientOut.write(("Cookie:" +" "+ "Idea-85ee03e6=cf1ca08b-3bd4-4f41-a348-195a76660c7b;"+" "+ cookie + "\r\n").getBytes());
clientOut.write("\r\n".getBytes());
clientOut.write(userName.getBytes());
clientOut.flush();
}
//主方法
public static void main(String[] args) throws IOException {
new Client().clientServer();
}
}
6、发送请求报文
那如何确认用户已经登录了呢?
安卓客户端与浏览器不同,浏览器能够自己提供http请求报文向服务端提交请求,等待服务端欸出响应再展示相关页面,服务端通过添加Set-Cookie响应头,修改其中max-age的值的操作,可以让浏览器"记住"或者"遗忘"这个cookie值。而安卓客户端拿到这个值之后,首先需要自己添加相应请求头Cookie,然后进行相应的逻辑处理才能够"记住"和"遗忘"这个cookie值。至于相应的逻辑处理,我们在Client这个类中,我们通过添加静态变量,来达到"记住"和"遗忘"的效果。
login()方法(登录)
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//添加静态变量,cookie是需要记住的内容,通过操作age来表示"遗忘"
static String cookie= "";
static String age= "";
String url ;
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
//登录方法
public String login(String username ,String password) throws IOException {
url ="/addUser2";
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
//将username和passwordset到Bag对象中
Bag bag = new Bag();
bag.setUserName(username);
bag.setPassWord(password);
//这里加入了jackson的依赖,将Object转换成了JSON
ObjectMapper mapper = new ObjectMapper();
String userName = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,userName,cookie);
InputStream clientIn = socket.getInputStream();
System.out.println("进入读取文件");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
//readResponse是我封装的一个读取服务器的方法,在之后会给出这个方法
String result = readResponse(bufferedReader);
System.out.println("获得Cookie="+cookie);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//封装请求报文
public void writeToServerPOST(OutputStream clientOut,String url ,String host,int port,String userName,String cookie) throws IOException {
clientOut.write(("POST" +" "+url+" "+"HTTP/1.1"+"\r\n").getBytes());
clientOut.write(("HOST:"+" "+ host+":"+port+"\r\n").getBytes());
clientOut.write(("Connection: "+"keep-alive"+"\r\n").getBytes());
clientOut.write(("Content-Length: "+userName.getBytes().length+"\r\n").getBytes());
clientOut.write(("Cookie:" +" "+ "Idea-85ee03e6=cf1ca08b-3bd4-4f41-a348-195a76660c7b;"+" "+ cookie + "\r\n").getBytes());
clientOut.write("\r\n".getBytes());
clientOut.write(userName.getBytes());
clientOut.flush();
}
//主方法
public static void main(String[] args) throws IOException {
new Client().clientServer();
}
}
change()方法(修改密码)
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
//登录方法
public String login(String username ,String password) throws IOException {
url ="/addUser2";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
//将username和passwordset到Bag对象中
Bag bag = new Bag();
bag.setUserName(username);
bag.setPassWord(password);
//这里加入了jackson的依赖,将Object转换成了JSON
ObjectMapper mapper = new ObjectMapper();
String userName = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,userName,cookie);
InputStream clientIn = socket.getInputStream();
System.out.println("进入读取文件");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
//readResponse是我封装的一个读取服务器的方法,在之后会给出这个方法
String result = readResponse(bufferedReader);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//修改密码
public String change(String password) throws IOException, JSONException {
url ="/addUser3";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
Bag bag = new Bag();
bag.setPassWord(password);
ObjectMapper mapper = new ObjectMapper();
String passWord = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,passWord,cookie);
InputStream clientIn = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
String result = readResponse(bufferedReader);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//封装请求报文
public void writeToServerPOST(OutputStream clientOut,String url ,String host,int port,String userName,String cookie) throws IOException {
clientOut.write(("POST" +" "+url+" "+"HTTP/1.1"+"\r\n").getBytes());
clientOut.write(("HOST:"+" "+ host+":"+port+"\r\n").getBytes());
clientOut.write(("Connection: "+"keep-alive"+"\r\n").getBytes());
clientOut.write(("Content-Length: "+userName.getBytes().length+"\r\n").getBytes());
clientOut.write(("Cookie:" +" "+ "Idea-85ee03e6=cf1ca08b-3bd4-4f41-a348-195a76660c7b;"+" "+ cookie + "\r\n").getBytes());
clientOut.write("\r\n".getBytes());
clientOut.write(userName.getBytes());
clientOut.flush();
}
//主方法
public static void main(String[] args) throws IOException {
new Client().clientServer();
}
}
Exit()方法(登出)
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
//登录方法
public String login(String username ,String password) throws IOException {
url ="/addUser2";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
//将username和passwordset到Bag对象中
Bag bag = new Bag();
bag.setUserName(username);
bag.setPassWord(password);
//这里加入了jackson的依赖,将Object转换成了JSON
ObjectMapper mapper = new ObjectMapper();
String userName = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,userName,cookie);
InputStream clientIn = socket.getInputStream();
System.out.println("进入读取文件");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
//readResponse是我封装的一个读取服务器的方法,在之后会给出这个方法
String result = readResponse(bufferedReader);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//修改密码
public String change(String password) throws IOException, JSONException {
url ="/addUser3";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
Bag bag = new Bag();
bag.setPassWord(password);
ObjectMapper mapper = new ObjectMapper();
String passWord = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,passWord,cookie);
InputStream clientIn = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
String result = readResponse(bufferedReader);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//登出方法
public String Exit (String flag) throws IOException {
url ="/addUser5";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
writeToServerPOST(clientOut,url,host,port,"",cookie);
InputStream clientIn = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
String result = readResponse(bufferedReader);
//通过将age的值来删除cookie的值,达到遗忘的效果
if (age.equals("0") || age.equals("")){
cookie = "";
clientOut.close();
flag = "true";
return flag;
}else{
System.out.println("没有删除掉cookie");
flag = "false";
return flag;
}
}
//封装请求报文
public void writeToServerPOST(OutputStream clientOut,String url ,String host,int port,String userName,String cookie) throws IOException {
clientOut.write(("POST" +" "+url+" "+"HTTP/1.1"+"\r\n").getBytes());
clientOut.write(("HOST:"+" "+ host+":"+port+"\r\n").getBytes());
clientOut.write(("Connection: "+"keep-alive"+"\r\n").getBytes());
clientOut.write(("Content-Length: "+userName.getBytes().length+"\r\n").getBytes());
clientOut.write(("Cookie:" +" "+ "Idea-85ee03e6=cf1ca08b-3bd4-4f41-a348-195a76660c7b;"+" "+ cookie + "\r\n").getBytes());
clientOut.write("\r\n".getBytes());
clientOut.write(userName.getBytes());
clientOut.flush();
}
//主方法
public static void main(String[] args) throws IOException {
new Client().clientServer();
}
}
7、封装读取相应报文的方法readResponse()
public class Client {
private int port =12345;
private String host ="你自己服务器的IP地址";
//抛出异常
private static class DemoApplication extends RuntimeException{
public DemoApplication() {
super();
}
public DemoApplication(String message) {
super(message);
}
public DemoApplication(String message, Throwable cause) {
super(message, cause);
}
public DemoApplication(Throwable cause) {
super(cause);
}
protected DemoApplication(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//构造方法
public Client(int port, String host){
this.port = port;
this.host = host;
}
public void clientServer() throws IOException {
if(port < 1||port >65535){
throw new DemoApplication("端口错误");
}
//连接服务器
Socket socket = new Socket(host, port);
}
//登录方法
public String login(String username ,String password) throws IOException {
url ="/addUser2";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
//将username和passwordset到Bag对象中
Bag bag = new Bag();
bag.setUserName(username);
bag.setPassWord(password);
//这里加入了jackson的依赖,将Object转换成了JSON
ObjectMapper mapper = new ObjectMapper();
String userName = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,userName,cookie);
InputStream clientIn = socket.getInputStream();
System.out.println("进入读取文件");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
//readResponse是我封装的一个读取服务器的方法,在之后会给出这个方法
String result = readResponse(bufferedReader);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//修改密码
public String change(String password) throws IOException, JSONException {
url ="/addUser3";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
Bag bag = new Bag();
bag.setPassWord(password);
ObjectMapper mapper = new ObjectMapper();
String passWord = mapper.writeValueAsString(bag);
writeToServerPOST(clientOut,url,host,port,passWord,cookie);
InputStream clientIn = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
String result = readResponse(bufferedReader);
clientOut.close();
System.out.println("结果是"+result);
return result;
}
//登出方法
public String Exit (String flag) throws IOException {
url ="/addUser5";
//连接服务器
Socket socket = new Socket(host,port);
OutputStream clientOut = socket.getOutputStream();
writeToServerPOST(clientOut,url,host,port,"",cookie);
InputStream clientIn = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientIn,"UTF-8"));
String result = readResponse(bufferedReader);
//通过将age的值来删除cookie的值,达到遗忘的效果
if (age.equals("0") || age.equals("")){
cookie = "";
clientOut.close();
flag = "true";
return flag;
}else{
System.out.println("没有删除掉cookie");
flag = "false";
return flag;
}
}
//封装读取响应报文的方法
public String readResponse(BufferedReader bufferedReader) throws IOException {
String rsHttp="false";
String str = "";
String e = "";
String d = "";
//将读取到的请求报文按行读取
while ((str=bufferedReader.readLine())!= null){
System.out.println("rsHttp="+rsHttp);
System.out.println("str="+str);
//e用来拆封响应报文的响应头
e = str.split(" ")[0];
if(e.equals("Set-Cookie:")) {
e = str.split(" ")[1];
System.out.println("Set-Cookie:" + e);
cookie = e.split(";")[0];
System.out.println("Cookie=" + cookie);
d = e.split(";")[1];
System.out.println("max-age="+d);
//age是Set-Cookie中max-age的值
age = d.split("=")[1];
System.out.println("age="+age);
}
//查看响应报文的主体,也就是返回服务端在请求数据库时的成功与否
if (str.equals("true")){
System.out.println("str"+str);
rsHttp = str;
System.out.println("rshttp="+rsHttp);
return rsHttp;
}else if(str.equals("false")) {
System.out.println("str"+str);
rsHttp =str;
System.out.println("rshttp="+rsHttp);
return rsHttp;
}
}
return rsHttp;
}
//封装请求报文
public void writeToServerPOST(OutputStream clientOut,String url ,String host,int port,String userName,String cookie) throws IOException {
clientOut.write(("POST" +" "+url+" "+"HTTP/1.1"+"\r\n").getBytes());
clientOut.write(("HOST:"+" "+ host+":"+port+"\r\n").getBytes());
clientOut.write(("Connection: "+"keep-alive"+"\r\n").getBytes());
clientOut.write(("Content-Length: "+userName.getBytes().length+"\r\n").getBytes());
clientOut.write(("Cookie:" +" "+ "Idea-85ee03e6=cf1ca08b-3bd4-4f41-a348-195a76660c7b;"+" "+ cookie + "\r\n").getBytes());
clientOut.write("\r\n".getBytes());
clientOut.write(userName.getBytes());
clientOut.flush();
}
//主方法
public static void main(String[] args) throws IOException {
new Client().clientServer();
}
}
客户端代码到这里就结束了,接下来写相应的安卓代码(相当于服务端的ajax)
四、给出相应的Android代码
LoginActivity类:
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
private View mBtnLogin;
private EditText mUsername;
private EditText mPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//找到控件
mBtnLogin = findViewById(R.id.btn_1);
mUsername = findViewById(R.id.username);
mPassword = findViewById(R.id.password);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
mBtnLogin.setOnClickListener(this);
}
public void onClick(View view) {
String username = mUsername.getText().toString();
String password = mPassword.getText().toString();
String flag = null;
Client client = new Client();
try {
flag = client.login(username,password);
} catch (IOException e) {
System.out.println("错误");
e.printStackTrace();
}
System.out.println("flag"+flag);
Intent intent = new Intent();
if("true".equals(flag)){
//正确
Toast.makeText(LoginActivity.this,"登入成功",Toast.LENGTH_SHORT).show();
intent = new Intent(LoginActivity.this,MainActivity3.class);
startActivity(intent);
}else{
//错误
Toast.makeText(LoginActivity.this,"密码或账号错误",Toast.LENGTH_SHORT).show();
}
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
}
MainActivity类:
public class MainActivity3 extends AppCompatActivity {
private Button mBtnChange;
private Button mBtnOut;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
mBtnChange = (Button) findViewById(R.id.btn_1);
mBtnChange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity3.this, ChangeActivity.class);
startActivity(intent);
}
});
mBtnOut = (Button) findViewById(R.id.btn_2);
mBtnOut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Client client = new Client();
String flag = "false";
String judgment = "";
try {
judgment= client.Exit(flag);
} catch (IOException e) {
e.printStackTrace();
}
if(judgment.equals("true")){
Toast.makeText(MainActivity3.this,"登出成功",Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity3.this, LoginActivity.class);
startActivity(intent);
}else {
Toast.makeText(MainActivity3.this,"登出失败",Toast.LENGTH_SHORT).show();
}
}
});
}
}
ChangeActivity类:
public class ChangeActivity extends AppCompatActivity implements View.OnClickListener {
private Button mBtnConfirm;
private EditText mPassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change);
mBtnConfirm = (Button) findViewById(R.id.btn_1);
mPassword = findViewById(R.id.et_2);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
.penaltyLog().penaltyDeath().build());
mBtnConfirm.setOnClickListener(this);
}
public void onClick(View view){
String password = mPassword.getText().toString();
Client client = new Client();
String flag = null;
try {
flag = client.change(password);
} catch (IOException | JSONException e) {
e.printStackTrace();
}
Intent intent = null;
if("true".equals(flag)){
Toast.makeText(ChangeActivity.this,"修改成功",Toast.LENGTH_SHORT).show();
intent = new Intent(ChangeActivity.this, MainActivity3.class);
startActivity(intent);
}else{
Toast.makeText(ChangeActivity.this,"修改失败,没有这个用户",Toast.LENGTH_SHORT).show();
}
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
super.onPointerCaptureChanged(hasCapture);
}
}
这样,一个简易的安卓客户端就手写完毕了。
总结:
1、手写完这个客户端可以发现,一个客户端的整体构造和服务端大差不差,十分的相似。只是服务端不仅需要连接客户端,还要等待客户端的请求。而客户端只需要连接服务端然后发请求就完事了,不需要等待。
2、一定要认真的打方法名和路径啊,时间久了真的会忘记的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。