工程构成如下图:

Java实现私聊和群聊功能之美_Java开发

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数据库,然后启动服务器启动成功,如下图。

Java实现私聊和群聊功能之美_Java开发_02

然后启动登录界面,如下图:

Java实现私聊和群聊功能之美_Java_03

点击注册按钮,进入注册界面,如下图:

Java实现私聊和群聊功能之美_Java开发_04

进行注册,如果注册成功就会进行注册成功提示,如下图:

Java实现私聊和群聊功能之美_Java_05

如果用户名已经注册就会进行注册失败提示,如下图:

Java实现私聊和群聊功能之美_Java_06

点击返回按钮进入登录界面进行登录,如果用户名不存在会提示登录失败,如下图:

Java实现私聊和群聊功能之美_Java_07

如果不输入任何东西点击登录也会提示用户进行输入,如下图:

Java实现私聊和群聊功能之美_Java_08

登录成功以后就进入客户端主界面,如下图:

Java实现私聊和群聊功能之美_Java开发_09

界面上有在线用户和上线提示。

然后通过重复前面的步骤在开两个客户端,此时界面,如下图:

Java实现私聊和群聊功能之美_Java_10

然后就可以进行聊天了,首先默认的是群聊,如下图:

Java实现私聊和群聊功能之美_Java开发_11

群聊正常的实现了。

私聊主要就是选择私聊按钮,然后选择想要私聊的用户,如果不选择私聊用户,就会进行提醒,如下图:‘

Java实现私聊和群聊功能之美_Java_12

选择在线用户后就可以进行私聊了,如下图:

Java实现私聊和群聊功能之美_Java_13

正常实现了,不过这里还有一个问题就是如果选择自己的话也能进行私聊,这就不对了,如下图:

Java实现私聊和群聊功能之美_Java开发_14

因为时间原因,服务器端没有全部进行完善,还有一部分功能没有实现,如下图:

Java实现私聊和群聊功能之美_Java开发_15

源代码下载:下载链接