import java.net.*;
import java.sql.*;
import java.util.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.VisitorSupport;
import org.dom4j.io.SAXReader;
import javax.servlet.http.*;
public class XMLOnlineEditorServer extends JFrame {// 新建服务器类
boolean started = false;// 创建布尔类型的变量Strated,并设定初始值为false
ServerSocket ss = null;// 创建ServerSocket的对象,并设置为空
static Connection con = null;// 创建静态变量Connection的对象,并设置为空
static PreparedStatement psql = null;// 创建PreparedStatement的对象,并设置为静态的,且值为空
static Statement sql = null;// 创建Statement对象,用于进行数据库操作
static ResultSet res = null;// 创建ResultSet的对象,并设置为静态的,且其值为空
static XMLOnlineEditorServer visit = new XMLOnlineEditorServer();// 创建XMLOnlineEditorServer对象,用于后续操作
ArrayList<Client> list = new ArrayList<Client>();
JTextArea inforArea = null;
String address;
int Clientcount = 0;
JButton conButton = null;
public void launchServer() {
this.setTitle("Serve");
this.setBounds(200, 200, 600, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container con = this.getContentPane();
con.setLayout(new BorderLayout());
conButton = new JButton("connect");
conButton.addActionListener(new conButtonListener());
JButton lookButton = new JButton("check");
lookButton.addActionListener(new lookButtonListener());
JButton escButton = new JButton("esc");
escButton.addActionListener(new lookButtonListener());
escButton.addActionListener(new ActionListener() {// 向取消按钮添加监听器
public void actionPerformed(ActionEvent e) {// 重写ActionListener中的actionPerformed方法,以实现监听效果
JOptionPane.showMessageDialog(null, "You have exited!");// 弹出警告消息,"您已经退出登录!"
System.exit(0);// 点击确认退出程序
}
});
JToolBar toolBar = new JToolBar();
toolBar.add(conButton);
toolBar.addSeparator(new Dimension(20, 0));
toolBar.add(lookButton);
toolBar.addSeparator(new Dimension(20, 0));
toolBar.add(escButton);
inforArea = new JTextArea();
inforArea.setEditable(false);
JScrollPane scroll = new JScrollPane(inforArea);
con.add(toolBar, BorderLayout.SOUTH);
con.add(scroll, BorderLayout.CENTER);
this.setVisible(true);
}
public static void main(String[] args) {// 在主方法中建立连接
try { UIManager.setLookAndFeel("com.jtattoo.plaf.hifi.HiFiLookAndFeel");
} catch (Exception e) {
e.printStackTrace();
}//..... }
visit.launchServer();
visit.start();
}
public void start() {// 创建方法
try {
ss = new ServerSocket(8888);// 新建ServerSocket对象,并设置端口号8888
started = true;// 将started设置为true
inforArea.append("server start" + "\n");
inforArea.append("please connect..." + "\n");
System.out.println("---------------------------------");
} catch (IOException e) {// 捕捉可能抛出的异常
inforArea.append("failed!" + "\n");
}
try {
while (started) {
Socket s = ss.accept();// 新建Socket对象,并将对象赋值为接收自客户端的Socket对象
Client c = new Client(s);// 新建Client对象,并将s穿进去
inforArea.append("new client has connected!" + "\n");
list.add(c);
Clientcount++;
new Thread(c).start();// 启用新的线程
}
} catch (Exception e) {// 捕捉异常
e.printStackTrace();// 打印异常
} finally {
try {
ss.close();// 将ServerSocket关掉
} catch (IOException e) {// 捕捉异常
e.printStackTrace();// 打印异常
}
}
}
/**
* 创建类,实现Runnable接口,来实现每一个客户端对应一个线程,实现多用户登录的效果
*/
class Client extends VisitorSupport implements Runnable {// 创建方法
private boolean bconnected = false;// 设置boolean类型的变量,并设置初始值为false
private Socket s;// 创建Socket对象s,并设置为空
private DataInputStream dis = null;// 创建DataInputStream对象dis,并设置初始值为空
private DataOutputStream dos = null;// 创建DataOutputStream对象,用于向客户端传输数据
private boolean isSave = false;// 创建布尔类型变量,用于确定是否可以保存客户端传来的数据,默认值为false
PreparedStatement sql = null;// 创建PreparedStatement对象,用于进行数据库操作
Statement state = null;// 创建Statement对象,用于进行后续的数据库操作
ResultSet res1 = null;// 创建ResultSet对象,用于进行后续的数据库操作
int count;// 定义int类型的变量,用于确定数据库中字段的个数
int sum = 1;// 定义int类型的变量,用于数据库中的存储操作
int i;// 定义int类型变量i
/**
* 定义countInit方法,用于确定数据库中字段的数量,以便在插入数据时能够更加合理有序
*/
public void countInit() {// 创建无返回值的countInit方法
int init = 1;// 定义int类型的变量init,并赋予初始值为1
try {
state = con.createStatement();// 初始化数据库操作语句
res1 = state.executeQuery("select * from xml_node");// 在数据库中查询数据
if (res1.next() == true) {// 判断是否有记录
while (res1.next()) {// 以while循环的形式进行遍历数据库中的记录
String str = res1.getString(1);// 获取第一列中的数据
if (str.equals("" + init)) {// 判断所获取的数据是否等于所需求的值
count = init;// 如果等于的话,令count赋值为init
init++;// init自加一
}
}
} else {// 如果没有记录
count = 1;// 将count赋值为1
}
} catch (SQLException e) {// 捕获异常
e.printStackTrace();// 处理异常
}
}
/**
* 创建构造方法,且其形式化参数为Socket对象
*
* @param s
*/
public Client(Socket s) {// 创建构造器,形参为Socket对象
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());// 初始化DataInputStream对象,用于接收客户端发来的消息
dos = new DataOutputStream(s.getOutputStream());// 初始化DataOutputStream对象,用于向客户端发送消息
bconnected = true;// 设置bconnected的值为true
} catch (Exception e) {// 捕获异常
e.printStackTrace();// 打印异常
}
}
/**
* 创建没有返回值的analyseXML()方法,形式化参数为String类型的对象,用于对客户端传来的xml文档进行分析
* 判断是否有错误,就进行异常处理
*
* @param str
*/
public void analyseXML(String str) {// 创建analyseXML()方法
File f = new File("F:/文件/临时文件/tempory.xml");// 新建File对象,并保存到服务器本地的临时文件夹中
f.setWritable(true);// 并将刚刚创建的文档设置为可写
try {
FileWriter fileWriter = new FileWriter(f);// 创建FileWriter对象,并进行初始化,用于向文档中写入数据
fileWriter.write(str);// 将传来的数据写入到文档中
fileWriter.flush();// 将FileWriter对象全部写入到新建的文档中
fileWriter.close();// 关闭FileWriter对象
SAXReader reader = new SAXReader();// 创建SAXReader对象并进行初始化,用于后面的xml文档的解析
Document document = reader.read(f);// 创建Document对象,读取保存的XML文档,并生成Document对象
Client.this.countInit();// 实现countInit方法,用于确定数据库表中字段的个数
Element rootElement = document.getRootElement();// 获取xml文档中的根节点
rootElement.accept(Client.this);// 实现visit()方法
f.delete();// 删除临时文档f
Client.this.send("success");
} catch (Exception e) {// 捕获异常
Client.this.send("wrong");
}
}
/**
* 重写visit()方法,形式化参数为Element对象,用于遍历xml中的所有节点,及其内容
*/
public void visit(Element element) {// 创建Element()方法
i = 1;// 初始化i值为1
String node = element.getName();// 获取节点的名字
try {
sql = con.prepareStatement("insert into xml_node values(?,?)");// 向数据表中添加记录
sql.setString(1, "" + count);// 先确定记录的索引值并保存的数据表中的第一列中
sql.setString(2, node);// 将节点名保存到第二列中
sql.executeUpdate();// 更新数据表中的数据
} catch (SQLException e) {// 捕获异常
e.printStackTrace();// 打印异常
}
String text = element.getTextTrim();// 获取节点的内容,并去掉空格
if (!text.equals("")) {// 判断节点的内容是否为空,如果不为空
try {
sql = con
.prepareStatement("insert into xml_content values(?,?)");// 将属性保存到数据表中
sql.setString(1, "" + count);// 确定索引值,并保存的第一列中
sql.setString(2, text);// 将内容保存到第二列中
sql.executeUpdate();// 更新数据表中的数据
} catch (SQLException e) {// 捕获异常
e.printStackTrace();// 打印异常
}
}
count++;// count的值自加1
}
/**
* 重写visit()方法,形式化参数为Attribute对象,用于遍历xml中的所有节点属性,及其属性值
*/
public void visit(Attribute attr) {// 创建visit()方法
String attrname = attr.getName();// 获取节点的属性名称
if (!attrname.equals("")) {// 判断节点的属性名称是否为空,如果不为空
sum = count - 1;// 将sum赋值为count - 1
try {
sql = con
.prepareStatement("insert into xml_node values(?,?)");// 将属性名称保存到数据表中
sql.setString(1, "" + sum + i);// 获取属性名称的索引并保存到第一列中
sql.setString(2, attrname);// 将属性名称保存到第二列中
sql.executeUpdate();// 更新数据表中的内容
} catch (SQLException e) {// 捕获异常
e.printStackTrace();// 打印异常
}
}
String data = (String) attr.getData();// 获取节点属性值
if (!attrname.equals("")) {// 判断节点属性值是否为空,如果不为空
try {
sql = con
.prepareStatement("insert into xml_content values(?,?)");// 将属性值保存到数据库表中
sql.setString(1, "" + sum + i);// 获取属性值的索引值并保存到数据表中
sql.setString(2, data);// 将属性值保存到数据表中
sql.executeUpdate();// 更新数据表中的数据
} catch (SQLException e) {// 捕获异常
e.printStackTrace();// 打印异常
}
}
i = i + 1;// i的值自加一
}
/**
* 创建send()方法,用于将信息传递给客户端
*
* @param str
*/
public void send(String str) {// 创建方法
try {
dos.writeUTF(str);// 向客户端传递信息
} catch (IOException e) {// 捕获异常
e.printStackTrace();// 打印异常
}
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
public void run() {// 由于实现Runnable接口,就要实现其中的run()方法
try {
while (bconnected) {
String userInfor = dis.readUTF();// 读取客户端来的数据
String[] infor = userInfor.split("%%");// 将客户端传来的数据分割成数组
// 将用户的信息保存到数据库中
if (infor.length == 1) {// 当数组的长度为3时
Client.this.analyseXML(infor[0]);// 先对传来的数据进行分析
} else if (infor.length == 2) {// 当数组为2时
// 就调用confineInfor()方法确认信息是否正确
if (XMLOnlineEditorServer.this.confineInfor(infor[0],// 如果正确
infor[1]) == true) {
Client.this.send("hello");// 将向客户端发送hello,告诉客户端登陆成功
} else {// 如果错误
Client.this.send("bye");// 向客户端发送bye消息,告诉客户端登陆失败
}
} else if (infor.length == 4) {// 当数组的长度为4时
// 就将用户的信息传递到数据库并保存
if(XMLOnlineEditorServer.this.RepeatInfor(infor[0])==false){
if(infor[0].equals("")||infor[1].equals("")||infor[2].equals("")||infor[3].equals("")){
Client.this.send("null");
}else{
XMLOnlineEditorServer.this.conveyDataToSQL(infor[0],
infor[1], infor[2], infor[3]);
Client.this.send("success register");
}
}else{
Client.this.send("repeat");// 向客户端发送bye消息,告诉客户端登陆失败
}
} else if (infor.length == 3) {
inforArea.append("one client has disconnected!" + "\n");
Clientcount--;
list.remove(Client.this);
}
}
} catch (Exception e) {// 捕获异常
} finally {
try {
// 如果dis不为空,关闭dis
if (dis != null)
dis.close();
if (dos != null)
dos.close();
} catch (Exception e) {// 捕获异常
e.printStackTrace();// 打印异常
}
}
}
}
public void connectSQL() {// 创建连接数据库方法
String driverName = "com.mysql.jdbc.Driver";// 创建String对象,赋值为jdbc驱动的名字
String dbURL = "jdbc:mysql://localhost/db_xmlRegister?user=root&password=1234";// 创建String对象,并赋值为驱动的URL
try {// 由于在连接数据库的过程中可能会抛出异常,所以需要try,catch一下
Class.forName(driverName);// 启动数据库驱动
con = DriverManager.getConnection(dbURL);// 连接数据库
inforArea.append("succeed" + "\n");// 当成功连接数据库的时候打印“数据库连接成功”
} catch (Exception e) {// 捕捉异常
inforArea.append("please try again" + "\n");// 当捕捉到异常的时候打印“无法连接数据库,请稍后重试”
}
}
/**
* 创建conveyDataToSQL()方法,以实现将客户端传来的信息保存到数据库中
*/
public void conveyDataToSQL(String userName, String userpasswd,
String userSex, String userEmail) {// 创建方法
try {
psql = con
.prepareStatement("insert into tb_xmlRegister values (?,?,?,?)");// 预命令,向数据库中添加数据
psql.setString(1, userName);// 将第一个数据添加到数据表第一列中
psql.setString(2, userpasswd);// 将第二个数据添加到数据表第二列中
psql.setString(3, userSex);// 将第三个数据添加到数据表第三列中
psql.setString(4, userEmail);// 将第四个数据添加到数据表第四列中
psql.executeUpdate();// 更新数据库中的数据
} catch (Exception e) {// 捕获异常
inforArea.append("无法向数据库中写入数据" + "\n");// 打印"无法向数据库中写入数据"
}
}
/*
* 创建confineInfor()方法,实现将用户的信息与数据库中已注册的用户进行比对,看该用户书否注册,并检查该用户的用户名和密码是否与注册一样
*/
public boolean confineInfor(String user, String passwd) {// 创建confineInfor()方法
try {
sql = con.createStatement();// 创建预处理Statement
res = sql.executeQuery("select * from tb_xmlRegister");// 创建预处理命令
while (res.next()) {// 读取数据库中的内容
String name = res.getString(1);// 获取数据库表格中第一列的信息并赋值给name
String password = res.getString(2);// 获取数据库表格中第二列的信息并赋值给passwd
if ((name.equals(user)) && (password.equals(passwd))) {// 判断数据库中的信息与用户输入的信息是否一样
System.out.println(name);// 如果一样,就输出用户的姓名
return true;// 返回
}
}
} catch (SQLException e) {// 捕获异常
inforArea.append("客户端查询信息失败!" + "\n");// 打印无法查询信息
}
// JOptionPane.showMessageDialog(null, "用户名或密码不正确,请重试!");//
// 如果不一样,弹出对话框,告知用户"用户名或密码不正确,请重试!"
return false;
}
private class conButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
visit.connectSQL();
conButton.setEnabled(false);
}
}
private class lookButtonListener implements ActionListener {
public lookButtonListener() {
}
public void actionPerformed(ActionEvent e) {
inforArea.append("Line numbers:" + Clientcount + "\n");
inforArea.append("IPaddress:" + address + "\n");
}
}
public String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
public boolean RepeatInfor(String name){
try {
sql = con.createStatement();// 创建预处理Statement
res = sql.executeQuery("select * from tb_xmlRegister");// 创建预处理命令
while (res.next()) {// 读取数据库中的内容
String Regname = res.getString(1);// 获取数据库表格中第一列的信息并赋值给name
if ((name.equals(Regname))) {// 判断数据库中的信息与用户输入的信息是否一样
return true;// 返回
}
}
} catch (SQLException e) {// 捕获异常
e.printStackTrace();
}
return false;
}
}