点击下载源代码

目录

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对象
public class CommUtils {//这里我们使用的是谷歌推出的GSON类,创建对象(建造者模式)   private static final Gson GSON=new GsonBuilder().create();//加载配置文件public static Properties loadProperties(String fileName) {Properties properties=new Properties();InputStream in=CommUtils.class.getClassLoader().getResourceAsStream(fileName);try {properties.load(in);} catch (IOException e) {return null;}return properties;}//将实例化对象转换为JSON格式public static String object2Json(Object obj) {return GSON.toJson(obj);}//将JSON格式转换为实例化对象public static Object json2Object(String jsonStr, Class objClass) {return GSON.fromJson(jsonStr,objClass);}
}

创建客户端和服务端之间通信的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();}
}

用户私聊逻辑

用画布画出该窗口的静态部分,显示聊天信息部分由代码实现

//缓存所有私聊界面private Map<String, PrivateChatGUI> privateChatGUIList = new ConcurrentHashMap<>();//私聊点击事件private class PrivateLabelAction implements MouseListener {private String labelName;//根据标签名称跳转私聊界面public PrivateLabelAction(String labelName) {this.labelName = labelName;}//鼠标点击事件@Overridepublic void mouseClicked(MouseEvent e) {//判断好友列表是否缓存私聊标签,if (privateChatGUIList.containsKey(labelName)) {//找到该标签使其显示PrivateChatGUI privateChatGUI = privateChatGUIList.get(labelName);privateChatGUI.getFreame().setVisible(true);} else {//第一次点击,创建私聊界面PrivateChatGUI privateChatGUI = new PrivateChatGUI(labelName, userName, connect2Server);privateChatGUIList.put(labelName, privateChatGUI);}}//鼠标一直按下事件@Overridepublic void mousePressed(MouseEvent e) {}//鼠标按下释放事件@Overridepublic void mouseReleased(MouseEvent e) {}//鼠标移入执行事件@Overridepublic void mouseEntered(MouseEvent e) {}//鼠标移出事件@Overridepublic void mouseExited(MouseEvent e) {}}

分析

客户端:

主动发送方

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

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;
import java.util.Iterator;
import java.util.Set;/*** Description:* Author:  llf* Created in 2019/8/29 20:30*/
public class GroupChatGUI {private JPanel groupPanel;private JTextArea readFromServer;private JTextField send2Server;private JPanel friendsPanel;private String groupName;private Set<String> friends;private String myName;private Connect2Server connect2Server;private JFrame frame;public GroupChatGUI(String groupName,Set<String> friends,String myName,Connect2Server connect2Server) {frame= new JFrame("GroupChatGUI");frame.setContentPane(groupPanel);frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);frame.setLocationRelativeTo(null);frame.setSize(400,400);frame.setVisible(true);friendsPanel.setLayout(new BoxLayout(friendsPanel,BoxLayout.Y_AXIS));Iterator<String> iterator=friends.iterator();while (iterator.hasNext()) {String lableName=iterator.next();JLabel jLabel=new JLabel(lableName);friendsPanel.add(jLabel);}send2Server.addKeyListener(new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {StringBuilder sb=new StringBuilder();sb.append(send2Server.getText());//捕捉回车按键if (e.getKeyCode()==KeyEvent.VK_ENTER) {String str2Server=sb.toString();/*** type:4* content:myName-msg* to:groupName*/MessageVO messageVO=new MessageVO();messageVO.setType("4");messageVO.setContent(myName+"-"+str2Server);messageVO.setTo(groupName);try {PrintStream out=new PrintStream(connect2Server.getOut(),true,"UTF-8");out.println(CommUtils.object2Json(messageVO));send2Server.setText("");} catch (UnsupportedEncodingException e1) {e1.printStackTrace();}}}});}public void readFromServer(String msg) {readFromServer.append(msg+"\n");}public JFrame getFrame() {return frame;}
}

服务端 
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.弹出群聊界面

 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")) {//私聊客户端逻辑}else if (messageVO.getType().equals("4")) {/*** 收到服务器发来的群聊信息* type* content:sender-msg* to:groupName-[]*/String groupName = messageVO.getTo().split("-")[0];String senderName = messageVO.getContent().split("-")[0];String groupMsg = messageVO.getContent().split("-")[1];//若此名称在群聊列表if (groupMap.containsKey(groupName)) {if (groupChatGUIMap.containsKey(groupName)) {//缓存中有群聊界面  弹出GroupChatGUI groupChatGUI = groupChatGUIMap.get(groupName);groupChatGUI.getFrame().setVisible(true);groupChatGUI.readFromServer(senderName+"说:"+groupMsg);}else {//缓存中没有Set<String> names=groupMap.get(groupName);GroupChatGUI groupChatGUI=new GroupChatGUI(groupName,names,userName,connect2Server);groupChatGUIMap.put(groupName,groupChatGUI);groupChatGUI.readFromServer(senderName+"说:"+groupMsg);}}else {/*** 用户第一次收到群聊消息*/Set<String> friends= (Set<String>) CommUtils.json2Object(messageVO.getTo().split("-")[1],Set.class);groupMap.put(groupName,friends);loadGroupList();//弹出群聊界面GroupChatGUI groupChatGUI = new GroupChatGUI(groupName,friends,userName,connect2Server);groupChatGUIMap.put(groupName,groupChatGUI);groupChatGUI.readFromServer(senderName+"说:"+groupMsg);}}} else {if (strFromServer.startsWith("newLogin:")) {               //**********************   好友上线逻辑String newFriendName = strFromServer.split(":")[1];users.add(newFriendName);JOptionPane.showMessageDialog(frame,newFriendName + "上线了!", "上线提醒", JOptionPane.INFORMATION_MESSAGE);//刷新好友列表loadUsers();}}}}}}

JavaGUI--模拟QQ聊天界面私聊群聊相关推荐

  1. 【uni-app】小程序实现微信在线聊天(私聊/群聊)

    之前学习使用uni-app简单实现一个在线聊天的功能,今天记录一下项目核心功能的实现过程.页面UI以及功能逻辑全部来源于微信,即时聊天业务的实现使用socket.io,前端使用uni-app开发,后端 ...

  2. socket聊天室实现-群聊,私聊,好友列表,完整版

    效果图 登录.好友上线,下线均提示. 点击好友列表所有人,发送消息,既为群聊 点击好友列表,好友名字,既为选中此好友进行私聊 服务器端代码 ChatRoomServer package sram.se ...

  3. 模拟QQ聊天小项目收尾---界面展示服务端与客户端进行信息交互(用到的知识:io,线程,Swing界面,面向对象思想...... )

    大家好,我是一位在java学习圈中不愿意透露姓名并苟且偷生的小学员,如果文章有错误之处,还望海涵,欢迎多多指正 如果你从本文学到有用的干货知识,那么请您尽量点赞,关注,评论,收藏 这两天我一直在设计这 ...

  4. SpringBoot与webSocket实现在线聊天室——实现私聊+群聊+聊天记录保存

    SpringBoot与webSocket实现在线聊天室--实现私聊+群聊+聊天记录保存 引用参考:原文章地址:https://blog.csdn.net/qq_41463655/article/det ...

  5. 项目实战:仿QQ的QQ简洁版2019群聊项目

    ​ 简介: 自我熟练qt的widget的使用,熟悉使用qt的network相关模块写一个仿QQ的QQ简洁版2019群聊项目.哇伊,这是我大学之处一直想写的IM即时通讯系统的,模仿写一个QQ的项目,但是 ...

  6. HTML5模拟微信聊天界面

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. 防qq聊天界面纯HTML5,html5 实现qq聊天的气泡效果

    教程:http://m.blog.csdn.net/blog/yhc13429826359/38778337 写的很好.自己实现的时候,由于img float:left,会脱离文档流,导致结构混乱. ...

  8. UI界面编写(仿QQ聊天界面)

    UI界面编写实战 这里我们模拟QQ聊天的主界面,编写一个简单的聊天界面. 项目描述 首先搭建我们的主界面,在最上边放一个标题栏,然后是一个ListView,用于展示发送的消息,最下边是选择要发送的表情 ...

  9. Socket编程 ------ 模拟QQ聊天工具

    模拟QQ聊天 一.要求 1.一个服务器可以与多个用户同时通讯 2.用户可以通过服务器与用户之间通讯 3.用户可以选择和所有人发消息,也可以选择和某个用户单独发消息 4.服务器要显示当前所有在线人员 5 ...

  10. qt小项目 代码实现简易的QQ聊天界面

    qt小项目 代码实现简易的QQ聊天界面 代码 效果图 总结 代码 myDialog.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QW ...

最新文章

  1. java中String,int,Integer,char、double类型转换
  2. 【Qt】Qt中QJsonObject类
  3. PYTHON作业----编写多级菜单
  4. 06 / LiveVideoStack主编观察:六岁的Frame.io被收购
  5. 达梦数据源配置_达梦DM8 数据库 DEM(Dameng Enterprise Manager) 安装配置
  6. php打印矩阵,PHP实现顺时针打印矩阵(螺旋矩阵)的方法示例
  7. 【Koa】Error: Cannot find module ‘koa-router‘
  8. 积分兑换平台(这是一种剥削么?强烈求拍砖)
  9. 深入浅出妙用 Javascript 中 apply、call、bind
  10. 机器学习笔记——感知机理解(自行取用,并不一定适合每个人)
  11. 手机沙盒隔离软件_Windows 这 5 个自带功能就很好用,你可以少装几个第三方软件了...
  12. AndroidTV开发(十一)Android Tv Launcher自定义RecyclerView
  13. 自制STM32F205最小系统板
  14. matlab传递函数带符号变量,符号传递函数matlab
  15. R绘图笔记 | 生存曲线的绘制
  16. UIWebView中添加活动指示器,来化解用户等待心理
  17. 网络安全工程师做什么?
  18. 一些常用技术文档网站
  19. 到底Python编程好学吗 为什么会如此受欢迎
  20. 如何快速删除卸载残余-注册表

热门文章

  1. 2021清北学堂储备营Day1
  2. 计算机组装与维修论文摘要,计算机组装与维护—本科毕业设计论文摘要.doc
  3. Excel如何简单快速的建立二级下拉菜单?
  4. document 使用方法介绍
  5. 什么是驻点和拐点_临界点、驻点、拐点的定义是什么?
  6. 大白菜 U盘系统指南
  7. 论文阅读|目标检测之CE-FPN,将通道增强运用到上采样,减少信息丢失,同时添加了通道注意力机制
  8. 艾可森 mysql,国足进世界杯有戏!巴西归化球员表决心:中国对我好,我必须努力...
  9. 企业怎么发国际短信?为什么要发国际短信?
  10. Domain Adaptation