文章目录
前言
在之前的学习中,我们已经学完了JAVASE的基本内容:基础语法、面向对象、异常、常用类、集合、线程、GUI、JDBC、网络编程。对于之前章节的内容我们除了牢记于心之外还应在实际场合中灵活掌握它们的应用。利用多人聊天室项目,我们可以把之前学习的内容系统的串联起来,在实战中加深理解。
一、服务器端
1.启动服务器端,打开服务器端窗口
public class Sever {
public static void main(String[] args) {
SeverFrame mySever = new SeverFrame();
mySever.createSeverFrame();
try {
ServerSocket mySeverSocket = new ServerSocket(5200);
mySever.start(mySeverSocket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void createSeverFrame(){
this.setSize(600,600);
this.setTitle("这是我的多人聊天服务器");
this.setIconImage(new ImageIcon("4.jpg").getImage());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setResizable(false);
JPanel myMessagePanel=new JPanel();
myChatArea=new JTextArea(30,40);
JScrollPane myChatScroll=new JScrollPane(myChatArea);
myMessagePanel.add(myChatScroll);
this.add( myMessagePanel);
this.addWindowFocusListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try {
clientSocket.close();
dispose();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
});
this.setVisible(true);
}
2.创建ServerSocket,循环监听客户端连接
public void start(ServerSocket mySeverSocket){
while(true){
try {
clientSocket= mySeverSocket.accept();
sockets.add(clientSocket);
System.out.println("成功连接上了第"+(++count)+"个客户端");
new SocketThread(clientSocket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.存储连接到服务器端的多个Socket(集合)
ArrayList<Socket>sockets=new ArrayList<>();
public void start(ServerSocket mySeverSocket){
while(true){
try {
clientSocket= mySeverSocket.accept();
sockets.add(clientSocket);
System.out.println("成功连接上了第"+(++count)+"个客户端");
new SocketThread(clientSocket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.接收客户端发送的信息(多线程)
//用内部类的方式来为每个socket创建一个线程,一直循环监听
class SocketThread extends Thread{
Socket ClientSocket;
DataOutputStream myOut;
DataInputStream myIn;
public SocketThread(Socket ClientSocket) {
this.ClientSocket=ClientSocket;
try {
myOut=new DataOutputStream(ClientSocket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
try {
myIn=new DataInputStream(ClientSocket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
String message= myIn.readUTF();
myChatArea.append(message+"\n");
//使用迭代器,给每个上线了的客户端都发送消息,把下线的客户端移出集合
Iterator<Socket>it=sockets.iterator();
while(it.hasNext()){
Socket s=it.next();
if(s.isClosed()){
it.remove();
continue;
}
DataOutputStream out=new DataOutputStream(s.getOutputStream());
out.writeUTF(message);
}
} catch (IOException e) {
stop();
}
}
}
}
5.将接收到的消息转发给所有的客户端socket
//使用迭代器,给每个上线了的客户端都发送消息,把下线的客户端移出集合
Iterator<Socket>it=sockets.iterator();
while(it.hasNext()){
Socket s=it.next();
if(s.isClosed()){
it.remove();
continue;
}
DataOutputStream out=new DataOutputStream(s.getOutputStream());
out.writeUTF(message);
}
二、客户端
1.登录和注册界面
public class Client {
public static void main(String[] args) {
LoginFrame myLogin= new LoginFrame();
myLogin.createLoginFrame();
}
}
public class LoginFrame extends JFrame {
public void createLoginFrame() {
this.setSize(400, 300);
this.setTitle("这是我的多人聊天GUI");
this.setIconImage(new ImageIcon("3.jpg").getImage());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setResizable(false);
//添加面板
JPanel total = new JPanel(); //总面板
total.setLayout(new GridLayout(4, 1));
//添加里面具体的面板
JPanel loginPanel = new JPanel();
JPanel accountPanel = new JPanel();
JPanel passwordPanel = new JPanel();
JPanel butPanel = new JPanel();
//添加标签 文本框 密码框 按钮组件
JLabel loginLabel = new JLabel("欢迎来到多人聊天室");
loginLabel.setFont(new Font("宋体", Font.BOLD, 20));
loginPanel.add(loginLabel);
JLabel accountLabel = new JLabel("用户名");
JTextField accountFiedl = new JTextField(15);
accountPanel.add(accountLabel);
accountPanel.add(accountFiedl);
JLabel passwordLabel = new JLabel("密 码");
JPasswordField passwrodField = new JPasswordField(15);
passwordPanel.add(passwordLabel);
passwordPanel.add(passwrodField);
JButton loginButton = new JButton("登录");
JButton registerButton = new JButton("注册");
butPanel.add(loginButton);
butPanel.add(registerButton);
//把所有的组件都加到总面板上去
total.add(loginPanel);
total.add(accountPanel);
total.add(passwordPanel);
total.add(butPanel);
this.add(total);
this.setVisible(true);
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) { 判断用户名和密码的程序,判断成功后进入登录界面
String account = accountFiedl.getText();
String password = passwrodField.getText();
if (account.equals("") || password.equals("")) {
JOptionPane.showMessageDialog(null, "账号或者密码不能为空", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
try {
LoginDao logindao = new LoginDao();
int result = logindao.test(account, password);
if (result == 0) {
JOptionPane.showMessageDialog(null, "账号或者密码错误", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
} catch (Exception t) {
t.printStackTrace();
}
try {
Socket mySocket = new Socket("192.168.43.39", 5200);
System.out.println("连接成功");
ChatFrame myChatFrame = new ChatFrame(mySocket, accountFiedl.getText());
dispose();
myChatFrame.createChatFrame();
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器连接失败", "错误", JOptionPane.ERROR_MESSAGE);
}
}
});
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
RegisterFrame myRegisterFrame = new RegisterFrame();
myRegisterFrame.createRegisterFrame();
}
});
}
}
public class RegisterFrame extends JFrame {
public void createRegisterFrame() {
this.setSize(400, 300);
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
this.setTitle("注册界面");
this.setLocationRelativeTo(null);
this.setLayout(new BorderLayout());
this.setResizable(false);
JLabel loginLabel = new JLabel("欢迎来到注册界面");
loginLabel.setFont(new Font("宋体", Font.BOLD, 20));
JPanel registerPanel = new JPanel();
registerPanel.setLayout(new GridLayout(4, 1));
JLabel account = new JLabel("用户名");
JLabel password = new JLabel("密 码");
JTextField accountfield = new JTextField(13);
JTextField passwordfield = new JTextField(13);
JButton but = new JButton("保存");
JPanel labelPanel = new JPanel();
labelPanel.add(loginLabel);
JPanel accountPanel = new JPanel();
accountPanel.add(account);
accountPanel.add(accountfield);
JPanel passwordPanel = new JPanel();
passwordPanel.add(password);
passwordPanel.add(passwordfield);
JPanel buttonPanel = new JPanel();
buttonPanel.add(but);
registerPanel.add(labelPanel);
registerPanel.add(accountPanel);
registerPanel.add(passwordPanel);
registerPanel.add(buttonPanel);
this.add(registerPanel);
but.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String account = accountfield.getText();
String password = passwordfield.getText();
if (account.equals("") || password.equals("")) { //判断是否为空,空就提示用户重新输入
JOptionPane.showMessageDialog(null, "请输入完整信息", "提示", JOptionPane.QUESTION_MESSAGE);
return;
}
//这里可以加个验证,验证账号是否在数据库中存在
try {
RegisterDao dao= new RegisterDao();
int result=dao.save(account,password);
if(result!=1){
dispose();
new LoginFrame().createLoginFrame();
}
} catch (Exception exception) {
exception.printStackTrace();
JOptionPane.showMessageDialog(null, "程序有误", "提示", JOptionPane.QUESTION_MESSAGE);
}
}
});
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int choice = JOptionPane.showConfirmDialog(null, "您确定要关闭吗", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
{
if (choice == 0) {
dispose();
new LoginFrame().createLoginFrame();
}
}
}
});
this.setVisible(true);
}
}
2.信息认证(数据库)
public class LoginDao {
int result;
public int test(String account, String password) throws ClassNotFoundException, SQLException {
Connection con = null;
Statement st = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver"); // 1.注册JDBC驱动程序 :这需要初始化驱动程序,这样就可以打开与数据库的通信信道。
String user = "jdbc:mysql://localhost:3306/chat_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
String userName1 = "root";
String password1 = "0521";
con = DriverManager.getConnection(user, userName1, password1);
PreparedStatement pst1 = con.prepareStatement(" SELECT COUNT(*)count1 FROM t_user WHERE account=? AND PASSWORD=?");
pst1.setString(1, account);
pst1.setString(2, password);
ResultSet res = pst1.executeQuery();
if (res.next()) {
result = res.getInt("count1");
}
} finally {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
}
return result;
}
}
public class RegisterDao {
//dao包的含义:data access object 数据处理包
public int save(String account, String password) throws ClassNotFoundException, SQLException {
int result = 0;
Connection con = null;
Statement st = null;
PreparedStatement pst1 = null;
PreparedStatement pst2 = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver"); // 1.注册JDBC驱动程序 :这需要初始化驱动程序,这样就可以打开与数据库的通信信道。
String user = "jdbc:mysql://localhost:3306/chat_db?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
String userName1 = "root";
String password1 = "0521";
con = DriverManager.getConnection(user, userName1, password1);
//先验证账号在数据库中存在不存在,如果存在直接返回
pst1 = con.prepareStatement("SELECT COUNT(*)count1 FROM t_user WHERE account=?");
pst1.setString(1, account);
ResultSet res = pst1.executeQuery();
if (res.next()) {
result = res.getInt("count1");
}
if (result != 0) { //如果已经有账号存在,直接用return返回 返回1表示账号已经存在,返回0表示成功注册
JOptionPane.showMessageDialog(null, "账号已经存在", "提示", JOptionPane.QUESTION_MESSAGE);
return 1;
}
pst2 = con.prepareStatement("INSERT INTO t_user(account,password,reg_time)values (?,?,?)");
pst2.setString(1, account);
pst2.setString(2, password);
pst2.setObject(3, new Date());
pst2.executeUpdate();
JOptionPane.showMessageDialog(null, "注册成功", "提示", JOptionPane.QUESTION_MESSAGE);
} finally {
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
if (pst1 != null) {
pst1.close();
}
if (pst2 != null) {
pst2.close();
}
}
return 0;
}
}
3.创建客户端Socket连接服务器
try {
Socket mySocket = new Socket("192.168.43.39", 5200);
System.out.println("连接成功");
ChatFrame myChatFrame = new ChatFrame(mySocket, accountFiedl.getText());
dispose();
myChatFrame.createChatFrame();
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器连接失败", "错误", JOptionPane.ERROR_MESSAGE);
}
4.聊天界面
public void createChatFrame() {
this.setSize(600, 600);
this.setTitle("您好" + account + "欢迎回来");
this.setIconImage(new ImageIcon("3.jpg").getImage());
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setResizable(false);
this.setLayout(new BorderLayout());
JPanel myTotalPanel = new JPanel();
JPanel myMessagePanel = new JPanel();
myChatArea = new JTextArea(30, 40);
JScrollPane myChatScroll = new JScrollPane(myChatArea);
myMessagePanel.add(myChatScroll);
JPanel mySendPanel = new JPanel();
JTextField mySendText = new JTextField(15);
mySendText.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) {
if (mySendText.getText().length() != 0) {
String message = account + " " + new Date().toLocaleString() + "\n" + mySendText.getText();
try {
myOut.writeUTF(message);
mySendText.setText("");
} catch (IOException ioException) {
ioException.printStackTrace();
}
} else {
JOptionPane.showMessageDialog(null, "聊天内容不能为空", "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
});
JButton mySendButton = new JButton("发送");
mySendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (mySendText.getText().length() != 0) {
String message = account + " " + new Date().toLocaleString() + "\n" + mySendText.getText();
try {
myOut.writeUTF(message);
mySendText.setText("");
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器连接失败", "错误", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(null, "聊天内容不能为空", "错误", JOptionPane.ERROR_MESSAGE);
}
}
});
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int choice = JOptionPane.showConfirmDialog(null, "您确定要关闭吗", "提示", JOptionPane.YES_NO_CANCEL_OPTION);
{
if (choice == 0) {
dispose();
}
}
}
});
mySendPanel.add(mySendText);
mySendPanel.add(mySendButton);
myTotalPanel.add(myMessagePanel, BorderLayout.CENTER);
myTotalPanel.add(mySendPanel, BorderLayout.SOUTH);
this.add(myTotalPanel);
this.setVisible(true);
}
5.发送聊天信息
JButton mySendButton = new JButton("发送");
mySendButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (mySendText.getText().length() != 0) {
String message = account + " " + new Date().toLocaleString() + "\n" + mySendText.getText();
try {
myOut.writeUTF(message);
mySendText.setText("");
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器连接失败", "错误", JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(null, "聊天内容不能为空", "错误", JOptionPane.ERROR_MESSAGE);
}
}
});
6.接收服务器的信息(线程)
public ChatFrame(Socket mySocket, String account) throws IOException {
this.mySocket = mySocket;
myOut = new DataOutputStream(mySocket.getOutputStream());
myIn = new DataInputStream(mySocket.getInputStream());
this.account = account;
new SocketThread().start();
}
class SocketThread extends Thread {
@Override
public void run() {
boolean flag = true;
while (flag) {
try {
String str = myIn.readUTF();
myChatArea.append(str + "\n" + "\n");
} catch (IOException e) {
e.printStackTrace();
flag = false;
JOptionPane.showMessageDialog(null, "服务器连接失败", "错误", JOptionPane.ERROR_MESSAGE);
}
}
}
}
三、项目效果
总结
多人聊天室项目涵盖了JAVASE的大部分内容:基础语法、集合、线程、JDBC等。JAVASE是JAVA的基础内容部分,树高千丈,根源还是在底部,想要在JAVA后续的学习中学的轻松牢固,基础内容尤为重要。我们不仅要学会各个章节的基本概念,还要在实战中掌握它们的实际应用,多上手操练代码,我们的技术才能发展更快更远。