基于Java的迷你在线小说在线系统
一、展示效果
需求分析:
- 创建客户端、服务器端,完成简单通信;
- 完成登录、注册、退出功能;
- 完成小说查询列表的功能
- 完成在线阅读小说功能
- 完成下载小说功能
- 完成上传小说功能
二、整体架构
客户端:
服务器:
三、实现步骤
1、创建客户端、服务器端,完成简单通信:创建服务器端线程类,并循环监听状态,创建客户端,请求服务器并发送消息,服务器端响应客户端。
客户端:
public void clientInit() throws IOException {
//初始化第一步先连接服务器
socket = new Socket();
socket.connect(new InetSocketAddress(7171));
}
服务器:
public void init() throws IOException, ClassNotFoundException, DocumentException {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(7171));
System.out.println("启动服务器,等待连接...");
//监听
socket = serverSocket.accept();
System.out.println("已成功连接服务器!!!");
postClientMessage();
}
2、完成登录、注册、退出功能:创建用于保存登录信息的文件,接收用户登录信息并查找登录信息文件,判断是否登录成功,接收用户注册信息并保存至登录信息文件。
客户端:
//用户登录
public void login() throws IOException, InterruptedException {
System.out.print("请输入登录用户名:");
String usename = input.nextLine();
System.out.print("请输入登录密码:");
String password = input.nextLine();
UserEntity userEntity = new UserEntity();
userEntity.setUsername(usename);
userEntity.setPwd(password);
MessageEntity messageEntity = new MessageEntity();
//传给服务器
messageEntity.setOprator(1);
messageEntity.setUserEntity(userEntity);
//发送数据给服务器
dataUtil.sendData(messageEntity);
//接收数据
String s = dataUtil.receiveDate();
//根据服务器的返回结果给出相应的处理
resultUtil.postUserResult(s);
}
//用户注册
public void register() throws IOException, InterruptedException {
System.out.println("请输入需要注册的用户ID:");
String userId = input.nextLine();
System.out.print("请输入注册用户名:");
String usename = input.nextLine();
System.out.print("请输入注册密码:");
String pwd = input.nextLine();
UserEntity userEntity = new UserEntity();
userEntity.setId(Integer.parseInt(userId));
userEntity.setUsername(usename);
userEntity.setPwd(pwd);
MessageEntity messageEntity = new MessageEntity();
//传给服务器
messageEntity.setOprator(2);
messageEntity.setUserEntity(userEntity);
//发送数据给服务器
dataUtil.sendData(messageEntity);
//接收数据
String s = dataUtil.receiveDate();
//根据服务器的返回结果给出相应的处理
resultUtil.postUserResult(s);
}
//用户退出
public void quit(){
System.out.println("正在退出系统...");
System.out.println("-----退出系统成功-----");
System.exit(0);
}
服务器:
MessageEntity m = (MessageEntity) ois.readObject();
if (m.getOprator() == 1) {//登录
//传入客户端输入的用户名和密码
String result = userService.login(m.getUserEntity());
//把登录结果返回给客户端
os.write(result.getBytes());
/*//让服务器不停地读取客户端消息
postClientMessage();*/
} else if (m.getOprator() == 2) {//注册
String register = userService.register(m.getUserEntity());
os.write(register.getBytes());
/*//让服务器不停地读取客户端消息
postClientMessage();*/
}
3、实现完成小说查询列表的功能:向服务器发送请求,获取书籍列表,然后返回这个列表。
public class BookClientService {
//查询服务器上小说列表
public List<String> queryPrintAllBook(DataUtil dataUtil) throws IOException {
List<String> list = new ArrayList<>();
MessageEntity messageEntity =new MessageEntity();
//只需要带一个操作
messageEntity.setOprator(5);
dataUtil.sendData(messageEntity);
//阻塞
String s = dataUtil.receiveDate();
String[] split = s.split("-");
for (String s1 : split) {
list.add(s1);
}
return list;
}
}
4、实现完成在线阅读小说功能:发送请求获取所有的书籍列表打印出来。然后,提示用户选择要阅读的书。发送请求以获取书的每一页的内容并打印出来,并提示用户是否要继续阅读下一页、上一页或者退出。如果用户选择上一页,它会检查当前是否在第一页,如果是则不执行任何操作。如果不是,则将页码减一。如果用户选择下一页,则将页码加一。如果用户选择退出,则结束阅读。如果服务器返回的内容是"end",则也结束阅读。
客户端:
if (info.equals("1")) {
//阅读小说---展示小说目录
List<String> list = (List<String>) bookClientService.queryPrintAllBook(dataUtil);
list.stream().forEach(s -> System.out.println(s));
System.out.print("请选择你要阅读的小说:");
//输入小说名
String bookName = input.nextLine();
//用来记录有页数
int page = 1;
while (true) {
//每一次都向服务器发送要执行的操作和书的页码,让服务器根据页码来读取相应的内容
MessageEntity m = new MessageEntity();
m.setBookName(bookName);
m.setOprator(6);
m.setPage(page);
//向服务器发送书名,和阅读小说的操作
dataUtil.sendData(m);
//接受读取到的内容
String contain = dataUtil.receiveDate();
//打印到控制台
System.out.println(contain);
System.out.println("当前为第"+page+"页");
System.out.println("上一页(1)\t下一页(2)\t退出(0)");
System.out.println("请输入数字执行相关操作...");
String choose = input.nextLine();
if (choose.equals("1")){
//如果客户端要阅读上一页,就把页码数减一
//先要判断当前页数是不是第一页
if(page == 1){
System.out.println("----- 当前已经为第一页! -----");
}else {
page--;
}
}else if(choose.equals("2")){
//如果客户端选择下一页,就把页码数加一
page++;
}else if(choose.equals("0")){
break;
}
if(contain.equals("end")){
break;
}
}
System.out.println("----- 阅读完成!!! -----");
String s = client.showMenu();
postBookResult(s);
}
服务器:
else if(m.getOprator() == 6){//阅读小说
//得到用户需要阅读的小说书名
String bookName = m.getBookName();
//去服务器的指定路径找到这本小说的路径
File bookFile = new File(Contain.bookPath+"\\\\"+bookName+".txt");
//先读取到程序
FileReader fr = new FileReader(bookFile);
//跳过字符,根据客户端的页面来定要读取的内容,用户如果要读取第2页的内容,就是跳过前面1000个字符
fr.skip((m.getPage() - 1) * 1000);
//一次读一千个字符
char[] chars = new char[1000];
int total = fr.read(chars);
if (total != -1) {
String contain = new String(chars, 0, total);
//将读取的内容写到网络中
os.write(contain.getBytes());
}
}
5、实现完成下载小说功能:如果info等于"3",则开始下载小说的过程。首先,会查询服务器上所有的书籍列表,并打印出来。然后提示用户输入需要下载的小说名。如果用户输入的小说名在列表中不存在,它会提示用户输入正确的名称。只有输入正确的小说名时,才会发送下载请求到服务器。服务器会开始发送下载内容,这个内容是一个流。在接收服务器发送的下载内容时,如果内容以"end"结束,那么就打印"下载完成",并将剩余的内容写入到文件中。如果内容没有以"end"结束,那么就直接将内容写入到文件中。下载完成后,调用方法来处理下载结果。
客户端:
else if (info.equals("3")) {
//下载之前先要做查询
List<String> list = bookClientService.queryPrintAllBook(dataUtil);
System.out.println("请输入需要下载的小说名进行下载:");
list.stream().forEach(s -> System.out.println(s));
String bookName="";
while (true){
bookName = input.nextLine();
String finalBookName = bookName;
long count = list.stream().filter(s -> s.equals(finalBookName)).count();
if(count==0){//输入的小说名字不存在
System.out.print("请输入正确的的小说名:");
}else {
break;
}
}
//收集到用户输入的小说名字
MessageEntity messageEntity = new MessageEntity();
messageEntity.setOprator(4);
messageEntity.setBookName(bookName);
dataUtil.sendData(messageEntity);
File file = new File(Contain.downloadPath + "\\" + bookName);
if (!file.exists()) {
file.createNewFile();
}else {
file.delete();
file.createNewFile();
}
while (true) {
String downLoadContent = dataUtil.receiveDate();
if (downLoadContent.endsWith("end")) {
System.out.println("----- 下载完成!!! -----");
FileOutputStream fos = new FileOutputStream(file, true);
fos.write(downLoadContent.replaceAll("end","").getBytes());
break;
}
FileOutputStream fos = new FileOutputStream(file, true);
fos.write(downLoadContent.getBytes());
}
String s = client.showMenu();
postBookResult(s);
}
服务器:
else if(m.getOprator()==4){//下载
//获取现需要下载的小说的名字
String bookName = m.getBookName();
String downLoadPath = Contain.bookPath+"\\"+ bookName;
File downLoadFile = new File(downLoadPath);
long length = downLoadFile.length();
FileInputStream fis = new FileInputStream(downLoadFile);
DecimalFormat df = new DecimalFormat("#.0");
int total =0;
double percent=0.0;
while ((total = fis.read(bs))!=-1){
String message = new String(bs,0,total);
os.write(message.getBytes());
}
//读完了
os.write("end".getBytes());
}
6、实现完成上传小说功能:如果info等于"2",则开始上传小说。首先,会提示用户输入需要上传的小说路径,获取该文件的大小,并打印出来。然后读取文件内容,并通过网络发送到服务器。在读取和发送过程中,会计算并显示上传的进度。当文件读取完毕后,会发送一个"end"信号给服务器,表示上传完成。最后服务器返回一个菜单信息,然后调用方法来处理上传结果。
客户端:
else if (info.equals("2")) {
System.out.print("请输入小说路径:");
String bookPathName = input.nextLine();
//为了获取文件的大小 ---百分比
File file = new File(bookPathName);
//整个文件的字节数
long length = file.length();
System.out.println("文件长度=" + length + "个字节");
//上传----先读到内容---写出去网络
FileInputStream fis = new FileInputStream(file);
//服务器那边不知道你的文件名 ---告诉服务器文件名
String bookName = bookPathName.substring(bookPathName.lastIndexOf("\\\\"));
int total = 0;//每次读取的字节数---读到文件末尾-1
double temp = 0.0;//累计读了多少
boolean flag = true;
while ((total=fis.read(bs))!=-1) {
temp += total;
MessageEntity messageEntity = new MessageEntity();
messageEntity.setOprator(3);
if (flag) {
messageEntity.setBookName(bookName);
flag = false;
}
messageEntity.setContent(new String(bs, 0, total));
dataUtil.sendData(messageEntity);
//如果接受成功 这一次读取的内容上传成功
String s = dataUtil.receiveDate();
System.out.print("已经完成:" + String.format("%.1f", (temp / length) * 100) + "%\r");
}
System.out.println("----- 上传进度 = 100% -----");
System.out.println("----- 上传完成!!! -----");
MessageEntity messageEntity = new MessageEntity();
messageEntity.setOprator(3);
messageEntity.setContent("end");
dataUtil.sendData(messageEntity);
String r = client.showMenu();
postBookResult(r);
}
服务器:
else if (m.getOprator() == 3) {//上传小说
//当次上传小说的内容
String content = m.getContent();
if(content.equals("end")){
//一部小说已经传完
uploadFile=null;
continue;
}
//这部分内容写到--对应的文件夹当中去---
//1、文件文件
if(uploadFile==null){
uploadFile = new File(Contain.bookPath + "\\" + m.getBookName());
}
if (!uploadFile.exists()) {//如果文件不存在则创建
uploadFile.createNewFile();
}else {
if (!XMLUtil.isEmpty(m.getBookName())) {//服务器已经存在小说 重写上传
uploadFile.delete();//先删除
uploadFile.createNewFile();//重写创建
}
}
FileWriter fileWriter = new FileWriter(uploadFile,true);
fileWriter.write(content);
//这一次成功
os.write("success".getBytes());
fileWriter.close();
}
四、总结
1、序列化与反序列化的路径需要保持一致,即报包名要相同,否则会出现异常;
2、在Java中,"/“是转义符,”\"才表示路径
3、使用DOM4J解析XML文件,首先需要将XML文件加载到内存中。DOM4J提供了一个名为SAXReader的类,可以方便地读取XML文件并将其转换为DOM树。
-
创建SAX解析器SAXReader对象,用于读取xml文件
SAXReader saxReader = new SAXReader();
-
读取xml文件,得到Document对象
Document document = saxReader.read(xmlFile);
-
通过Document对象获取根元素
Element element = document.getRootElement();
-
通过父元素获取子元素
1、获取迭代器:elementIterator(),迭代获取。
2、指定元素名:element(元素名) 获取指定元素名的第一个子元素
3、获取指定元素: elements(元素名) List 指定元素名的所有的子元素 -
通过元素获取属性
1、获取迭代器:attributeIterator(),迭代获取。
2、指定索引(0开始)或名字:attribute(索引)、attribute(属性名)
4、极致的封装,把抽象的数据(变量)和实现的方法(函数)封装在一起,形成一个对象。这样做的目的是隔离出影响范围,让代码更容易理解和维护。体现了六大设计原则中的单一原则(即:一个类、接口、方法只负责一项职责),也是“高内聚,低耦合”的体现。
例如:
五、完整代码
链接:https://pan.baidu.com/s/1L3BusUK5eb6FNvX5D_03RA
提取码:zngj