首先给一个需求:用手写的安卓客户端与服务器进行交互,实现登录,登出,修改密码的功能。
在手写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、一定要认真的打方法名和路径啊,时间久了真的会忘记的。


爱摇头的电风扇
7 声望2 粉丝