代码地址
1 任务需求
1:创建客户端、服务器端,完成简单通信任务
2:完成登录、注册、退出、返回功能任务
3:完成小说查询列表的功能任务
4:完成在线阅读小说功能任务
5:完成下载小说功能任务
6:完成上传小说功能
2 项目技能目标
* 应用面向对象的思想编写简单项目
* 熟练操作Socket编程
* 熟练操作文件I/O读写的操作
* 了解配制文件的解析方式
* 了解多线程的应用
* 了解xml文件作为轻量级的存储,以及使用dom4j解析xml文件
3 创建客户端、服务器端,完成简单通信任务
3.1 分析
1. 创建服务器端线程类,并循环监听状态
2. 创建客户端,请求服务器并发送消息
3. 服务器端响应客户端
3.2 技能分析
建立连接
Socket socket = new Socket("localhost",8888);
打开Socket关联的输入输出流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
数据流中读写信息
String info ="用户名:Tom;用户密码:123456";
out.write(info.getBytes());
scoket.shutdownOutput();
关闭所有的数据流和Socket
out.close();
in.close();
socket.close();
3.3 建立连接
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
public void clientInit() throws IOException {
//连接服务器
socket = new Socket();
socket.connect(new InetSocketAddress(8080));
}
3.4 DataUtil
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class DataUtil {
static byte[] bs = new byte[512];
Socket socket;
ObjectOutputStream oos = null;
public DataUtil(Socket socket){
this.socket=socket;
}
/**
* 发送messageEntity对象
* @param messageEntity messageEntity对象
* @throws IOException 发送数据错误
*/
public void sendData(MessageEntity messageEntity) throws IOException {
//打开Socket关联的输出流
OutputStream os = socket.getOutputStream();
if(oos==null){
oos= new ObjectOutputStream(os);
}
//写到服务器(规定为messageEntity)
oos.writeObject(messageEntity);
}
/**
* 接受数据
* @return 数据
* @throws IOException 接收数据错误
*/
public String receiveData() throws IOException {
//打开Socket关联的输入流
InputStream is = socket.getInputStream();
//数据流中读信息进入bs数组中
int total = is.read(bs);
//将bs数组转化为字符串
String result = new String(bs,0,total);
return result;
}
public void send(String str) throws IOException {
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
}
}
打开Socket关联的输入输出流,数据流中读写信息这些客户端与服务端传输数据的方法都在这一类中
3.5 MessageEntity
package com.test;
import java.io.Serializable;
public class MessageEntity implements Serializable {
//执行什么操作
/**
* 1、登录
* 2、注册
* 3、选择阅读小说
* 4、上传小说
* 5、下载小说
*/
private String bookName;
private String content;
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
private int page;
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getBookName() {
return bookName;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
private int oprator;
private UserEntity userEntity;
public int getOprator() {
return oprator;
}
public void setOprator(int oprator) {
this.oprator = oprator;
}
public UserEntity getUserEntity() {
return userEntity;
}
public void setUserEntity(UserEntity userEntity) {
this.userEntity = userEntity;
}
}
规定在客户端和服务端发送数据时应为MessageEntity对象
4.实现完成登录、注册、退出、返回功能
4.1 分析
1. 创建用于保存登录信息的文件
2. 接收用户登录信息并查找登录信息文件,判断是否登录成功
3. 接收用户注册信息并保存至登录信息文件
4.2 技能分析:
4.2.1 解析XML
Dom4j 是一个开源的 XML 解析框架,它基于 Java 的 sax 解析器和 jaxp 解析器开发,提供了灵活简便、性能优良、扩展性强的 XML 解析和生成功能。
帮助手册:https://dom4j.github.io/javadoc/1.6.1/
Dom4J通过SAXReader对象加载并解析xml文件,把它转换为内存中的Document对象,接着对Document对象中的元素进行添加、更新和删除操作,并且通过XMLWriter对象把修改后的Document对象保存到xml文件中。
4.2.2 读取xml文件
创建SAX解析器SAXReader对象,用于读取xml文件
SAXReader saxReader = new SAXReader();
读取xml文件,得到Document对象:
Document document = saxReader.read(xmlFile); //xmlFile xml文件
通过Document对象获取根元素
Element element = document.getRootElement();
通过父元素获取子元素:
(1)、获取迭代器:elementIterator(),迭代获取。
(2)、指定元素名:element(元素名) 获取指定元素名的第一个子元素
(3)、获取指定元素: elements(元素名) List 指定元素名的所有的子元素
通过元素获取它的属性:
(1)、获取迭代器:attributeIterator(),迭代获取。
(2)、指定索引(0开始)或名字:attribute(索引)、attribute(属性名)
4.2.4 读取xml文件
创建输出格式对象
OutputFormat format = OutputFormat.createPrettyPrint();
设置编码
format.setEncoding("UTF-8");
把内存的Document对象保存到xml文件
FileWriter fw = new FileWriter(path);
XMLWriter xw = new XMLWriter(fw,format);
xw.write(doc);
关闭资源
xw.close();
fw.close();
4.3 User.xml
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user userId="1">
<username>admin</username>
<pwd>123</pwd>
</user>
<user userId="2">
<username>jack</username>
<pwd>123456</pwd>
</user>
<user userId="3">
<username>asd</username>
<pwd>123</pwd>
</user>
</users>
4.4 XmlUtil
package com.test;
import org.dom4j.*;
import org.dom4j.io.*;
import java.io.*;
import java.util.*;
public class XMLUtil {
static List<UserEntity> list = new ArrayList<>();
public static List<UserEntity> getList() {
return list;
}
static String xmlFilePath = "D:\\qq\\204853899\\FileRecv\\client (1)\\server\\src\\Users.xml";
static Document document = null;
/**
* 获取所有xml当中的元素
* @return UserEntity集合
* @throws DocumentException
*/
public static List<UserEntity> parseXml() throws DocumentException {
list.clear();
Element rootElement = init();
//遍历根节点下面所有的节点
Iterator<Element> iterator = rootElement.elementIterator();
while (iterator.hasNext()){
//一个子元素---根元素下面的子元素
Element element = iterator.next();
//获取元素的属性
// element.attribute("userId");
Element username = element.element("username");
String usernameValue = username.getStringValue();
Element pwd = element.element("pwd");
String pwdValue = pwd.getStringValue();
UserEntity userEntity = new UserEntity();
userEntity.setUsername(usernameValue);
userEntity.setPassword(pwdValue);
list.add(userEntity);
}
return list;
}
/**
* 更新xml
* @param userEntity userEntity对象
* @throws IOException
* @throws DocumentException
*/
public static void updateXml(UserEntity userEntity) throws IOException, DocumentException {
Element rootElement = init();
//往根元素下面添加一个元素 user
Element element = rootElement.addElement("user");
//给先添加的user元素添加一个属性 userId
element.addAttribute("userId",userEntity.getId()+"");
element.addElement("username").setText(userEntity.getUsername());
element.addElement("pwd").setText(userEntity.getPassword());
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("UTF-8");
FileWriter fw = new FileWriter(xmlFilePath);
XMLWriter xw = new XMLWriter(fw,format);
xw.write(document);
xw.close();
fw.close();
}
public static Element init() throws DocumentException {
List<UserEntity> list = new ArrayList<>();
File xmlFile = new File(xmlFilePath);
//实例化SAXReader对象 读取xml
SAXReader saxReader = new SAXReader();
//整个xml的内容----Document
document = saxReader.read(xmlFile);
//根元素节点
Element rootElement = document.getRootElement();
return rootElement;
}
//判断s 是否为null 或者“”
public static boolean isEmpty(String s){
if(s==null){
return true;
}else if(s.equals("")){
return true;
}
return false;
}
public static void main(String[] args) {
isEmpty("");
}
}
4.5 登录,注册,退出
客户端
输出提醒消息
输入操作
/**
* 欢迎界面
* @throws IOException
*/
public void welcome() throws IOException {
System.out.println("欢迎来到xxxxx系统");
System.out.println("1、登录系统");
System.out.println("2、注册用户");
System.out.println("3、退出系统");
System.out.println("输入序号进行操作");
String no = sc.nextLine();
if(no.equals("1")){//登录
userClientService.login();
} else if (no.equals("2")) {//注册
userClientService.register();
}else {//退出
System.exit(0);
}
}
4.6 UserClientService
输出提醒消息
给userEntity对象赋值
给messageEntity对象赋值
发送messageEntity
接收数据
根据服务器返回的结果取完成不同的处理
package com.test;
import java.io.IOException;
import java.util.Scanner;
public class UserClientService {
DataUtil dataUtil;
ResultUtil resultUtil;
public UserClientService(DataUtil dataUtil,ResultUtil resultUtil){
this.dataUtil =dataUtil;
this.resultUtil = resultUtil;
}
Scanner sc =new Scanner(System.in);
/**
* 登录
* @throws IOException
*/
public void login() throws IOException {
System.out.println("请输入登录的用户名:");
String username = sc.nextLine();
System.out.println("请输入登录的密码:");
String password = sc.nextLine();
UserEntity userEntity = new UserEntity();
userEntity.setPassword(password);
userEntity.setUsername(username);
MessageEntity messageEntity = new MessageEntity();
messageEntity.setOprator(1);
messageEntity.setUserEntity(userEntity);
dataUtil.sendData(messageEntity);
//接受数据
String s = dataUtil.receiveData();
//根据服务器返回的结果取完成不同的处理
resultUtil.postUserResult(s);
}
/**
* 注册
* @throws IOException
*/
public void register() throws IOException {
System.out.println("请输入注册的用户id:");
String userId = sc.nextLine();
System.out.println("请输入注册的用户账号:");
String username = sc.nextLine();
System.out.println("请输入注册的密码:");
String password = sc.nextLine();
UserEntity userEntity = new UserEntity();
userEntity.setUsername(username);
userEntity.setId(Integer.parseInt(userId));
userEntity.setPassword(password);
MessageEntity messageEntity= new MessageEntity();
messageEntity.setOprator(2);
messageEntity.setUserEntity(userEntity);
dataUtil.sendData(messageEntity);
String s = dataUtil.receiveData();
resultUtil.postUserResult(s);
}
}
5 完成小说查询列表的功能任务
5.1 客户端 BookClientService
把小说查询操作发送给服务器
获取服务器上的小说
package com.test;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.receiveData();
String[] split = s.split("-");
for (String s1 : split) {
list.add(s1);
}
return list;
}
}
5.2 服务端
找到服务器上的小说文件夹
获取服务器上的小说名字
else if(m.getOprator()==5){//查询小说列表
String bookList="";
File file = new File(Contain.bookPath);
String[] list = file.list();
for (String s : list) {
bookList+=s+"-";
}
os.write(bookList.getBytes());
}
5.3 Contain
package com.test;
public class Contain {
public static final String downloadPath="d:\\book";
public static final String bookPath = "D:\\qq\\204853899\\FileRecv\\client (1)\\server\\book";
public static final String REGISTERSUCRESULT="100";
public static final String REGISTERFAIRESULT="101";
public static final String LOGINSUCRESULT="200";
public static final String LOGINFAIRESULT="201";
public static final String FLAGSTR="j#";
}
package com.test;
public class Contain {
//服务器上存放小说的路径
public static final String bookPath = "D:\\qq\\204853899\\FileRecv\\client (1)\\server\\book";
public static final String FLAGSTR="j#";
}
用于储存常量的类
6.完成在线阅读小说功能任务
6.1 客户端
获取服务器上的小说
对小说名字进行判断
发送阅读消息
接收阅读消息
判断一页是否结束
发送翻页消息和页码
接收结束消息
if (info.equals("1")) {//阅读小说
//获取服务器上的小说
List<String> list = bookClientService.queryPrintAllBook(dataUtil);
//对小说名字进行判断
System.out.println("请输入下面显示的小说名字进行阅读");
list.stream().forEach(s -> System.out.println(s));
String bookName="";//书名
while (true){
bookName = sc.nextLine();
String finalBookName = bookName;
long count = list.stream().filter(s -> s.equals(finalBookName)).count();//与书名相同的个数
if(count==0){//输入的小说名字不存在
System.out.println("请输入合理的小说名字");
}else {
break;
}
}
//收集到用户输入的小说名字
//发送阅读消息
MessageEntity messageEntity = new MessageEntity();
messageEntity.setOprator(6);
messageEntity.setBookName(bookName);
dataUtil.sendData(messageEntity);
int k=0;//页码
while (true) {
//接收阅读消息
String downLoadContent = dataUtil.receiveData();
System.out.println(downLoadContent);
//判断一页是否结束
if(downLoadContent.endsWith("ok")) {
System.out.println();
k++;
//第一页不需要上一页
if(k<2){
System.out.println("下一页(n),退出(e)");
}else {
System.out.println("上一页(p),下一页(n),退出(e)");
}
//发送翻页消息和页码
String str=sc.next();
if (str.equals("p")){
k--;
}
dataUtil.send(str+k);
//接收结束消息
String s = dataUtil.receiveData();
if (s.endsWith("end")) {
break;
}
}
}
String r = ct.menu();
postBookResult(r);
}
6.总结与问题
之前没怎么接触过网络编程,这一次只是模拟 ,总是用一台机器的思想解决问题,帮我熟练了客户端与服务端的数据传输与接收
之前没怎么接触过dom4j,这一次帮我熟练了dom4来解析xml
之前没有想到常量类的封装,封装不到位
用Scanner输入,一旦输入错误,直接报错.