在线小说阅读项目

1.案例需求

完成在线小说阅读项目,主要分为以下几个任务:
任务1:创建客户端、服务器端,完成简单通信
任务2:完成登录、注册、退出、返回功能
任务3:完成小说查询列表的功能
任务4:完成在线阅读小说功能
任务5:完成下载小说功能
任务6:完成上传小说功能

2.编程思路

  1. 客户端的编程思路可以概括为以下几个关键步骤:

    • 初始化资源:创建必要的资源,如Scanner用于输入,Socket用于网络通信,以及ObjectInputStream和ObjectOutputStream用于对象的序列化和反序列化。
    • 主菜单展示:编写showFirstMenu方法,展示给用户的主菜单,包括登录、注册和退出选项。
    • 注册功能实现:在register方法中,通过控制台接收用户输入的用户名和密码,创建User对象,并将其封装在Map中,通过Socket发送给服务器。
    • 登录功能实现:在login方法中,接收用户输入的用户名和密码,与注册过程类似,创建User对象并发送至服务器,根据服务器响应判断登录是否成功。
    • 网络通信方法:编写startNet方法来初始化网络连接和相关的输入输出流。编写closeNet方法来关闭网络连接和资源,确保在每个网络操作后都能正确关闭资源。
    • 二级菜单展示:在showSecondMenu方法中,展示给已登录用户的二级菜单,包括小说列表、在线阅读、下载小说和上传小说功能。
    • 小说列表展示:在showNovelList方法中,通过Socket向服务器请求小说列表,接收并展示小说信息。
    • 在线阅读功能:在onlineRead方法中,允许用户选择小说并阅读。需要实现分页阅读,提供上一页和下一页的功能。
    • 下载小说功能:在downLoad方法中,允许用户选择并下载小说,将服务器传来的小说文本写入本地文件。
    • 上传小说功能:在upLoad方法中,允许用户上传本地小说文件到服务器,需要用户输入小说信息和选择本地文件。
    • 错误处理和资源管理:在每个网络操作和文件操作后,使用try-catch-finally结构来处理可能发生的异常,并确保资源(如流和Socket连接)被正确关闭。
    • 用户界面反馈:在每个关键操作后,给予用户明确的反馈信息,如登录成功、注册成功、下载完成等。
    • 循环和条件判断:使用循环和条件判断来处理用户的输入,确保用户能够接收到正确的提示,并能够在操作失败时重新输入。
    • 配置文件加载:使用Properties对象加载配置文件,获取服务器的端口号、文件保存路径等配置信息。
    • 多线程考虑:如果客户端需要处理多个用户的并发操作,考虑使用多线程来实现。
      初始化服务器:创建ServerSocket对象并监听特定端口,等待客户端的连接。
  2. 服务端的编程思路主要包括以下几个关键步骤:

    • 接受客户端连接:在无限循环中调用serverSocket.accept()方法,接受客户端的连接请求,并为每个连接创建一个新的线程。
    • 创建线程处理类:为每个客户端连接创建一个TxtServerThread线程对象,该对象将处理来自客户端的具体请求。
    • 读取和解析用户数据:在TxtServerThread类中,通过ReadeXml方法读取和解析User.xml文件,将用户数据加载到内存中,以便进行登录验证。
    • 网络通信初始化:在TxtServerThread的run方法中,调用startNet方法初始化网络通信,获取输入输出流以及对象输入输出流。
    • 接收客户端请求:从客户端读取请求,通常是一个包含操作码和数据的Map对象。
    • 根据操作码处理请求:根据客户端请求的操作码(如登录、注册、查询小说列表、在线阅读、下载、上传等),调用相应的处理方法。
    • 处理登录和注册请求:在doLogin和doRegister方法中,根据用户请求进行用户验证或注册新用户,并返回相应的响应信息。
    • 处理小说列表请求:在doShowNovelList方法中,解析Book.xml文件,并将小说列表发送给客户端。
    • 处理在线阅读请求:在doOnlineRead方法中,根据客户端请求的页码和小说名,读取小说文本的指定部分并发送给客户端。
    • 处理下载请求:在download方法中,根据客户端请求的小说名,读取服务器上的小说文件内容,并发送给客户端。
    • 处理上传请求:在upload方法中,接收客户端上传的小说信息和文本内容,将信息写入Book.xml文件,并将文本内容保存到服务器。
    • 错误处理和资源管理:在每个网络和文件操作后,使用try-catch-finally结构来处理可能发生的异常,并确保资源(如流和Socket连接)被正确关闭。
    • 关闭网络连接:在closeNet方法中,关闭与客户端的网络连接和相关资源。
    • 多线程管理:确保服务器能够同时处理来自多个客户端的请求,每个请求在独立的线程中处理。
    • 配置文件加载:使用Properties对象加载配置文件,获取服务器的文件保存路径等配置信息。
    • XML操作:使用dom4j库对XML文件进行读取、修改和写入操作。

3.案例源码

Client源码:

package novel2good;

import java.io.*;
import java.net.Socket;
import java.util.*;

/**
 * @Version:1.0
 * @Description: TODO(一句话描述该类的功能)
 * @Date: 2024/7/15 15:49
 * @Author: wjy
 */
public class Client {
    Scanner input = new Scanner(System.in);
    Socket socket = null;
    InputStream is = null;
    OutputStream os = null;
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    List<Book> booksList = new ArrayList<>();
    int page = 1;
    Properties props = new Properties();

    public static void main(String[] args) {
        new Client().showFirstMenu();
    }

    //编写显示一级菜单
    public void showFirstMenu() {
        System.out.println("=======欢迎进入飞思小说网=======");
        System.out.println("\n\n");
        System.out.println("1.登录");
        System.out.println("2.注册");
        System.out.println("0.退出");
        System.out.print("请选择:");
        int choose = input.nextInt();
        switch (choose) {
            case 0:
                System.out.println("感谢使用本软件");
                System.exit(0);
                break;
            case 1:
                login();
                break;
            case 2:
                register();
                break;
            default:
                System.out.println("输入有误,请重新输入!!!");
                showFirstMenu();
                break;

        }
    }

    /**
     * 注册操作
     */
    private void register() {
        //todo 注册待完成
        System.out.print("请输入要注册用户名:");
        String username = input.next();
        System.out.print("请输入要注册的密码:");
        String password = input.next();
     /*   System.out.print("请输入要注册的性别:");
        String sex = input.next();*/
        User user = new User(username, password);
        Map<String, Object> map = new HashMap<>();
        map.put("op", OpCanstant.OP_REGISTER);
        map.put("data", user);
        try {
            //开启网络
            startNet();

            //发送数据
            oos.writeObject(map);
            oos.flush();//刷新,把缓冲区数据写出去
            socket.shutdownOutput();

            //接收服务器的信息
            String reply = ois.readUTF();
            System.out.println("【友情提示】:" + reply);
            if ("注册成功,用户可以登录了".equals(reply)) {
                //注册成功,开始登录
                showFirstMenu();
            } else {
                //注册失败失败,回到一级菜单
                showFirstMenu();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeNet();
        }
    }


    /**
     * 登录操作
     */
    private void login() {
        //输入用户密码
        System.out.print("请输入用户名:");
        String username = input.next();
        System.out.print("请输入密码:");
        String password = input.next();
        User user = new User(username, password);
        Map<String, Object> map = new HashMap<>();
        //操作  1: 称为魔法数字, 可读性差, 在实际开发中,避免魔法数字
        // 使用常量替换
        map.put("op", OpCanstant.OP_LOGIN);
        map.put("data", user);

        try {
            //开启网络
            startNet();

            //发送数据
            oos.writeObject(map);
            oos.flush();//刷新,把缓冲区数据写出去
            socket.shutdownOutput();

            //接收服务器的信息
            String reply = ois.readUTF();
            System.out.println("【友情提示】:" + reply);
            if ("登录成功".equals(reply)) {
                //登录成功,进入二级菜单
                showSecondMenu();
            } else {
                //登录失败,回到一级菜单
                showFirstMenu();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeNet();
        }
    }

    /**
     * 开启网络
     */
    public void startNet() throws IOException {
        //创建Socket对象
        socket = new Socket("127.0.0.1", 9666);
        //得到输入输出流
        is = socket.getInputStream();
        os = socket.getOutputStream();

        //初始化对象流
        oos = new ObjectOutputStream(os);
        ois = new ObjectInputStream(is);
    }

    /**
     * 关闭网络, 关闭资源
     * ctrl + alt + t 包裹代码的快捷键
     */
    public void closeNet() {
        //倒序关 先开的后关
        try {
            if (oos != null) oos.close();
            if (ois != null) ois.close();
            if (socket != null) socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void showSecondMenu() {
        //todo 二级菜单
        System.out.println("===========二级菜单=============");
        System.out.println("1.小说列表");
        System.out.println("2.在线阅读");
        System.out.println("3.下载小说");
        System.out.println("4.上传小说");
        System.out.println("0.返回上一级");
        System.out.print("请选择:");
        int choose = input.nextInt();
        switch (choose) {
            case 0:
                showFirstMenu();
                break;
            case 1:
                showNovelList();
                break;
            case 2:
                onlineRead();
                break;
            case 3:
                downLoad();
                break;
            case 4:
                upLoad();
                break;
            default:
                System.out.println("输入有误,请重新输入!!!");
                showFirstMenu();
                break;

        }
    }

    private void downLoad() {
        String bname=null;
        if (booksList == null || booksList.isEmpty()) {
            System.out.println("该小说列表中没有小说,请先获取小说列表");
            showSecondMenu();
        } else {
            for (Book book : booksList) {
                System.out.println(book.getBookId() + "." + book.getName());
            }
        }
        System.out.println("请选择你要下载的小说:");
        int i = input.nextInt();
        if (i < 1 || i > booksList.size()) {
            System.out.println("超出小说范围啦!");
            System.out.println("========================");
            showSecondMenu();
        }else {
            for (Book book : booksList) {
                if (i==book.getBookId()){
                    bname=book.getName();
                    break;
                }
            }
        }
        Map<String, Object> map = new HashMap<>();
        map.put("op", OpCanstant.OP_DOWNLOAD);
        map.put("data", bname);
        BufferedWriter bw=null;
        try {
            startNet();
            oos.writeObject(map);
            oos.flush();

            map=(Map)ois.readObject();
            String txt = map.get("data").toString();
            props.load(new FileInputStream("src/novel.properties"));
            bw=new BufferedWriter(new FileWriter(props.getProperty("ClientsavePath")+"/"+bname+".txt"));
            StringBuffer stringBuffer = new StringBuffer();
            char[] chars = txt.toCharArray();
            for (int j = 0; j < chars.length; j++) {
                stringBuffer.append(chars[j]);
                if (j%100==0&&j!=0){
                    bw.write(stringBuffer.toString());
                    stringBuffer=stringBuffer.delete(0,101);
                    bw.newLine();
                    bw.flush();
                }
            }
            if (bw!=null)bw.close();
            System.out.println("下载完成");

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
                closeNet();
                showSecondMenu();
        }
    }

    private void upLoad() {
        System.out.println("==========上传小说=========");
        System.out.print("请输入要上传的小说名:");
        String bookname = input.next();
        System.out.print("请输入要上传的小说作者:");
        String author = input.next();
        System.out.print("请输入要上传的小说描述:");
        String desc = input.next();
        System.out.println("请选择类型:1.武侠 2.言情 3.科幻 4.玄幻");
        int typeId = input.nextInt();
        String type = "";
        switch (typeId) {
            case 1:
                type = "武侠";
                break;
            case 2:
                type = "言情";
                break;
            case 3:
                type = "科幻";
                break;
            case 4:
                type = "玄幻";
                break;
            default:
                System.out.println("选择的类型有误");
                showSecondMenu();
        }
        Book book = new Book(bookname, author, desc, type);
        Map<String, Object> map = new HashMap<>();
        map.put("op", OpCanstant.OP_UPLOAD);
        map.put("data", book);
        BufferedReader br = null;
        try {
            startNet();
            props.load(new FileInputStream("src/novel.properties"));
            br = new BufferedReader(new FileReader(props.getProperty("ClientsavePath")+"/" + bookname + ".txt"));
            StringBuffer stringBuffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                stringBuffer.append(line);
            }
            map.put("txt", stringBuffer.toString());
            oos.writeObject(map);
            oos.flush();
            socket.shutdownOutput();

            String reply = ois.readUTF();
            System.out.println(reply);
            socket.shutdownInput();
        } catch (FileNotFoundException e) {
            System.out.println("本地没有该小说文件" );
            showSecondMenu();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (br!=null)br.close();
                closeNet();
                showSecondMenu();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }
    }

    public void secondOnlineRead(String bookName) {
        Map<String, Object> map = new HashMap<>();
        map.put("op", OpCanstant.OP_READ);
        map.put("data", bookName);
        map.put("page", page);
        try {
            //开启网络
            startNet();
            //发送数据
            oos.writeObject(map);
            oos.flush();//刷新,把缓冲区数据写出去
            socket.shutdownOutput();
            //接收服务器的信息
            String s = ois.readUTF();
            System.out.println(s);
            if (s.equals("这已经是最后一页了")) {
                page--;
            }
            System.out.println("==================================");
            socket.shutdownInput();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeNet();
            showPage(bookName);
        }

    }

    public void showPage(String bookName) {
        System.out.println("1.上一页   2.下一页");
        System.out.println("请选择(按除以上选项的任意数字键返回二级菜单:)");
        switch (input.nextInt()) {
            case 1:
                upPage(bookName);
                break;
            case 2:
                downPage(bookName);
                break;
            default:
                page = 1;
                showSecondMenu();
                break;
        }
    }

    private void downPage(String bookName) {
        page++;
        secondOnlineRead(bookName);
    }

    private void upPage(String bookName) {
        if (page == 1) {
            System.out.println("这已经是第一页了");
            showSecondMenu();
        }
        page--;
        secondOnlineRead(bookName);
    }

    public void onlineRead() {
        if (booksList == null || booksList.isEmpty()) {
            System.out.println("该小说列表中没有小说,请先获取小说列表");
            showSecondMenu();
        } else {
            for (Book book : booksList) {
                System.out.println(book.getBookId() + "." + book.getName());
            }
        }
        System.out.println("请选择你要阅读的小说:");
        int i = input.nextInt();
        if (i < 1 || i > booksList.size()) {
            System.out.println(i);
            System.out.println("没有对应的小说,你输入的可能不在范围内。请重新输入");
            onlineRead();
        } else {
            for (Book book : booksList) {
                if (i == book.getBookId()) {
                    String bookName = book.getName();
                    secondOnlineRead(bookName);
                    break;
                }
            }

        }
    }

    public void showNovelList() {
        System.out.println("序号\t\t名称\t\t\t作者\t\t\t类型\t\t\t描述");
        Map<String, Object> map = new HashMap<>();
        map.put("op", OpCanstant.OP_LIST);
        try {
            //开启网络
            startNet();
            //发送数据
            oos.writeObject(map);
            oos.flush();//刷新,把缓冲区数据写出去
            socket.shutdownOutput();

            //接收服务器的信息
            booksList = (List<Book>) ois.readObject();
            if (booksList == null || booksList.isEmpty()) {
                System.out.println("该小说列表中暂无小说数据");
                System.out.println("===============================================================");
                showSecondMenu();
            } else {
                for (Book book : booksList) {
                    System.out.println(book.getBookId() + "\t\t" + book.getName() + "\t\t" + book.getAuthor() + "\t\t" + book.getType() + "\t\t" + book.getDescription());
                }
                socket.shutdownInput();
                System.out.println("===============================================================");
                showSecondMenu();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            closeNet();
        }
    }
}

Serve源码

public class TxtNovelServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9666);
        System.out.println("服务器启动");
        while(true){
            Socket socket = serverSocket.accept(); //阻塞的
            System.out.println("有一个客户端连接了");
            //创建线程对象
            new TxtServerThread(socket).start();
        }
    }
}

ServerThread源码:

package novel2good;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.*;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 服务器的处理客户端请求的线程
 */
public class TxtServerThread extends Thread {
    private Socket socket;
    InputStream is = null;
    OutputStream os = null;
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    private static List<User> users = new ArrayList<>();
    List<Book> books = new ArrayList<>();

    //一旦这个线程类加载, 自动读取User.xml.并解析 保存User集合
    public void ReadeXml() {
        SAXReader saxReader = new SAXReader();
        try {
            //读取xml文件得到Document对象
            Document document = saxReader.read("src/xml/User.xml");
            //得到根元素
            Element rootElement = document.getRootElement();
            //获取user子元素
            List<Element> userEles = rootElement.elements("user");
            //遍历
            for (Element userEle : userEles) {
                //创建一个User对象
                User user = new User();
                //获取元素的属性
                int userId = Integer.parseInt(userEle.attributeValue("userId"));
                user.setUserId(userId);
                //获取user的子元素
                List<Element> childEles = userEle.elements();
                for (Element childEle : childEles) {
                    if (childEle.getName().equalsIgnoreCase("username")) {
                        user.setUsername(childEle.getText());
                    }
                    if (childEle.getName().equalsIgnoreCase("password")) {
                        user.setPassword(childEle.getText());
                    }
                }
                //把User对象保存集合
                users.add(user);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    public TxtServerThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //启动网络
            startNet();
            //接收客户端信息
            Map<String, Object> map = (Map<String, Object>) ois.readObject();
            socket.shutdownInput();
            //获取操作符
            int op = (Integer) map.get("op");
            switch (op) {
                case OpCanstant.OP_LOGIN:
                    doLogin(map);
                    break;
                case OpCanstant.OP_REGISTER:
                    doRegister(map);
                    break;
                case OpCanstant.OP_LIST:
                    doShowNovelList();
                    break;
                case OpCanstant.OP_DOWNLOAD:
                    download(map);
                    break;
                case OpCanstant.OP_UPLOAD:
                    upload(map);
                    break;
                case OpCanstant.OP_READ:
                    doOnlineRead(map);
                    break;
                default:
                    System.out.println("没有这个选项");
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        } finally {
            closeNet();
        }
    }

    public void doOnlineRead(Map<String, Object> map) {
        int page = (int) map.get("page");
        String bookName = (String) map.get("data");
        int skip = (page - 1) * SysCanstant.LIMIT;
        FileReader fr = null;
        try {
            fr = new FileReader("D:/novelServe/" + bookName + ".txt");
            fr.skip(skip);
            StringBuffer stringBuffer = new StringBuffer("");
            int ch = -1;
            for (int i = 0; i < SysCanstant.LIMIT && (ch = fr.read()) != -1; i++) {
                stringBuffer.append((char) ch);
            }
            if (stringBuffer.length() == 0) {
                System.out.println("这已经是最后一页了");
            }
            oos.writeUTF(stringBuffer.toString());
            oos.flush();
            socket.shutdownOutput();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (fr!=null)fr.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }

    public void WriteBookXml(Book book) throws DocumentException, IOException {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("src/xml/Book.xml");
        Element rootElement = document.getRootElement();
        int maxId = rootElement.elements().size();
        ;
        book.setBookId(maxId + 1);

        //2,添加元素
        Element newUserEle = rootElement.addElement("book");
        //设置属性
        newUserEle.addAttribute("bookId", String.valueOf(maxId + 1));
        //添加子元素
        newUserEle.addElement("name").setText(book.getName());
        newUserEle.addElement("author").setText(book.getAuthor());
        newUserEle.addElement("description").setText(book.getDescription());
        newUserEle.addElement("type").setText(book.getType());
        //上述步骤,都是保存在内存中
        //把内存的数据写到xml
        //输出格式
        OutputFormat outputFormat = OutputFormat.createPrettyPrint();
        outputFormat.setEncoding("UTF-8");//默认UTF-8
        FileWriter fw = new FileWriter("src/xml/Book.xml");

        XMLWriter xmlWriter = new XMLWriter(fw, outputFormat);

        //写入
        xmlWriter.write(document);

        //关闭资源
        fw.close();
        xmlWriter.close();
    }
    Properties props = new Properties();

    public void upload(Map<String, Object> map) throws DocumentException, IOException {
        Book book = (Book) map.get("data");
        WriteBookXml(book);
        books.add(book);
        String txt = (String) map.get("txt");
        BufferedWriter bw = null;
        String reply = "";

        props.load(new FileInputStream("src/novel.properties"));
        bw = new BufferedWriter(new FileWriter(props.getProperty("ServesavePath")+"/" + book.getName() + ".txt"));
        char[] chars = txt.toCharArray();
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < chars.length; i++) {
            stringBuffer.append(chars[i]);
            if (i % 100 == 0 && i != 0) {
                bw.write(stringBuffer.toString());
                stringBuffer = stringBuffer.delete(0, 101);
                bw.newLine();
                bw.flush();
            }
        }
        //发送
        reply = "上传成功";
        oos.writeUTF(reply);
        oos.flush();
        socket.shutdownOutput();
        if (bw!=null)bw.close();

    }

    public void download(Map<String, Object> map) {
        String bname = (String) map.get("data");
         BufferedReader br=null;
        try {
            Properties props = new Properties();
            props.load(new FileInputStream("src/novel.properties"));
            br=new BufferedReader(new FileReader(props.getProperty("ServesavePath")+"/"+bname+".txt"));
            StringBuffer stringBuffer = new StringBuffer();
            String line=null;
            while ((line=br.readLine())!=null){      
                stringBuffer.append(line);
            }
            map.put("data",stringBuffer);
            oos.writeObject(map);
            oos.flush();
          socket.shutdownOutput();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                if (br!=null)br.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }

    public void doShowNovelList() throws DocumentException, IOException {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("src/xml/Book.xml");
        Element rootElement = document.getRootElement();
        if (rootElement.elements().size() == 0) {
            books = null;
        } else {
            for (Object element : rootElement.elements("book")) {
                Element book = (Element) element;
                int bookId = Integer.parseInt(book.attributeValue("bookId"));
                Element bookName = book.element("name");
                String bookNameText = bookName.getText();
                Element bookAuthor = book.element("author");
                String bookAuthorText = bookAuthor.getText();
                Element bookDescription = book.element("description");
                String bookDescriptionText = bookDescription.getText();
                Element bookType = book.element("type");
                String bookTypeText = bookType.getText();
                Book book1 = new Book(bookId, bookNameText, bookAuthorText, bookDescriptionText, bookTypeText);
                books.add(book1);
            }
        }
        oos.writeObject(books);
        oos.flush();
        socket.shutdownOutput();
    }


    /**
     * 处理客户端的登录请求
     */
    private void doRegister(Map<String, Object> map) throws IOException, DocumentException {
        User user = (User) map.get("data");
        String reply = "注册失败,用户已注册,请直接登录";
        if (!checkUser(user)) {
            reply = "注册成功,用户可以登录了";
            WriteXml(user);
        }
        oos.writeUTF(reply);
        oos.flush();
        socket.shutdownOutput();
    }

    public void WriteXml(User user) throws DocumentException, IOException {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read("src/xml/User.xml");
        Element rootElement = document.getRootElement();
        List elements = rootElement.elements();
        int id = elements.size();
        //2,添加元素
        Element newUserEle = rootElement.addElement("user");
        //设置属性
        newUserEle.addAttribute("userId", String.valueOf(id + 1));
        //添加子元素
        newUserEle.addElement("username").setText(user.getUsername());
        newUserEle.addElement("password").setText(user.getPassword());
        //上述步骤,都是保存在内存中
        //把内存的数据写到xml
        //输出格式
        OutputFormat outputFormat = OutputFormat.createPrettyPrint();
        outputFormat.setEncoding("UTF-8");//默认UTF-8
        FileWriter fw = new FileWriter("src/xml/User.xml");

        XMLWriter xmlWriter = new XMLWriter(fw, outputFormat);

        //写入
        xmlWriter.write(document);

        //关闭资源
        fw.close();
        xmlWriter.close();
    }

    /**
     * 处理客户端的登录请求
     */
    private void doLogin(Map<String, Object> map) throws IOException {
        ReadeXml();
        User user = (User) map.get("data");
        String reply = "登录失败";
        if (checkUser(user)) {
            reply = "登录成功";
        }
        oos.writeUTF(reply);
        oos.flush();
        socket.shutdownOutput();
    }


    /**
     * 判断用户是否存在
     *
     * @param user
     * @return true: 存在   false: 不存在
     */
    public boolean checkUser(User user) {
        for (User user1 : users) {
            if (user1.getUsername().equalsIgnoreCase(user.getUsername())
                    && user1.getPassword().equals(user.getPassword())) {
                return true;
            }
        }
        return false;
    }


    /**
     * 开启网络
     */
    public void startNet() throws IOException {
        //得到输入输出流
        is = socket.getInputStream();
        os = socket.getOutputStream();

        //初始化对象流
        oos = new ObjectOutputStream(os);
        ois = new ObjectInputStream(is);
    }

    /**
     * 关闭网络, 关闭资源
     * ctrl + alt + t 包裹代码的快捷键
     */
    public void closeNet() {
        //倒序关 先开的后关
        try {
            if (oos != null) oos.close();
            if (ois != null) ois.close();
            if (socket != null) socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }
}

4.小结

在线小说阅读系统的代码实现涉及的关键知识点有

  • 面向对象编程:类和对象的定义与使用。封装、继承和多态的应用。
  • Socket 网络编程:服务器端使用 ServerSocket 监听端口并接受客户端连接。客户端使用 Socket 连接服务器。利用输入输出流进行数据的发送和接收。
  • 多线程处理:服务器端为每个客户端连接创建新线程处理请求。
  • 文件I/O操作:使用 FileReader、BufferedReader 等进行文件读取。使用 FileWriter、BufferedWriter 等进行文件写入。
  • XML文件处理:使用dom4j库解析和生成XML文件。XML文件的读取、修改、创建和保存。
  • 属性文件操作:使用 Properties 类加载和读取属性文件。
  • 集合框架使用:List、Map 等集合的使用来存储和管理数据。
  • 异常处理:使用 try-catch-finally 语句块处理异常和资源释放。
  • 数据序列化与反序列化:使用 ObjectOutputStream 和 ObjectInputStream 进行对象的序列化和反序列化。
  • 基本数据结构操作:使用 StringBuffer、ArrayList 等数据结构。
  • 控制台输入输出:使用 Scanner 类进行控制台输入。
  • 网络通信协议:定义通信协议,使用特定的操作码(如登录、注册等)。
  • 用户认证:实现用户登录和注册的用户认证机制。
  • 资源管理:合理管理网络资源和文件资源,确保及时关闭。
  • 文本处理:字符串操作,如拼接、分割、查找等。
  • 分页显示:在线阅读小说时,实现内容的分页显示。
  • 文件路径处理:文件路径的构建和处理。
  • 编码规范:遵循良好的编码规范,如驼峰命名法、代码注释等。
  • 安全性考虑:考虑输入验证和异常安全,避免潜在的安全隐患。
  • 用户界面反馈:为用户提供清晰的操作反馈信息。
    这些知识点构成了在线小说阅读系统开发的基础,涵盖了从前端用户交互到后端数据处理的全过程。
  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值