工程构成如下图:
1、ClientGUI包里面
(1)LogOn.java代码如下:
package ClientGUI;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import ClientSide.Client;
import ClientSide.Client.ClientThread;
import ServerSide.Server;
//登录窗口
public class LogOn extends JFrame implements ActionListener {
private Register register;
private ClientThread clientThread;
private Server server=Server.GetServer();
private JLabel jlUserName;
private JLabel jlPassword;
private JTextField jtUserName;
private JPasswordField jpPassword;
private JButton jbLogOn;
private JButton jbRegister;
private JPanel jpDown;
private JPanel jpCenter;
public LogOn() {
Init();
}
private void Init() {
jlUserName = new JLabel("用户名");
jlPassword = new JLabel("密 码");
jtUserName = new JTextField(15);
jpPassword = new JPasswordField(15);
jbLogOn = new JButton("登录");
jbRegister = new JButton("注册");
jbLogOn.addActionListener(this);
jbRegister.addActionListener(this);
jpCenter = new JPanel();
jpCenter.setLayout(new GridLayout(2, 2));
jpCenter.add(jlUserName);
jpCenter.add(jtUserName);
jpCenter.add(jlPassword);
jpCenter.add(jpPassword);
jpDown = new JPanel();
jpDown.setLayout(new FlowLayout());
jpDown.add(jbLogOn);
jpDown.add(jbRegister);
this.setLayout(new BorderLayout());
this.add("Center", jpCenter);
this.add("South", jpDown);
this.setTitle("用户登录界面");
this.setSize(350, 150);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
// 更改窗体样式
try {
String style = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(style);
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == jbLogOn) {
//判断是否为空
if(jtUserName.getText()==null || jtUserName.getText().equals("")
|| String.valueOf(jpPassword.getPassword())==null ||
String.valueOf(jpPassword.getPassword()).equals(""))
{
JOptionPane.showMessageDialog(null, "请输入账号或者密码");
}else
{
if(server.LogOn(jtUserName.getText(), String.valueOf(jpPassword.getPassword())))
{
MainUI mainUI=new MainUI(jtUserName.getText());
Client client=Client.GetClient();
client.SetMainUI(mainUI);
client.SetUserName(mainUI.GetUserName());
clientThread=client.new ClientThread();
client.SetClientThread(clientThread);
clientThread.start();
this.setVisible(false);
}else
{
JOptionPane.showMessageDialog(null, "账号或者密码错误");
}
}
} else if (source == jbRegister) {
register=new Register(this);
this.setVisible(false);
}
}
public static void main(String[] args) {
new LogOn();
}
}
(2)MainUI代码如下:
package ClientGUI;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import ClientSide.Client;
public class MainUI{
// private static MainUI mainUIInstance=null;
//
// public static synchronized MainUI GetMainUI()
// {
// if(mainUIInstance==null)
// {
// mainUIInstance=new MainUI();
// }
// return mainUIInstance;
// }
//
private JFrame frmtcp;
private JTextField textServerIP;// IP
private JTextField textServerPort;// 端口
private JTextArea textAreaRecord;// 消息文本记录框
private JButton btnSend;// 发送按钮
private JButton btnQuit;// 退出按钮
private JLabel jlUsername;// 用户名
private JList<String> listUsers;// 用户列表 Jlist控件显示客户端的用户列表。数据保存在DefaultListmodel数据模型对象中。
private final ButtonGroup buttonGroup = new ButtonGroup();
private JRadioButton rdbtnBrocast;// 群聊
private JRadioButton rdbtnPrivateChat;// 私聊
private JTextArea textAreaMsg;// 消息框
private DefaultListModel<String> modelUsers;// 数据模型,用于更新数据
public JTextField GetTextServerIP()
{
return textServerIP;
}
public JTextField GetTextServerPort()
{
return textServerPort;
}
public void SetUserName(String userName)
{
jlUsername.setText(userName);
}
public String GetUserName()
{
return jlUsername.getText();
}
public JTextArea GetTextAreaMsg()
{
return textAreaMsg;
}
public JButton GetBtnQuit()
{
return btnQuit;
}
public JButton GetBtnSend()
{
return btnSend;
}
public JList<String> GetListUsers()
{
return listUsers;
}
public DefaultListModel<String> GetModelUsers()
{
return modelUsers;
}
public JRadioButton GetRdbtnBrocast()
{
return rdbtnBrocast;
}
public JRadioButton GetRdbtnPrivateChat()
{
return rdbtnPrivateChat;
}
public JTextArea GetTextAreaRecord()
{
return textAreaRecord;
}
public JFrame GetJfrmtcp()
{
return frmtcp;
}
public MainUI(String userName)
{
Init(userName);
modelUsers = new DefaultListModel();
modelUsers = new DefaultListModel<String>();
listUsers.setModel(modelUsers);
}
private void Init(String userName)
{
frmtcp = new JFrame();
frmtcp.setResizable(false);
frmtcp.setTitle("聊天主界面");
frmtcp.setBounds(100, 100, 715, 476);
frmtcp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmtcp.getContentPane().setLayout(null);
frmtcp.setVisible(true);
// IP
JLabel lblNewLabel = new JLabel("服务器IP地址:");
lblNewLabel.setBounds(14, 24, 121, 18);
frmtcp.getContentPane().add(lblNewLabel);
textServerIP = new JTextField();
textServerIP.setText("127.0.0.1");
textServerIP.setBounds(121, 21, 121, 24);
frmtcp.getContentPane().add(textServerIP);
textServerIP.setColumns(10);
// 端口
JLabel lblNewLabel_1 = new JLabel("端口号:");
lblNewLabel_1.setBounds(256, 24, 58, 18);
frmtcp.getContentPane().add(lblNewLabel_1);
textServerPort = new JTextField();
textServerPort.setText("6666");
textServerPort.setBounds(313, 21, 48, 24);
frmtcp.getContentPane().add(textServerPort);
textServerPort.setColumns(10);
// 用户名
JLabel label_1 = new JLabel("用户名:");
label_1.setBounds(375, 24, 58, 18);
frmtcp.getContentPane().add(label_1);
jlUsername = new JLabel(userName);
jlUsername.setBounds(433, 21, 95, 24);
frmtcp.getContentPane().add(jlUsername);
//退出按钮
btnQuit = new JButton("退出");
btnQuit.setBounds(596, 20, 95, 27);
frmtcp.getContentPane().add(btnQuit);
btnQuit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
}
});
// 发送按钮
btnSend = new JButton("发送");
btnSend.setEnabled(false);
btnSend.setBounds(444, 409, 88, 27);
frmtcp.getContentPane().add(btnSend);
// 连接按钮事件处理程序
btnSend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
}
});
// 聊天记录框
JLabel label = new JLabel("聊天区");
label.setBounds(14, 55, 72, 18);
frmtcp.getContentPane().add(label);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(14, 72, 518, 228);
frmtcp.getContentPane().add(scrollPane);
textAreaRecord = new JTextArea();
textAreaRecord.setBackground(Color.pink);
textAreaRecord.setEditable(false);
scrollPane.setViewportView(textAreaRecord);
// 消息输入框
JScrollPane scrollPane_1 = new JScrollPane();
scrollPane_1.setBounds(14, 332, 518, 73);
frmtcp.getContentPane().add(scrollPane_1);
textAreaMsg = new JTextArea();
scrollPane_1.setViewportView(textAreaMsg);
JLabel label_2 = new JLabel("在线用户");
label_2.setBounds(543, 55, 72, 18);
frmtcp.getContentPane().add(label_2);
// 用户列表框
JScrollPane scrollPane_2 = new JScrollPane();
scrollPane_2.setBounds(546, 72, 149, 334);
frmtcp.getContentPane().add(scrollPane_2);
listUsers = new JList<String>();
listUsers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
scrollPane_2.setViewportView(listUsers);
rdbtnBrocast = new JRadioButton("群聊");
buttonGroup.add(rdbtnBrocast);
rdbtnBrocast.setBounds(66, 306, 104, 22);
frmtcp.getContentPane().add(rdbtnBrocast);
rdbtnBrocast.setSelected(true);
rdbtnPrivateChat = new JRadioButton("私聊");
buttonGroup.add(rdbtnPrivateChat);
//rdbtnPrivateChat.setSelected(true);
rdbtnPrivateChat.setBounds(192, 306, 72, 22);
frmtcp.getContentPane().add(rdbtnPrivateChat);
// 更换样式
try {
String style = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(style);
// 更新窗体样式
SwingUtilities.updateComponentTreeUI(this.frmtcp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(3)Register代码如下:
package ClientGUI;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import ClientSide.Client;
import ServerSide.Server;
//注册窗口
public class Register extends JFrame implements ActionListener {
private LogOn logOn;
private JLabel jlUserName;
private JLabel jlPassword;
private JTextField jtUserName;
public JTextField GetJtUserName()
{
return jtUserName;
}
private JPasswordField jpPassword;
private JButton jbreturn;
private JButton jbRegister;
private JPanel jpDown;
private JPanel jpCenter;
private Server server = Server.GetServer();
public Register(LogOn logOn) {
Init(logOn);
}
private void Init(LogOn logOn) {
this.logOn=logOn;
jlUserName = new JLabel("用户名");
jlPassword = new JLabel("密 码");
jtUserName = new JTextField(15);
jpPassword = new JPasswordField(15);
jbreturn = new JButton("返回");
jbRegister = new JButton("注册");
jbreturn.addActionListener(this);
jbRegister.addActionListener(this);
jpCenter = new JPanel();
jpCenter.setLayout(new GridLayout(2, 2));
jpCenter.add(jlUserName);
jpCenter.add(jtUserName);
jpCenter.add(jlPassword);
jpCenter.add(jpPassword);
jpDown = new JPanel();
jpDown.setLayout(new FlowLayout());
jpDown.add(jbRegister);
jpDown.add(jbreturn);
this.setLayout(new BorderLayout());
this.add("Center", jpCenter);
this.add("South", jpDown);
this.setTitle("用户注册界面");
this.setSize(350, 150);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
// 更改窗体样式
try {
String style = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(style);
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == jbreturn) {
logOn.setVisible(true);
this.setVisible(false);
} else if (source == jbRegister) {
// 判断是否为空
if (jtUserName.getText() == null || jtUserName.getText().equals("")
|| String.valueOf(jpPassword.getPassword()) == null
|| String.valueOf(jpPassword.getPassword()).equals("")) {
JOptionPane.showMessageDialog(null, "请输入账号和密码");
} else {
// 数据传给服务器进行注册
if (server.Register(jtUserName.getText(), String.valueOf(jpPassword.getPassword()))) {
JOptionPane.showMessageDialog(null, "注册成功");
}else {
JOptionPane.showMessageDialog(null, "注册失败,改用户名已注册");
}
}
}
}
}
2、ClientSide包里面
(1)Client代码如下:
package ClientSide;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JOptionPane;
import ClientGUI.MainUI;
import ClientGUI.Register;
import Utils.Utils;
public class Client{
private static Client clientInstance=null;
public static synchronized Client GetClient()
{
if(clientInstance==null)
{
clientInstance=new Client();
}
return clientInstance;
}
private MainUI mainUI;
public void SetMainUI(MainUI mainUI)
{
this.mainUI=mainUI;
}
private ClientThread clientThread;
public void SetClientThread(ClientThread clientThread)
{
if(clientThread!=null)
{
this.clientThread=clientThread;
}
}
private String username;
public void SetUserName(String userName)
{
this.username=userName;
}
// 内部类.客户端线程,负责与服务器交互
public class ClientThread extends Thread{
private final Component frmtcp = null;
// 通信套接字
private Socket socket;
// 基本数据输入流
private DataInputStream dis;
// 基本数据输出流
private DataOutputStream dos;
// 是否登录
private boolean isLogged;
// 连接服务器
public void ConnectServer() {
// 获取服务器IP地址和端口
String serverIp = mainUI.GetTextServerIP().getText();
int serverPort = Integer.parseInt(mainUI.GetTextServerPort().getText());
try {
// 连接服务器,获取套接字IO流
socket = new Socket(serverIp, serverPort);
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
// 获取用户名,构建、发送登录报文
//String username = mainUI.GetUserName();
String msgConnect = "CONNECT#" + username;
dos.writeUTF(msgConnect);
dos.flush();
// 读取服务器返回的信息,判断是否登录成功
String response = dis.readUTF();
// 登录失败
if (response.equals("FAIL")) {
addMsg("username+连接服务器失败");
dis.close();
dos.close();
socket.close();
return;
} else {
addMsg(username+"上线了!");
isLogged = true;
mainUI.GetBtnQuit().setText("退出");
mainUI.GetBtnSend().setEnabled(true);
String[] self = { username };
UpdateJList(mainUI.GetListUsers(), self, "ADD");
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 退出聊天室功能实现
public void DisConnectServer() {
try {
Utils.sendMsg(socket, "DISCONNECT#" + username);
mainUI.GetModelUsers().clear();
mainUI.GetListUsers().setModel(mainUI.GetModelUsers());
addMsg(username+"下线了");
isLogged=false;
mainUI.GetBtnQuit().setEnabled(false);
mainUI.GetBtnSend().setEnabled(false);
String[] self = { username };
UpdateJList(mainUI.GetListUsers(), self, "DEL");
dis.close();
dos.close();
socket.close();
clientThread.interrupt();
} catch (IOException e) {
e.printStackTrace();
}
}
// 更新用户列表
public void UpdateJList(JList jList, String[] items, String op) {
switch (op) {
case "ADD": // 添加新数据
for (int i = 0; i < items.length; ++i) {
mainUI.GetModelUsers().addElement(items[i]);
break;
}
case "DEL":// 删除数据
for (int i = 0; i < items.length; ++i) {
mainUI.GetModelUsers().removeElement(items[i]);
break;
}
default:
break;
}
jList.setModel(mainUI.GetModelUsers());// 更新数据
}
// 发送消息
public void SendChatMsg() {
String msgChat = null;
String dstUser = "所有人";
if (mainUI.GetRdbtnBrocast().isSelected()) {
msgChat = "TALKTO_ALL#" + mainUI.GetTextAreaMsg().getText();
}
if (mainUI.GetRdbtnPrivateChat().isSelected()) {
dstUser = (String) mainUI.GetListUsers().getSelectedValue();
if (dstUser == null) {
JOptionPane.showMessageDialog(this.frmtcp, "请选择需要私聊的用户!");
return;
}
msgChat = "TALKTO#" + dstUser + "#" + mainUI.GetTextAreaMsg().getText();
}
// 发送聊天报文到服务器
Utils.sendMsg(socket, msgChat);
// 添加到消息记录框
addMsg(" 我对" + dstUser + "说:" + mainUI.GetTextAreaMsg().getText());
// if (null != msgChat) {
// try {
// dos.writeUTF(msgChat);
// dos.flush();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
}
// 线程主体
@Override
public void run() {
ConnectServer();
//发送消息按钮
mainUI.GetBtnSend().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SendChatMsg();
}
});
//退出按钮
mainUI.GetBtnQuit().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DisConnectServer();
}
});
//消息处理
while (isLogged) {
try {
String msg = dis.readUTF();
String[] parts = msg.split("#");
switch (parts[0]) {
// 处理服务器发来的用户列表报文
case "USERLIST":
String[] self = { username };
UpdateJList(mainUI.GetListUsers(), self, "ADD");
for (int i = 1; i < parts.length; i++) {
mainUI.GetModelUsers().addElement(parts[i]);
}
break;
// 处理服务器发来的新用户连接表报文
case "CONNECT":
mainUI.GetModelUsers().addElement(parts[1]);
addMsg(" " + parts[1] + "上线了");
break;
case "DISCONNECT":
mainUI.GetModelUsers().removeElement(parts[1]);
addMsg(" " + parts[1] + "下线了");
break;
case "TALKTO_ALL":
addMsg(parts[1] + " 跟所有人说:" + parts[2]);
break;
case "TALKTO":
addMsg(parts[1] + " 跟我说:" + parts[2]);
break;
default:
break;
}
} catch (IOException e) {
isLogged = false;
e.printStackTrace();
}
}
}
// 添加消息到文本框textAreaRecord
public void addMsg(String msg) {
// 在文本区中添加一条消息,并加上换行
mainUI.GetTextAreaRecord().append(msg + "\n");
// 自动滚动到文本区的最后一行
mainUI.GetTextAreaRecord().setCaretPosition(mainUI.GetTextAreaRecord().getText().length());
}
}
}
3、ConnectMysql包里面
(1)Connect代码如下:
package ConnectMysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Connect {
private Connection con;
public Connection GetConnection() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/student?useSSL=false&serverTimezone=UTC",
"root", "249913");
System.out.println("数据库连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
//关闭数据库
public void CloseConn(ResultSet rs, PreparedStatement pstm, Connection conn) throws Exception {
if (rs != null) {
rs.close();
}
if (pstm != null) {
pstm.close();
}
if (conn != null) {
conn.close();
}
}
}
(2)OperatorMysql代码如下
package ConnectMysql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class OperatorMysql {
Connect connect = new Connect();
private Connection con;
// 增加数据(增加数据前先查询用户名是否已存在)
public void Insert(String userName, String userPassword) {
con = connect.GetConnection();
PreparedStatement pstm = null;
String sql_insert = "insert into user(userName,userPassword) values(?,?)";
try {
pstm = con.prepareStatement(sql_insert);
pstm.setString(1, userName);
pstm.setString(2, userPassword);
pstm.executeUpdate();
connect.CloseConn(null, pstm, con);
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 查找数据(用户名重复返回false)
public Boolean Search(String userName) {
con = connect.GetConnection();
PreparedStatement pstm = null;
ResultSet rs = null;
String sql_Search = "select *from user where userName=?";
try {
pstm = con.prepareStatement(sql_Search);
pstm.setString(1, userName);
rs = pstm.executeQuery();
if(rs.next())
{
return false;
}
} catch (SQLException e) {
e.printStackTrace();
}
return true;
}
}
4、ServerGUI包里面
(1)ServerUI代码如下:
package ServerGUI;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import ServerSide.Server;
public class ServerUI {
// 单例
private static ServerUI serverUIInstance = null;
public static synchronized ServerUI GetServerUI() {
if (serverUIInstance == null) {
serverUIInstance = new ServerUI();
}
return serverUIInstance;
}
private JFrame frmTcp;
private JTextField textServerIP;
private JTextField textPort;
private JButton btnStart;
private JButton btnStop;
private JTextArea textAreaRecord;
private JTable tblUsers;// 在线用户列表
public JTextField GetTextServerIP() {
return textServerIP;
}
public JTextField GetTextPort() {
return textPort;
}
public JButton GetBtnStart() {
return btnStart;
}
public JButton GetBtnStop() {
return btnStop;
}
public JTextArea GetTextAreaRecord() {
return textAreaRecord;
}
private ServerUI() {
Init();
}
private void Init() {
frmTcp = new JFrame();
frmTcp.setResizable(false);
frmTcp.setTitle("服务器界面");
frmTcp.setBounds(100, 100, 700, 509);
frmTcp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmTcp.getContentPane().setLayout(null);
// IP框
JLabel lblip = new JLabel("服务器IP地址:");
lblip.setBounds(10, 27, 105, 18);
frmTcp.getContentPane().add(lblip);
textServerIP = new JTextField();
textServerIP.setText("0.0.0.0");
textServerIP.setBounds(114, 24, 141, 24);
frmTcp.getContentPane().add(textServerIP);
textServerIP.setColumns(10);
// 端口框
JLabel label = new JLabel("端口号:");
label.setBounds(293, 27, 72, 18);
frmTcp.getContentPane().add(label);
textPort = new JTextField();
textPort.setText("6666");
textPort.setBounds(356, 24, 86, 24);
frmTcp.getContentPane().add(textPort);
textPort.setColumns(10);
// 开始按钮
btnStart = new JButton("开始");
btnStart.setBounds(479, 23, 95, 27);
frmTcp.getContentPane().add(btnStart);
// 启动按钮事件监听处理
btnStart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// 创建、启动服务器通信线程
Thread serverThread = new Thread(new Server().new ServerThread());
serverThread.start();
}
});
// 停止按钮
btnStop = new JButton("停止");
btnStop.setEnabled(false);
btnStop.setBounds(588, 23, 95, 27);
frmTcp.getContentPane().add(btnStop);
// 停止按钮事件监听处理
btnStop.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
}
});
JLabel label_1 = new JLabel("消息记录");
label_1.setBounds(10, 58, 72, 18);
frmTcp.getContentPane().add(label_1);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 78, 673, 159);
frmTcp.getContentPane().add(scrollPane);
textAreaRecord = new JTextArea();
textAreaRecord.setBackground(Color.pink);
textAreaRecord.setEditable(false);
scrollPane.setViewportView(textAreaRecord);
JLabel lblNewLabel = new JLabel("在线用户列表");
lblNewLabel.setBounds(10, 250, 105, 18);
frmTcp.getContentPane().add(lblNewLabel);
JScrollPane scrollPane_1 = new JScrollPane();
scrollPane_1.setBounds(10, 273, 673, 191);
frmTcp.getContentPane().add(scrollPane_1);
// 在线用户列表
tblUsers = new JTable();
tblUsers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tblUsers.setModel(new DefaultTableModel(new Object[][] {}, new String[] { "序号", "用户名",
"IP地址", "端口号", "登录时间" }) {
private static final long serialVersionUID = 1L;
boolean[] columnEditables = new boolean[] { true, true, true, false, true };
public boolean isCellEditable(int row, int column) {
return columnEditables[column];
}
});
tblUsers.getColumnModel().getColumn(0).setPreferredWidth(56);
tblUsers.getColumnModel().getColumn(1).setPreferredWidth(118);
tblUsers.getColumnModel().getColumn(2).setPreferredWidth(126);
tblUsers.getColumnModel().getColumn(3).setResizable(false);
tblUsers.getColumnModel().getColumn(3).setPreferredWidth(61);
tblUsers.getColumnModel().getColumn(4).setPreferredWidth(122);
scrollPane_1.setViewportView(tblUsers);
// 更换样式
try {
String style = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
UIManager.setLookAndFeel(style);
SwingUtilities.updateComponentTreeUI(this.frmTcp);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ServerUI serverUI = ServerUI.GetServerUI();
serverUI.frmTcp.setVisible(true);
}
}
5、ServerSide包里面
(1)Server代码
package ServerSide;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.HashMap;
import ConnectMysql.OperatorMysql;
import ServerGUI.ServerUI;
public class Server {
private static Server serverInstance=null;
public static synchronized Server GetServer()
{
if(serverInstance==null)
{
serverInstance=new Server();
}
return serverInstance;
}
// 对数据库进行操作
private static OperatorMysql operatorMysql = new OperatorMysql();
private String userName;
private String userPassword;
ServerUI serverUI = ServerUI.GetServerUI();
// 服务器套接字
private ServerSocket server;
// 服务器是否运行
private boolean isRunning;
// 用户名不能相同,存放每个用户和客户端套接字的映射。
private HashMap<String, ClientHandler> clientHandlerMap = new HashMap<String, ClientHandler>();
// 注册
public Boolean Register(String userName, String userPassword) {
if (operatorMysql.Search(userName)) {
operatorMysql.Insert(userName, userPassword);
this.userName = userName;
this.userPassword = userPassword;
return true;
}
return false;
}
// 登录
public Boolean LogOn(String userName, String userPassword) {
if (operatorMysql.Search(userName)) {
return false;
}
return true;
}
// 内部类,服务器后台线程
public class ServerThread implements Runnable {
// 启动
private void startServer() {
try {
// 获取serverIp 和 serverPort
String serverIp = serverUI.GetTextServerIP().getText();
int serverPort = Integer.parseInt(serverUI.GetTextPort().getText());
// 创建套接字地址
SocketAddress socketAddress = new InetSocketAddress(serverIp, serverPort);
// 创建ServerSocket,绑定套接字地址
server = new ServerSocket();
server.bind(socketAddress);
// 修改判断服务器是否运行的标识变量
isRunning = true;
// 修改启动和停止按钮状态
serverUI.GetBtnStart().setEnabled(false);
serverUI.GetBtnStop().setEnabled(true);
addMsg("服务器启动成功");
System.out.println("服务器启动成功");
} catch (IOException e) {
addMsg("服务器启动失败,请检查端口是否被占用");
e.printStackTrace();
isRunning = false;
}
}
// 线程主体
@Override
public void run() {
startServer();
CloseServer();
// 当服务器处于运行状态时,循环监听客户端的连接请求
while (isRunning) {
try {
Socket socket = server.accept();
// 创建与客户端交互的线程
Thread thread = new Thread(new ClientHandler(socket));
thread.start();
} catch (IOException e) {
}
}
}
}
public void CloseServer() {
serverUI.GetBtnStop().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
isRunning = false;
server.close();
clientHandlerMap.clear();
// 修改按钮状态
serverUI.GetBtnStart().setEnabled(true);
serverUI.GetBtnStop().setEnabled(false);
addMsg("服务器关闭成功");
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
}
// 添加消息到文本框textAreaRecord
private void addMsg(String msg) {
// 在文本区中添加一条消息,并加上换行
serverUI.GetTextAreaRecord().append(msg + "\n");
// 自动滚动到文本区的最后一行
serverUI.GetTextAreaRecord().setCaretPosition(serverUI.GetTextAreaRecord().getText().length());
}
//更新服务器在线用户表
public void UpdateUserTbl() {
}
//获取IP地址
public Object GetInetAddress() {
return null;
}
//获取端口号
public int GetPort()
{
return 6666;
}
// 内部类,用于和客户端交互
class ClientHandler implements Runnable {
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
private boolean isConnected;
private String username;
public ClientHandler(Socket socket) {
this.socket = socket;
try {
this.dis = new DataInputStream(socket.getInputStream());
this.dos = new DataOutputStream(socket.getOutputStream());
isConnected = true;
} catch (IOException e) {
isConnected = false;
e.printStackTrace();
}
}
@Override
public void run() {
while (isRunning && isConnected) {
try {
// 读取客户端发送的报文
String msg = dis.readUTF();
String[] parts = msg.split("#");
switch (parts[0]) {
// 处理登录报文
case "CONNECT":
String connectUsername = parts[1];
// 如果该用户名已登录,则返回失败报文,否则返回成功报文
if (clientHandlerMap.containsKey(connectUsername)) {
dos.writeUTF("FAIL");
} else {
dos.writeUTF("SUCCESS");
// 将此客户端处理线程的信息添加到clientHandlerMap中
clientHandlerMap.put(connectUsername, this);
// 将现有用户的信息发给新用户
StringBuffer msgUserList = new StringBuffer();
msgUserList.append("USERLIST#");
for (String username : clientHandlerMap.keySet()) {
msgUserList.append(username + "#");
}
dos.writeUTF(msgUserList.toString());
// 将新登录的用户信息广播给其他用户
String msgLogin = "CONNECT#" + connectUsername;
BroadcastMsg(connectUsername, msgLogin);
// 存储登录的用户名
this.username = connectUsername;
UpdateUserTbl();// 未实现
}
break;
// 处理退出报文
case "DISCONNECT":
clientHandlerMap.remove(username);
String msgLogout = "DISCONNECT#" + username;
BroadcastMsg(username, msgLogout);
isConnected = false;
dis.close();
dos.close();
socket.close();
UpdateUserTbl();
break;
case "TALKTO_ALL":
String msgTalkToAll = "TALKTO_ALL#" + username + "#" + parts[1];
BroadcastMsg(username, msgTalkToAll);
break;
case "TALKTO":
ClientHandler clientHandler = clientHandlerMap.get(parts[1]);
if (null != clientHandler) {
String msgTalkTo = "TALKTO#" + username + "#" + parts[2];
clientHandler.dos.writeUTF(msgTalkTo);
clientHandler.dos.flush();
}
default:
break;
}
} catch (IOException e) {
isConnected = false;
e.printStackTrace();
}
}
}
// 将某个用户发来的消息广播给其它用户
private void BroadcastMsg(String fromUsername, String msg) throws IOException {
for (String toUserName : clientHandlerMap.keySet()) {
if (fromUsername.equals(toUserName) == false) {
DataOutputStream dos = clientHandlerMap.get(toUserName).dos;
dos.writeUTF(msg);
dos.flush();
}
}
}
}
}
6、Utils包里面
(1)Utils代码
package Utils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JTextArea;
public class Utils {
// 通过套接字S发送字符串
public static void sendMsg(Socket s, String msg) {
try {
// 字符流
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// 在套接字S上读取字符串,如果TCP连接关闭,返回null
public static String recvMsg(Socket s) throws IOException {
String msg = null;
DataInputStream dis = (DataInputStream) new DataInputStream(s.getInputStream());
msg = ((DataInputStream) dis).readUTF();
return msg;
}
// 添加消息到文本记录框JTextArea,并且滚动显示到最后一行
public static void addMsgRec(JTextArea textArea, String msg) {
textArea.append(msg + "\n");
textArea.setCaretPosition(textArea.getText().length());
}
}
最后添加一个Mysql的库文件就可以连接数据库了。
测试如下:
首先,打开mysql数据库,然后启动服务器启动成功,如下图。
然后启动登录界面,如下图:
点击注册按钮,进入注册界面,如下图:
进行注册,如果注册成功就会进行注册成功提示,如下图:
如果用户名已经注册就会进行注册失败提示,如下图:
点击返回按钮进入登录界面进行登录,如果用户名不存在会提示登录失败,如下图:
如果不输入任何东西点击登录也会提示用户进行输入,如下图:
登录成功以后就进入客户端主界面,如下图:
界面上有在线用户和上线提示。
然后通过重复前面的步骤在开两个客户端,此时界面,如下图:
然后就可以进行聊天了,首先默认的是群聊,如下图:
群聊正常的实现了。
私聊主要就是选择私聊按钮,然后选择想要私聊的用户,如果不选择私聊用户,就会进行提醒,如下图:‘
选择在线用户后就可以进行私聊了,如下图:
正常实现了,不过这里还有一个问题就是如果选择自己的话也能进行私聊,这就不对了,如下图:
因为时间原因,服务器端没有全部进行完善,还有一部分功能没有实现,如下图:
源代码下载:下载链接