JavaGUI--模拟QQ聊天界面私聊群聊
點擊下載源代碼
目錄
JFrame類的幾個重要方法
持久層
服務端創建
用戶注冊模塊
用戶登錄窗口
好友列表和群聊列表模塊
用戶私聊邏輯
群聊創建
群聊消息
JFrame類的幾個重要方法
- Frame類的構造方法,設置所畫出的窗口的名稱:public JFrame(String title)
- 設置該窗口下的所有窗體(panel):public void setContentPane(Container contentPane)
- 設置窗口關閉之后的狀態:public void setDefaultCloseOperation(int operation)
- 設置窗口顯示位置(傳入為null時則默認顯示在屏幕正中央):public void setLocationRelativeTo(Component c)
- 根據布局來確定窗口的最佳大小 public void pack()
- 設置頁面是否顯示(默認不顯示):public void setVisible(boolean b)
持久層
- 創建User對象
- 創建持久層層基礎類,封裝數據源,獲取連接,關閉資源等操作
- 創建用戶持久層模塊
用戶對象和數據庫對應
package com.lin.client.entity;import java.util.Set;/*** Description:* Author: llf* Created in 2019/8/23 16:30*/ public class User {private Integer id;private String userName;private String password;private String brief;private Set<String> userNames;public User() {}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getBrief() {return brief;}public void setBrief(String brief) {this.brief = brief;}public Set<String> getUserNames() {return userNames;}public void setUserNames(Set<String> userNames) {this.userNames = userNames;} }dao層基類
package com.lin.client.dao;import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; import com.alibaba.druid.pool.DruidPooledConnection; import com.lin.client.entity.User; import com.lin.util.CommUtils;import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties;/*** Description:dao層基礎類i,封裝數據源,獲取連接,關閉資源等操作* Author: llf* Created in 2019/8/23 16:30*/ public class BasedDao {//獲取數據源private static DruidDataSource dataSource;static{Properties properties= CommUtils.loadProperties("db.properties");try {dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {System.out.println("獲取數據源失敗");e.printStackTrace();}}//獲取連接protected DruidPooledConnection getConnection() {try {return (DruidPooledConnection) dataSource.getPooledConnection();} catch (SQLException e) {System.out.println("數據庫連接失敗");e.printStackTrace();}return null;}//關閉資源protected void closeResources(Connection connection,Statement statement) {try {if (connection != null) connection.close();if (statement != null) statement.close();} catch (SQLException e) {e.printStackTrace();}}protected void closeResources(Connection connection,Statement statement,ResultSet resultSet) {closeResources(connection,statement);if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}}}用戶查詢和插入DAO模塊
package com.lin.client.dao;import com.alibaba.druid.pool.DruidPooledConnection; import com.lin.client.entity.User; import org.apache.commons.codec.digest.DigestUtils;import java.sql.*;/*** Description:* Author: llf* Created in 2019/8/23 16:30*/ public class AccountDao extends BasedDao{public User userLogin(String userName, String password) {Connection connection=null;PreparedStatement statement=null;ResultSet resultSet=null;try{connection=getConnection();String sql="select * from user where username=? and password=?";statement = connection.prepareStatement(sql);statement.setString(1,userName);//******解密 ********************statement.setString(2, DigestUtils.md5Hex(password));resultSet=statement.executeQuery();if (resultSet.next()) {User user=getUser(resultSet);return user;}}catch (SQLException e) {System.out.println("用戶登陸失敗");e.printStackTrace();}finally {closeResources(connection,statement,resultSet);}return null;}private User getUser(ResultSet resultSet) throws SQLException {User user=new User();user.setId(resultSet.getInt("id"));user.setUserName(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));user.setBrief(resultSet.getString("brief"));return user;}public boolean userReg(User user) {Connection connection=null;PreparedStatement statement=null;try{connection = getConnection();String str="insert into user(username,password,brief) values (?,?,?)";//接收執行sql語句影響的行數,statement=connection.prepareStatement(str, Statement.RETURN_GENERATED_KEYS);statement.setString(1,user.getUserName());//加密 *************statement.setString(2,DigestUtils.md5Hex(user.getPassword()));statement.setString(3,user.getBrief());int rows = statement.executeUpdate();if (rows==1) {return true;}}catch (SQLException ex) {System.out.println("用戶注冊失敗");ex.printStackTrace();}finally {closeResources(connection,statement);}return false;} }服務端創建
- 設置工具類,讀取配置文件中的IP和PORT
- 設置Json格式和Object類之間的相互轉換
- 設置客戶端和服務端之間通信的VO對象
創建客戶端和服務端之間通信的VO對象
public class MessageVO {/*** 客戶端和服務器之間的協議*/private String type;/*** 客戶端與服務端之間發送數據的內容*/private String content;/*** 客戶端告訴服務器要將信息發送給何處,在群聊中也可封裝群聊用戶集合等數據*/private String to;public String getType() {return type;}public void setType(String type) {this.type = type;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public String getTo() {return to;}public void setTo(String to) {this.to = to;} }創建服務端
public class MultiThreadServer {private static final String IP;private static final int PORT;static {//讀取socket配置文件Properties pros= CommUtils.loadProperties("socket.properties");IP=pros.getProperty("address");PORT=Integer.parseInt(pros.getProperty("port"));}public static void main(String[] args) throws IOException {//創建服務端ServerSocket serverSocket=new ServerSocket(PORT);//創建線程池設置連接服務端的人數ExecutorService executors = Executors.newFixedThreadPool(50);for (int i = 0; i < 50; i++) {System.out.println("等待客戶端連接...");Socket client = serverSocket.accept();System.out.println("有新的客戶端連接,端口號為"+client.getPort());//有客戶端連接就創建線程不斷監聽該客戶端發來的信息executors.submit(new ExecuteClient(client));}} }封裝客戶端與服務端的連接
public class Connect2Server {private static final String IP;private static final int PORT;static {Properties properties = CommUtils.loadProperties("socket.properties");IP=properties.getProperty("address");PORT= Integer.parseInt(properties.getProperty("port"));}private Socket client;private InputStream in;private OutputStream out;public Connect2Server() {try {//連接服務器的端口,所以此處與服務器端讀取同一個配置文件client=new Socket(IP,PORT);//獲取連接的輸入流in=client.getInputStream();//獲取連接的輸出流out=client.getOutputStream();} catch (IOException e) {e.printStackTrace();}}public InputStream getIn() {return in;}public OutputStream getOut() {return out;} }用戶注冊模塊
創建GUI Form畫布(窗口):設置相應的窗體名稱,Idea軟件會為我們自動生成窗口的對應的屬性,
分析
當點擊注冊按鈕之后獲取窗體數據,封裝成User對象,調用數據庫進行創建,
? ? ? ? 創建成功,顯示創建成功,創建用戶窗口關閉
? ? ? ? 創建失敗,顯示失敗信息
public class UserReg {private JTextField userNameText;private JPasswordField passwordText;private JTextField brifeText;private JButton regBtn;private JPanel userRegPanel;private AccountDao accountDao=new AccountDao();public UserReg() {JFrame frame = new JFrame("用戶注冊");//包含這個窗口下的所有窗體frame.setContentPane(userRegPanel);//設置關閉即該窗口永久消失frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使窗口顯示在正中央frame.setLocationRelativeTo(null);//根據布局來確定窗口的最佳大小frame.pack();//窗口圖形界面可視, 默認不可視frame.setVisible(true);//點擊注冊按鈕,將信息持久化到db中,成功彈出提示框regBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//獲取表單參數String userName = userNameText.getText();//獲取加密的密碼String password = String.valueOf(passwordText.getPassword());String brief=brifeText.getText();//封裝User保存到數據庫中User user=new User();user.setUserName(userName);user.setPassword(password);user.setBrief(brief);if (accountDao.userReg(user)) {//注冊成功JOptionPane.showMessageDialog(frame,"注冊成功!","提示信息",JOptionPane.INFORMATION_MESSAGE);frame.setVisible(false);}else {//注冊失敗JOptionPane.showMessageDialog(frame,"注冊失敗!","錯誤信息",JOptionPane.ERROR_MESSAGE);}}});} }用戶登錄窗口
用畫布畫出該窗口
分析
用戶在登陸界面當點擊注冊按鈕時,創建注冊按鈕,跳轉到注冊頁面邏輯
用戶在登陸頁面點擊登陸按鈕時,獲取窗體數據,調用數據庫根據密碼和用戶名查詢是否存在該用戶
? ? ? ? ?若存在:提示登陸成功信息,
? ? ? ? ? ? ?? ? ? ? ? ?點擊確定,創建連接,將自己的信息以協議“1”發送到服務端,標識為登陸邏輯,方便服務端轉向,
? ? ? ? ? ? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * 此時?type = 1? ? ?content? = userName
? ? ? ? ? ? ? ? ? ? ? ? 登陸頁面關閉,創建用戶列表界面
? ? ? ??若不存在
? ? ? ? ? ? ? ? ? ? ? ??提示錯誤信息即可
package com.lin.client.service;import com.lin.client.dao.AccountDao; import com.lin.client.entity.User; import com.lin.util.CommUtils; import com.lin.vo.MessageVO;import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.Scanner; import java.util.Set;/*** Description:登陸成功,將當前用戶所有的在線好友發送到服務器* Author: llf* Created in 2019/8/23 19:04*/ public class UserLogin {private JButton regButton;private JButton loginButton;private JTextField userNameText;private JPasswordField passwordText;private JPanel userPanel;private JPanel UserLoginPanel;private JFrame frame;private AccountDao accountDao=new AccountDao();public UserLogin() {frame = new JFrame("用戶登陸");frame.setContentPane(UserLoginPanel);//退出即關閉該對象frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//將窗口置于屏幕中央frame.setLocationRelativeTo(null);//根據頁面布局設置窗口的大小frame.pack();//圖形界面可視frame.setVisible(true);//創建監聽事件,注冊按鈕,當點擊該按鈕,創建出一個注冊頁面regButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {new UserReg();}});//創建監聽事件,登陸按鈕loginButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//獲取表單參數 校驗用戶信息String userName = userNameText.getText();String password = String.valueOf(passwordText.getPassword());User user = accountDao.userLogin(userName,password);if (user!=null) {//顯示成功信息**************JOptionPane.showMessageDialog(frame,"登陸成功","提示信息",JOptionPane.INFORMATION_MESSAGE);//登陸頁面關閉frame.setVisible(false);/*** 與服務器建立連接* 讀取服務端所有好友信息* 新建一個后臺進程不斷讀取服務器發來的信息* type 1* content userName* to 私聊 群聊*///以協議1的方式向服務器發送數據Connect2Server connect2Server=new Connect2Server();MessageVO msg2Server=new MessageVO();msg2Server.setType("1");msg2Server.setContent(userName);String json2Server=CommUtils.object2Json(msg2Server);try {PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");//發送數據到服務器out.println(json2Server);//接收服務器發來的消息,顯示所有在線好友,加載用戶列表/*** type 1* content 在線用戶信息* to*/Scanner in=new Scanner(connect2Server.getIn());if (in.hasNextLine()) {String msgFromServerStr = in.nextLine();MessageVO msgFromServer=(MessageVO) CommUtils.json2Object(msgFromServerStr,MessageVO.class);//將服務器發送過來的信息封裝成一個儲存user對象的Set集合,傳入好友列表進行顯示,Set<String> users = (Set<String>) CommUtils.json2Object(msgFromServer.getContent(), Set.class);System.out.println("所有在線用戶為:" + users);//加載用戶列表界面,將當前用戶名,所有在線好友,與服務器建立連接,傳遞到好友列表界面***********new FriendsList(userName,users,connect2Server);}} catch (UnsupportedEncodingException ex) {ex.printStackTrace();}}else {//登陸失敗,留在當前登陸頁面,顯示錯誤信息JOptionPane.showMessageDialog(frame,"登陸失敗!","錯誤信息",JOptionPane.ERROR_MESSAGE);}}});}public static void main(String[] args) {UserLogin userLogin=new UserLogin();} }服務器端程序
?//type:“1”?
?//content 所有在線好友? ? ?
//緩存當前服務器所有在線的客戶信息private static Map<String,Socket> clients=new ConcurrentHashMap<>();private static class ExecuteClient implements Runnable{private Socket client;private Scanner in;private PrintStream out;public ExecuteClient(Socket client) {this.client=client;try {this.in=new Scanner(client.getInputStream());this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//獲取客戶發來的用戶名String userName=msgFromClient.getContent();/*** 將當前所有在線用戶發送給新用戶* 將新用戶上限信息發送給其他用戶(上線提醒)* 保存新用戶上線信息*///將當前在線的所有用戶發回客戶端MessageVO msg2Client=new MessageVO();msg2Client.setType("1");msg2Client.setContent(CommUtils.object2Json(clients.keySet()));out.println(CommUtils.object2Json(msg2Client));//將新上線的用戶信息發回給當前以在線的所有用戶sendUserLogin("newLogin:"+userName);//將當前用戶注冊到服務端緩存clients.put(userName,client);System.out.println(userName+"上線了!");System.out.println("當前聊天室共有"+clients.size()+"人"); }}}}private void sendUserLogin(String msg) {for(Map.Entry<String,Socket> entry:clients.entrySet()) {Socket socket=entry.getValue();try {PrintStream out=new PrintStream(socket.getOutputStream(),true,"UTF-8");out.println(msg);}catch (IOException e) {e.printStackTrace();}}}}好友列表和群聊列表模塊
用畫布畫出好友列表和群聊列表窗口的靜態部分,動態部分由代碼實現
分析
登陸成功后程序邏輯
服務端
1.保存新用戶上線信息??request
2.將當前所有在線用戶發回給新用戶?response
3.將新用戶上線信息發給所有其他用戶(上線提醒)
客戶端
1.與服務器建立連接,將自己的用戶名與Socket保存到服務端緩存
2.讀取服務端的所有在線好友信息
3.新建一個后臺線程不斷讀取服務器發來的信息
?
?
public class FriendsList {private JButton createGroupBtn;private JScrollPane friendsList;private JScrollPane groupListPanel;private JPanel friendsPanel;private JFrame frame;private String userName;private Set<String> users;private Connect2Server connect2Server;public FriendsList(String userName, Set<String> users, Connect2Server connect2Server) {this.userName = userName;this.users = users;this.connect2Server = connect2Server;frame = new JFrame(userName);frame.setContentPane(friendsPanel);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(400, 300);//使窗口顯示在正中央frame.setLocationRelativeTo(null);//圖形界面可視, 默認不可視frame.setVisible(true);//加載所有在線用戶信息loadUsers();//啟動后臺線程不斷監聽服務器發來的消息*************************************Thread daemonThread = new Thread(new DaemonTask());//設置為守護線程daemonThread.setDaemon(true);daemonThread.start();}//好友列表后臺任務,不斷監聽服務器發來的信息,好友上限提醒,用戶私聊,群聊private class DaemonTask implements Runnable {private Scanner in = new Scanner(connect2Server.getIn());@Overridepublic void run() {while (true) {if (in.hasNextLine()) {//接收服務到發來的信息String strFromServer = in.nextLine();//此時服務器端發來一個Json字串if (strFromServer.startsWith("{")) {} else {if (strFromServer.startsWith("newLogin:")) { //好友上線邏輯String newFriendName = strFromServer.split(":")[1];users.add(newFriendName);JOptionPane.showMessageDialog(frame,newFriendName + "上線了!", "上線提醒", JOptionPane.INFORMATION_MESSAGE);//刷新好友列表loadUsers();}}}}}}private void loadUsers() {//創建好友列表的盤子JLabel[] userLables = new JLabel[users.size()];JPanel friends = new JPanel();//垂直布局friends.setLayout(new BoxLayout(friends, BoxLayout.Y_AXIS));Iterator<String> iterator = users.iterator();int i = 0;while (iterator.hasNext()) {String userName = iterator.next();userLables[i] = new JLabel(userName); //將好友列表加到盤子中friends.add(userLables[i]);i++;}//將所有的好友的數組盤子加到friendsList的大盤子friendsList.setViewportView(friends);//設置滾動條垂直滾動 friendsList.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);//刷新friends.revalidate();friendsList.revalidate();} }用戶私聊邏輯
用畫布畫出該窗口的靜態部分,顯示聊天信息部分由代碼實現
?
分析
客戶端:
主動發送方
1.點擊要私聊的用戶標簽,彈出私聊界面 (緩存私聊界面對象)?
2.按照指定的協議向服務器發送私聊信息
? ? ? ?// type:2
? ? ? // content:sender-msg
? ? ? // to:目標客戶端的用戶名
package com.lin.client.service;import com.lin.util.CommUtils; import com.lin.vo.MessageVO;import javax.swing.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.PrintStream; import java.io.UnsupportedEncodingException;/*** Description:* Author: llf* Created in 2019/8/24 18:55*/ public class PrivateChatGUI {private JTextArea readFromServer;private JPanel privateChatPanel;private JTextField send2Server;private String friendName;private String myName;private Connect2Server connect2Server;private JFrame frame;private PrintStream out;public PrivateChatGUI(String friendName,String myName,Connect2Server connect2Server) {this.friendName=friendName;this.connect2Server=connect2Server;this.myName=myName;try {this.out=new PrintStream(connect2Server.getOut(),true,"UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}frame = new JFrame("與"+friendName+"私聊中...");frame.setContentPane(privateChatPanel);//設置窗口關閉操作,將其設置為隱藏frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);frame.setSize(400,400);frame.setVisible(true);//捕捉輸入框的鍵盤輸入send2Server.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {StringBuilder sb=new StringBuilder();sb.append(send2Server.getText());//當捕捉到按下enterif(e.getKeyCode()==KeyEvent.VK_ENTER) {String msg=sb.toString();MessageVO messageVO=new MessageVO();messageVO.setType("2");messageVO.setContent(myName+"-"+msg);messageVO.setTo(friendName);PrivateChatGUI.this.out.println(CommUtils.object2Json(messageVO));//將自己發送的信息展示到當前私聊界面*******readFromServer(myName+"說:"+ msg);send2Server.setText("");}}});}//給信息接收者public void readFromServer(String s) {readFromServer.append(s+"\n");}public JFrame getFreame() {return this.frame;} }?
服務端:
1.收到客戶端發來的私聊信息,取出目標端Socket,做信息轉發?
? ? ?// type:2
? ? ?// content:sender-msg
? ? ?// to
private static class ExecuteClient implements Runnable{private Socket client;private Scanner in;private PrintStream out;public ExecuteClient(Socket client) {this.client=client;try {this.in=new Scanner(client.getInputStream());this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//用戶登錄邏輯}else if (msgFromClient.getType().equals("2")) {//用戶私聊 Type2//Content:myName-msg//to:friendNameString friendName=msgFromClient.getTo();Socket clientSocket=clients.get(friendName);try{PrintStream out=new PrintStream(clientSocket.getOutputStream(),true,"UTF-8");MessageVO msg2Client=new MessageVO();msg2Client.setType("2");msg2Client.setContent(msgFromClient.getContent());System.out.println("收到私聊消息,內容為:"+msgFromClient.getContent());out.println(CommUtils.object2Json(msg2Client));}catch (IOException ex){ex.printStackTrace();} }}}}信息接收方:
1.判斷服務端發來的消息是否是私聊信息,若是,判斷緩存中是否有該界面?
2.按照指定協議來讀取內容,發送消息
//緩存所有私聊界面private Map<String, PrivateChatGUI> privateChatGUIList = new ConcurrentHashMap<>();private class DaemonTask implements Runnable {private Scanner in = new Scanner(connect2Server.getIn());@Overridepublic void run() {while (true) {if (in.hasNextLine()) {//接收服務到發來的信息String strFromServer = in.nextLine();//此時服務器端發來一個Json字串if (strFromServer.startsWith("{")) {MessageVO messageVO = (MessageVO) CommUtils.json2Object(strFromServer, MessageVO.class);if (messageVO.getType().equals("2")) {String friendName = messageVO.getContent().split("-")[0];String msg = messageVO.getContent().split("-")[1];//判斷此私聊是不是第一次創建if (privateChatGUIList.containsKey(friendName)) {PrivateChatGUI privateChatGUI = privateChatGUIList.get(friendName);privateChatGUI.getFreame().setVisible(true);privateChatGUI.readFromServer(friendName + "說:" + msg);} else {PrivateChatGUI privateChatGUI = new PrivateChatGUI(friendName, userName, connect2Server);privateChatGUIList.put(friendName, privateChatGUI);privateChatGUI.readFromServer(friendName + "說:" + msg);}}} else {if (strFromServer.startsWith("newLogin:")) { //********************** 好友上線邏輯String newFriendName = strFromServer.split(":")[1];users.add(newFriendName);JOptionPane.showMessageDialog(frame,newFriendName + "上線了!", "上線提醒", JOptionPane.INFORMATION_MESSAGE);//刷新好友列表loadUsers();}}}}}}群聊創建
用畫布畫出該窗口的靜態部分,顯示好友列表的復選框部分由代碼實現
分析:點擊提交信息,獲取表單參數,發送到服務端
客戶端
? ? ?// type:3
? ? ?// content:groupName
? ? ?// to:selectedFriends
package com.lin.client.service;import com.lin.util.CommUtils; import com.lin.vo.MessageVO; import javafx.scene.effect.SepiaTone; import sun.plugin2.message.Message;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.util.HashSet; import java.util.Iterator; import java.util.Set;/*** Description:* Author: llf* Created in 2019/8/29 18:43*/ public class CreateGroupGUI {private JPanel createGroupPanel;private JTextField groupNameText;private JButton conformBtn;private JPanel friendLabelPanel;private String myName;private Set<String> friends;private Connect2Server connect2Server;private FriendsList friendsList;public CreateGroupGUI(String myName,Set<String> friends,Connect2Server connect2Server,FriendsList friendsList){this.myName=myName;this.friends=friends;this.connect2Server=connect2Server;this.friendsList=friendsList;JFrame frame = new JFrame("創建群聊");frame.setContentPane(createGroupPanel);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(400,300);//居中frame.setLocationRelativeTo(null);frame.setVisible(true);//將在線好友以checkBox展示到界面中friendLabelPanel.setLayout(new BoxLayout(friendLabelPanel,BoxLayout.Y_AXIS));Iterator<String> iterator=friends.iterator();while (iterator.hasNext()) {String lableName=iterator.next();JCheckBox checkBox=new JCheckBox(lableName);friendLabelPanel.add(checkBox);}friendLabelPanel.revalidate();conformBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {Set<String> selectedFriends=new HashSet<>();Component[] comps=friendLabelPanel.getComponents();for (Component comp : comps) {//向下轉型JCheckBox checkBox= (JCheckBox) comp;if (checkBox.isSelected()) {String labelName= checkBox.getText();selectedFriends.add(labelName);}}//創建記得加入自己的名字selectedFriends.add(myName);//獲取群名String groupName=groupNameText.getText();//發送給服務器MessageVO messageVO=new MessageVO();/*** 協議3*/messageVO.setType("3");messageVO.setContent(groupName);messageVO.setTo(CommUtils.object2Json(selectedFriends));try {PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");out.println(CommUtils.object2Json(messageVO));} catch (UnsupportedEncodingException e1) {e1.printStackTrace();}//將當前群聊界面隱藏frame.setVisible(false);friendsList.addGroup(groupName,selectedFriends);friendsList.loadGroupList();}});}}服務端
//緩存當前服務器所有的群名以及群好友private static Map<String,Set<String>> groups=new ConcurrentHashMap<>(); private static class ExecuteClient implements Runnable{private Socket client;private Scanner in;private PrintStream out;public ExecuteClient(Socket client) {this.client=client;try {this.in=new Scanner(client.getInputStream());this.out=new PrintStream(client.getOutputStream(),true,"UTF-8");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//用戶登陸邏輯}else if (msgFromClient.getType().equals("2")) {//用戶私聊邏輯}else if (msgFromClient.getType().equals("3")) {String groupName=msgFromClient.getContent();Set<String> friends= (Set<String>) CommUtils.json2Object(msgFromClient.getTo(), Set.class);groups.put(groupName,friends);System.out.println("有新的群注冊成功,群名為:"+groupName+",一共有"+groups.size()+"個群");} }}}好友列表窗口
//存儲所有的群名稱和群好友private Map<String, Set<String>> groupMap = new ConcurrentHashMap<>(); public void addGroup(String groupName, Set<String> friends) {groupMap.put(groupName, friends);}public void loadGroupList() {//存儲所有群名稱的標簽JpanelJPanel groupNamePanel = new JPanel();groupNamePanel.setLayout(new BoxLayout(groupNamePanel, BoxLayout.Y_AXIS));JLabel[] labels = new JLabel[groupMap.size()];//Map遍歷Set<String> entries = groupMap.keySet();Iterator<String> iterator = entries.iterator();int i = 0;while (iterator.hasNext()) {String groupName = iterator.next();labels[i] = new JLabel(groupName);//添加到自定義的盤子groupNamePanel.add(labels[i]);i++;}//將自定義盤子加到滾動條中groupListPanel.setViewportView(groupNamePanel);//設置垂直滾動groupListPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);//刷新groupListPanel.revalidate();}群聊消息
用畫布畫出該窗口的靜態部分,顯示好友列表和群聊消息的部分由代碼實現
?
//緩存所有的群聊頁面 群聊名 和群聊界面private Map<String, GroupChatGUI> groupChatGUIMap=new ConcurrentHashMap<>();//群聊點擊事件private class GroupLabelAction implements MouseListener {private String groupName;public GroupLabelAction(String groupName) {this.groupName=groupName;}@Overridepublic void mouseClicked(MouseEvent e) {if (groupChatGUIMap.containsKey(groupName)) {GroupChatGUI groupChatGUI=groupChatGUIMap.get(groupName);groupChatGUI.getFrame().setVisible(true);}else {// 獲得所有的群聊成員Set<String> names=groupMap.get(groupName);GroupChatGUI groupChatGUI=new GroupChatGUI(groupName,names,userName,connect2Server);groupChatGUIMap.put(groupName,groupChatGUI);}}@Overridepublic void mousePressed(MouseEvent e) {}@Overridepublic void mouseReleased(MouseEvent e) {}@Overridepublic void mouseEntered(MouseEvent e) {}@Overridepublic void mouseExited(MouseEvent e) {}}分析
客戶端
群聊發起者?
1.點擊群名稱標簽,彈出群聊界面
2.將群聊內容發送到服務端
//?type:4
//?content:senderName-msg
//?to:groupName
服務端?
1.收到群聊信息,解出群名
2.根據群名稱找到該群的所有用戶的用戶名
3.再根據用戶名找到該用戶的Socket,發送群聊信息
//?type:4
//?content:senderName-msg
//?to:groupName-[該群的所有群成員]
?
@Overridepublic void run() {while (true) {if (in.hasNextLine()) {String jsonStrFromClient = in.nextLine();MessageVO msgFromClient = (MessageVO) CommUtils.json2Object(jsonStrFromClient, MessageVO.class);if (msgFromClient.getType().equals("1")) {//登陸成功邏輯}else if (msgFromClient.getType().equals("2")) {//用戶私聊 }else if (msgFromClient.getType().equals("3")) {//創建群組}else if (msgFromClient.getType().equals("4")) {// type:4// content:myName-msg// to:groupNameString groupName=msgFromClient.getTo();Set<String> names=groups.get(groupName);Iterator<String> iterator=names.iterator();while (iterator.hasNext()) {String socketName=iterator.next();Socket client=clients.get(socketName);try {PrintStream out=new PrintStream(client.getOutputStream(),true,"UTF-8");/*** typr=4* content直接轉發* 組名-所有的群成員*/MessageVO messageVO=new MessageVO();messageVO.setType("4");messageVO.setContent(msgFromClient.getContent());messageVO.setTo(groupName+"-"+CommUtils.object2Json(names));out.println(CommUtils.object2Json(messageVO));System.out.println("服務器發送的群聊消息為:"+messageVO);} catch (IOException e) {e.printStackTrace();}}}}}群聊接收者
1.當第一次收到群信息,先刷新本客戶端的群聊列表信息
2.彈出群聊界面
?
?
總結
以上是生活随笔為你收集整理的JavaGUI--模拟QQ聊天界面私聊群聊的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Joomla 插件 attachment
- 下一篇: C语言经典69题(又易到难)每日更新5道