利用Java编写的一个简单的群聊程序,模仿QQ群聊,利用本地通讯,实现了简单的群聊功能。

源码请自取:
csdn资源下载传送门(免费)

程序共分为3个包 (先编译运行TerminalServerView.java,再运行LoginView.java):

  1. client包中存放辅助实现客户端功能的类
  2. server包中存放辅助实现服务终端功能的类
  3. view包中存放界面相关的类

附各模块代码:

目录:

  • 1. client包
    • 1.1 package-info.java
    • 1.2 Client.java
  • 2. server包
    • 2.1 package-info.java
    • 2.2 UserSupervision.java
    • 2.3 TerminalServer.java
  • 3. view包
    • 3.1 package-info.java
    • 3.2 TerminalServerView.java
    • 3.3 LoginView.java
    • 3.4 ChatView.java
    • 3.5 WarningView.java

1. client包

1.1 package-info.java

/*** 提供帮助实现客户端功能的类* * @author JuJunjian* @version 1.0 2020-11-21*/
package com.jujunjian.client;

1.2 Client.java

package com.jujunjian.client;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;/*** 实现了客户端的功能* * @author JuJunjian* @version 1.0 2020-11-21*/
public class Client {private static final String LOCALHOST = "127.0.0.1";private static final String CHARSET = "UTF-8"; // 编码方式private static final int TERMINAL_PORT = 8888; // 服务端的端口号private Socket socket;private InputStream inputStream;private OutputStream oututStream;/*** 实例化一个客户端对象*/public Client() {try {socket = new Socket(LOCALHOST, TERMINAL_PORT);inputStream = socket.getInputStream();oututStream = socket.getOutputStream();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 向服务终端申请账号的请求* * @param id 用户申请的账号,id为0代表申请一个随机的账号*/public void applyId(int id) {byte[] byteId = toByte(id);try {oututStream.write(byteId);oututStream.flush();} catch (IOException e) {e.printStackTrace();}}/*** @return 返回申请账号的结果*/public int getApplyIdResult() {int result = -1;byte[] byteResult = new byte[4];try {inputStream.read(byteResult);result = toInt(byteResult);} catch (IOException e) {e.printStackTrace();}return result;}/*** 调用此方法以发送消息* * @param message 需发送的消息*/public void sendMessage(String message) {try {byte[] byteMessage = message.getBytes(CHARSET);int length = byteMessage.length;byte[] head = toByte(length); // 字节流前的报头,代表了消息字节流的长度,用于辅助接收端判断是否接收完毕// 得到最终被传输的字节流byte[] outByte = new byte[length + head.length];System.arraycopy(head, 0, outByte, 0, head.length);System.arraycopy(byteMessage, 0, outByte, head.length, byteMessage.length);oututStream.write(outByte);oututStream.flush();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 用于接收新消息,每次调用只可接收一条新消息* * @return 返回接收到的信息(字符串类型)*/public String startReceiveMessage() {String receiveMessage = "";byte[] head = new byte[4]; // 消息的长度try {inputStream.read(head);int messageLength = toInt(head);byte[] byteMessage = new byte[messageLength];inputStream.read(byteMessage);receiveMessage = new String(byteMessage, CHARSET);} catch (SocketException e) {} catch (IOException e) {e.printStackTrace();}return receiveMessage;}/*** 调用此方法可用结束与服务终端的通讯*/public void endCommunication() {try {int result = -1; // -1 表示结束整个群聊byte[] end = toByte(result); oututStream.write(end);oututStream.flush();socket.close();} catch (IOException e) {e.printStackTrace();}}// 将int类型的数据转换为4位byte类型的数据private byte[] toByte(int intData) {byte[] byteData = new byte[4];byteData[0] = (byte) ((intData >> 24) & 0xFF);byteData[1] = (byte) ((intData >> 16) & 0xFF);byteData[2] = (byte) ((intData >> 8) & 0xFF);byteData[3] = (byte) (intData & 0xFF);return byteData;}// 将4位byte类型的数据转换为int类型的数据private int toInt(byte[] byteData) {int intData = 0;for (int i = 0; i < byteData.length; i++) {int shift = (3 - i) * 8;intData += ((byteData[i] & 0xFF) << shift);}return intData;}}

2. server包

2.1 package-info.java

/*** 提供了一个简单的服务端,用以接收、回馈和转发客户端的消息* * @author JuJunjian* @version 1.0 2020-11-20*/
package com.jujunjian.server;

2.2 UserSupervision.java

package com.jujunjian.server;import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 用于存储已注册用户的信息,同时提供向已注册用户发送消息、增加用户、删除用户以及其它一些辅助功能* * @author JuJunjian* @version 1.0 2020-11-20*/
public class UserSupervision {private static final String CHARSET = "UTF-8"; // 传输数据的编码方式private static final int SEND_THREADS_NUMBER = 10;private static final int MAX_SIZE = 1000; // 最大聊天人数private static final Integer MIN_ID = 9000;private static final Integer MAX_ID = 9999;private  Map<Integer, OutputStream> userOutputStreamMap;private  Map<Integer, Socket> userSocketMap;private  ExecutorService sendMessagePool;private  int size; // 用于记录当前用户的数量/*** 实例化一个管理群聊用户的对象*/public UserSupervision() {userOutputStreamMap = new HashMap<>();userSocketMap = new HashMap<>();sendMessagePool = Executors.newFixedThreadPool(SEND_THREADS_NUMBER);size = 0;}/*** 检查注册账号是否满足格式要求* * @param id 需检测的账号* @return 账号符合要求返回true;否则返回false*/public boolean isRightId(Integer id) {if (id >= MIN_ID && id <= MAX_ID) {return true;}return false;}/*** 检测群聊人数是否已满* * @return 聊人数是否已满返回true;否则返回false*/public boolean isFull() {return (MAX_SIZE == size);}/*** 检测用户申请的账号是否已经被占用* * @param id 需检测的账号* @return 未被占用返回true;否则返回false*/public synchronized boolean isFreeId(Integer id) {if (userOutputStreamMap.containsKey(id)) {return false;}return true;}/*** 向群聊中添加新用户* * @param id 新注册用户的账号* @param socket 用于和此用户通讯的套接字* @param outputStream 用于向此用户发送消息的输出流* @return 添加成功返回true;否则返回false*/public synchronized boolean addUser(Integer id, Socket socket , OutputStream outputStream) {if (isFull()) {return false;} else {// 将此用户添加到群聊中userSocketMap.put(id, socket);userOutputStreamMap.put(id, outputStream);size++; // 当前群聊人数加一return true;}}/*** 从群聊中移除指定的用户* * @param deletedUserId 需被移除的用户的账号* @return 删除成功返回true;否则返回false*/public synchronized boolean removeUser(Integer deletedUserId) {if (userOutputStreamMap.isEmpty()) {return false;}if (userOutputStreamMap.containsKey(deletedUserId)) {try {// 关闭并移除此用户对应的OutputStream和SocketuserOutputStreamMap.get(deletedUserId).close();userOutputStreamMap.remove(deletedUserId);userSocketMap.get(deletedUserId).close();userSocketMap.remove(deletedUserId);size--; // 当前群聊人数减一return true;} catch (IOException e) {e.printStackTrace();}}return false;}/*** 向群内的其它用户(除了提供消息的用户)发送消息* * @param senderId 发送者的账号* @param message 发送的消息*/public synchronized void sendMessage(Integer senderId, String message) {for (Integer id : userOutputStreamMap.keySet()) {if(id != senderId) {sendMessagePool.submit(() -> {OutputStream out = userOutputStreamMap.get(id);// 将所需发送的消息转换为字节流,编码方式为UTF-8byte[] byteMessage = null;try {byteMessage = message.getBytes(CHARSET); } catch (UnsupportedEncodingException e) {e.printStackTrace();}// head是消息字节前面的报头,代表消息的长度,可辅助接收端判断是否接收完毕int length = byteMessage.length;byte[] head = toByte(length); // 得到最终需要传输的字节流outBytebyte[] outByte = new byte[length + head.length];System.arraycopy(head, 0, outByte, 0, head.length);System.arraycopy(byteMessage, 0, outByte, head.length, byteMessage.length);// 发送消息try {out.write(outByte);out.flush();} catch (IOException e) {e.printStackTrace();}});}}}/*** 获得空闲的账号* * @return 存在可用账号时返回一个可用账号,否则返回-1*/public synchronized Integer getFreeId() {Integer freeId = -1;if (isFull()) {return freeId;}// 遍历以寻找可用账号for (Integer id = MIN_ID; id < MAX_ID; id++) {if (!userOutputStreamMap.containsKey(id)) {freeId = id;break;}}return freeId;}/*** 调用此方法可结束群聊*/public synchronized void end() {userOutputStreamMap.clear();// 关闭所有socketfor (Integer id : userSocketMap.keySet()) {try {userSocketMap.get(id).close();} catch (IOException e) {e.printStackTrace();}}userSocketMap.clear();}/*** 将int类型的数据转换为4位byte类型的数据* * @param intData 传入的int类型的参数* @return 返回转换之后的4位byte类型的数据*/public static synchronized byte[] toByte(int intData) {byte[] byteData = new byte[4];byteData[0] = (byte) ((intData >> 24) & 0xFF);byteData[1] = (byte) ((intData >> 16) & 0xFF);byteData[2] = (byte) ((intData >> 8) & 0xFF);byteData[3] = (byte) (intData & 0xFF);return byteData;}/*** 将4位byte类型的数据转换为int类型的数据* * @param byteData 传入的4位byte类型的参数* @return 返回转换之后的int类型的数据*/public static synchronized int toInt(byte[] byteData) {int intData = 0;for (int i = 0; i < byteData.length; i++) {int shift = (3 - i) * 8;intData += ((byteData[i] & 0xFF) << shift);}return intData;}
}

2.3 TerminalServer.java

package com.jujunjian.server;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 实现了一个简单的群聊服务终端* * @author JuJunjian* @version 1.0 2020-11-20*/
public class TerminalServer {private static final int TERMINAL_PORT = 8888; // 服务端的端口号private static final String CHARSET = "UTF-8"; // 传输数据的编码方式private UserSupervision userManager; // 群聊用户的管理者private ServerSocket terminalServerSocket;private ExecutorService receiveMessagePool;/*** 实例化一个服务终端对象*/public TerminalServer() {userManager = new UserSupervision();receiveMessagePool = Executors.newCachedThreadPool();try {terminalServerSocket = new ServerSocket(TERMINAL_PORT);} catch (IOException e) {e.printStackTrace();}}/*** 调用此方法可用使服务终端开始工作*/public void startRunning() {while (true) {try {Socket socket = terminalServerSocket.accept(); // 开始接收用户端建里连接的请求InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();// 刚建立连接时会接收到4个字节的特殊消息,用于辅助服务端做出后续动作byte[] initialByte = new byte[4];inputStream.read(initialByte);int initialMessage = UserSupervision.toInt(initialByte);// 群聊结束,服务终端退出if (-1 == initialMessage) {userManager.end();}Integer uesrId = 0; // 用户申请的账号boolean isAvailableId = false; // 用于表示账号是否可用if (userManager.isFull()) {int result = 0; // 发送给客户端的数据,0 代表群聊已满byte[] byteResult = UserSupervision.toByte(result);outputStream.write(byteResult);outputStream.flush();} else {if (0 == initialMessage) { // initialMessage等于0表示用户正在申请一个随机的可用账号uesrId = userManager.getFreeId();int result = 3; // 3 代表未知的错误byte[] byteResult = new byte[4];if ((-1 != uesrId) && userManager.addUser(uesrId, socket, outputStream)) {isAvailableId = true;result = uesrId;}byteResult = UserSupervision.toByte(result);outputStream.write(byteResult);outputStream.flush();} else if (!userManager.isRightId(initialMessage)) {int result = 1; // 1 代表账号格式错误byte[] byteResult = UserSupervision.toByte(result);outputStream.write(byteResult);outputStream.flush();} else if (!userManager.isFreeId(initialMessage)) {int result = 2; // 2 代表账号已存在byte[] byteResult = UserSupervision.toByte(result);outputStream.write(byteResult);outputStream.flush();} else {uesrId = initialMessage;int result = 3; // 3 代表未知的错误byte[] byteResult = new byte[4];if (userManager.addUser(uesrId, socket, outputStream)) {isAvailableId = true; // 客户端申请的账号可用result = uesrId;}byteResult = UserSupervision.toByte(result);outputStream.write(byteResult);outputStream.flush();}}if (isAvailableId) {final Integer userFinalId = uesrId;receiveMessagePool.submit(() -> {// 建里连接之后就一直接收对方的消息,直至对方退出while (!socket.isClosed()) {try {// 前四位用于记录消息总长度,或断开通讯的通知(-1表示断开连接)byte[] byteHead = new byte[4];inputStream.read(byteHead);int head = UserSupervision.toInt(byteHead);// 结束与该用户的通讯if (-1 == head) {userManager.removeUser(userFinalId);inputStream.close();socket.close();break;} else {// 读取对方发送的消息byte[] messageByte = new byte[head];inputStream.read(messageByte);String message = new String(messageByte, CHARSET);userManager.sendMessage(userFinalId, message); // 将消息发送给其它用户}} catch (SocketException e) {System.out.println("一位用户已退出");} catch (IOException e) {e.printStackTrace();}}});}} catch (IOException e) {e.printStackTrace();}}}}

3. view包

3.1 package-info.java

/*** 提供客户端的界面,主要包括:聊天界面、登陆界面、警告弹窗、服务终端界面(用于结束服务终端程序)* * @author JuJunjian* @version 1.0 2020-11-21*/
package com.jujunjian.view;

3.2 TerminalServerView.java

package com.jujunjian.view;import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;import javax.swing.JButton;
import javax.swing.JFrame;import com.jujunjian.server.TerminalServer;/*** 服务终端界面,只包含一个结束按钮,用于结束终端服务器* * @author JuJunjian* @version 1.0 2020-11-21*/
@SuppressWarnings("serial")
public class TerminalServerView extends JFrame {private static final String TITLE = "服务终端";private static final String END_BUTTON_TEXT = "结束服务终端";private static final int FRAME_WIDTH = 300;private static final int FRAME_HIGH = 150;private static final int BUTTON_WIDTH = FRAME_WIDTH - 100;private static final int BUTTON_HIGH = FRAME_HIGH - 100;private JButton endButton;private TerminalServer terminalServer;private Thread terminalThread;/*** 实例化了一个服务终端界面*/public TerminalServerView() {// 开启服务终端terminalServer = new TerminalServer();terminalThread = new Thread(() -> {terminalServer.startRunning();});terminalThread.start();// 初始化界面setTitle(TITLE);setSize(FRAME_WIDTH, FRAME_HIGH);setResizable(false);setLayout(new FlowLayout(1, 30, 30));// 为右上角的叉号添加监听器和动作setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {terminalServer = null;System.exit(0);}});// 初始化结束按钮endButton = new JButton(END_BUTTON_TEXT);endButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HIGH));endButton.setFont(new java.awt.Font("宋体", 1, 20));endButton.addActionListener(e -> {terminalServer = null;System.exit(0);});add(endButton);setVisible(true);}public static void main(String[] args) {// 启动终端界面new TerminalServerView();}}

3.3 LoginView.java

package com.jujunjian.view;import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;import com.jujunjian.client.Client;/*** 用户登陆界面,大致包括:提示面板、账号输入面板、必须的按钮* * @author JuJunjian* @version 1.0 2020-11-21*/
@SuppressWarnings("serial")
public class LoginView extends JFrame {private static final Integer MIN_ID = 9000;private static final Integer MAX_ID = 9999;private static final String ERROR_TEXT = "账号有误";private static final String ID_EXIST_TEXT = "账号已存在";private static final String USER_FULL_TEXT = "群聊人数已满";private static final String UNKNOWN_ERROR_TEXT = "未知的错误";private static final String TITLE = "登陆界面";private static final String TIP_TEXT = "请输入账号(9000-9999):";private static final String OK_BUTTON_TEXT = "确定";private static final String GET_FREE_ID_TEXT = "获取随机账号";private static final int FRAME_WIDTH = 400;private static final int FRAME_HIGH = 240;private static final int TEXT_AREA_WIDTH = 350;private static final int TEXT_AREA_HIGH = 35;private static final int BUTTON_WIDTH = 170;private static final int BUTTON_HIGH = 50;private JTextArea inputIdTextArea;private JLabel errorTip;private JButton okButton;private JButton getFreeIdButton;/*** 实例化一个用户登陆界面*/public LoginView() {setTitle(TITLE);setSize(FRAME_WIDTH, FRAME_HIGH);setResizable(false);setLayout(new FlowLayout(1, 10, 10));setLocation(0, 150);// 为右上角的叉号添加监听器和动作setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {new WarningView();}});// 显示提示信息JLabel showTip = new JLabel(TIP_TEXT);showTip.setFont(new java.awt.Font("宋体", 1, 24));// 输入账号的面板inputIdTextArea = new JTextArea();inputIdTextArea.setPreferredSize(new Dimension(TEXT_AREA_WIDTH, TEXT_AREA_HIGH));inputIdTextArea.setFont(new java.awt.Font("宋体", 1, 24));// 输入账号有误时,给出的错误提示errorTip = new JLabel("");errorTip.setFont(new java.awt.Font("宋体", 1, 18));JPanel errorTipPanel = new JPanel(new FlowLayout());errorTipPanel.setPreferredSize(new Dimension(TEXT_AREA_WIDTH, TEXT_AREA_HIGH - 10));errorTipPanel.add(errorTip);// 初始化确认按钮okButton = new JButton(OK_BUTTON_TEXT);okButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HIGH));okButton.setFont(new java.awt.Font("宋体", 1, 20));okButton.addActionListener(e -> {final int uesrId = getInputId(); // 得到用户输入的账号if (-1 == uesrId) { // 账号中存在非数字errorTip.setText(ERROR_TEXT);} else {Client client = new Client(); // 每一个群聊用户都对应了一个客户端对象client.applyId(uesrId); // 向终端申请申请账号int result = client.getApplyIdResult(); // 得到申请账号的结果// 根据申请账号的结果进行处理switch (result) {case 0:  // 群聊人数已满errorTip.setText(USER_FULL_TEXT);break;case 1: // 账号格式错误errorTip.setText(ERROR_TEXT);break;case 2: // 账号已存在errorTip.setText(ID_EXIST_TEXT);break;case 3: // 未知的错误errorTip.setText(UNKNOWN_ERROR_TEXT);break;default:if (uesrId == result) { // 申请成功inputIdTextArea.setText("");new ChatView(client, uesrId);} else { // 未知的错误errorTip.setText(UNKNOWN_ERROR_TEXT);}break;}}});// 初始化随机获得账号按钮getFreeIdButton = new JButton(GET_FREE_ID_TEXT);getFreeIdButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HIGH));getFreeIdButton.setFont(new java.awt.Font("宋体", 1, 20));getFreeIdButton.addActionListener(e -> {Client client = new Client();client.applyId(0); // 申请随机的账号int result = client.getApplyIdResult();if (0 == result) { // 0 代表群聊人数已满errorTip.setText(USER_FULL_TEXT);}  else if ((result >= MIN_ID) && (result <= MAX_ID)) { // 申请成功inputIdTextArea.setText("");new ChatView(client, result);} else {errorTip.setText("未知的错误");}});add(showTip);add(inputIdTextArea);add(errorTipPanel);add(okButton);add(getFreeIdButton);setVisible(true);}// 得到用户输入的Id,读取失败时返回-1private Integer getInputId() {Integer inputId = -1;String stringId = inputIdTextArea.getText();try {inputId = Integer.valueOf(stringId);} catch (Exception e) {}return inputId;}public static void main(String[] args) {new LoginView();}}

3.4 ChatView.java

package com.jujunjian.view;import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;import com.jujunjian.client.Client;/*** 用户群聊天界面,大致包括:消息显示面板、输入面板、发送按钮* * @author JuJunjian* @version 1.0 2020-11-21*/
@SuppressWarnings("serial")
public class ChatView extends JFrame {private static final String TITLE = "用户:";private static final String SEND_BUTTON_TEXT = "发送";private static final int FRAME_WIDTH = 1100;private static final int FRAME_HIGH = 900;private static final int PANEL_WIDTH = FRAME_WIDTH - 50;private static final int CHAT_PANEL_HIGH = 500;private static final int INPUT_PANEL_HIGH = 250;private static final int SEND_BUTTON_WIDTH = 200;private static final int SEND_BUTTON_HIGH = 60;private JPanel chatPanel;private JPanel inputPanel;private JTextArea chatArea;private JTextArea inputArea;private JButton sendButton;private Integer id; // 用户账号private Client client; // 用户对应的客户端对象private Thread receiveThread;private Thread sendThread;private boolean isRunning; // 用户的状态(false表示用户已退出群聊)/*** 实例化一个群聊界面* * @param client 注册界面申请成功之后得到的客户端对象* @param id 用户的账号*/public ChatView(Client client, int id) {// 初始客户端this.client = client;this.id = id;isRunning = true;// 界面初始化setTitle(TITLE + this.id);setSize(FRAME_WIDTH, FRAME_HIGH);setResizable(false);setLocationRelativeTo(null); // 窗口居中setLayout(new FlowLayout(2, 10, 10));// 为右上角的叉号添加监听器和动作setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) { // 用户退出isRunning = false;client.endCommunication(); // 结束通讯dispose();}});// 消息显示面板chatArea = new JTextArea();initialJTextArea(chatArea);chatPanel = new JPanel();initialJPanel(chatPanel, PANEL_WIDTH, CHAT_PANEL_HIGH);chatPanel.add(new JScrollPane(chatArea));// 消息发送面板inputArea = new JTextArea();initialJTextArea(inputArea);inputPanel = new JPanel();initialJPanel(inputPanel, PANEL_WIDTH, INPUT_PANEL_HIGH);inputPanel.add(new JScrollPane(inputArea), BorderLayout.CENTER);// 发送按钮sendButton = new JButton(SEND_BUTTON_TEXT);sendButton.setPreferredSize(new Dimension(SEND_BUTTON_WIDTH, SEND_BUTTON_HIGH));sendButton.setFont(new Font("宋体", 1, 24));sendButton.addActionListener(e -> {sendMessage("用户" + id + ":\n" + inputArea.getText() + "\n\n");chatArea.append("我:\n" + inputArea.getText() + "\n\n");inputArea.setText("");});startReceive(); // 开始接收消息add(chatPanel);add(inputPanel);add(sendButton);setVisible(true);}// 调用此方法发送一次消息private void sendMessage(String message) {sendThread = new Thread(() -> {client.sendMessage(message);});sendThread.start();}// 调用此方法开始接收消息private void startReceive() {receiveThread = new Thread(() -> {while (isRunning) {String message = client.startReceiveMessage();chatArea.append(message);}});receiveThread.start();}// 设置JTextArea的字体private void initialJTextArea(JTextArea textArea) {textArea.setFont(new Font("宋体", 0, 24));}// 设置JPanel的大小和排版private void initialJPanel(JPanel panel, int width, int high) {panel.setPreferredSize(new Dimension(width, high));panel.setLayout(new BorderLayout());}
}

3.5 WarningView.java

package com.jujunjian.view;import java.awt.Dimension;
import java.awt.FlowLayout;import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;import com.jujunjian.client.Client;/*** 警告弹窗,关闭登陆界面时弹出,提示用户关闭此界面将结束整个群聊程序* * @author JuJunjian* @version 1.0 2020-11-21*/
@SuppressWarnings("serial")
public class WarningView extends JFrame {private static final String WARNING_TEXT = "关闭此窗口将结束整个群聊程序,是否关闭?";private static final String OK_BUTTON_TEXT = "确定";private static final String CANCEL_BUTTON_TEXT = "取消";private static final int FRAME_WIDTH = 600;private static final int FRAME_HIGH = 200;private static final int TEXT_LABEL_WIDTH = 600;private static final int TEXT_LABEL_HIGH = 50;private static final int BUTTON_WIDTH = 170;private static final int BUTTON_HIGH = 50;private static final int END_ID = -1; // 结束群聊private JLabel warningLabel;private JButton okButton;private JButton cancelButton;private Thread endThread;/*** 实例化一个警告窗口,尝试结束群聊(关闭登陆界面)时会弹出*/public WarningView() {setSize(FRAME_WIDTH, FRAME_HIGH);setResizable(false);setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // 只关闭当前窗口setLocationRelativeTo(null);setLayout(new FlowLayout(1, 20, 20));// 显示提示信息warningLabel = new JLabel(WARNING_TEXT);warningLabel.setFont(new java.awt.Font("宋体", 1, 24));JPanel warningPanel = new JPanel(new FlowLayout());warningPanel.setPreferredSize(new Dimension(TEXT_LABEL_WIDTH, TEXT_LABEL_HIGH));warningPanel.add(warningLabel);// 初始化确认按钮okButton = new JButton(OK_BUTTON_TEXT);okButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HIGH));okButton.setFont(new java.awt.Font("宋体", 1, 20));okButton.addActionListener(e -> {endThread = new Thread(() -> {Client client = new Client();client.applyId(END_ID); // 告诉服务终端群聊已结束});endThread.start();System.exit(0);});// 初始化取消按钮cancelButton = new JButton(CANCEL_BUTTON_TEXT);cancelButton.setPreferredSize(new Dimension(BUTTON_WIDTH, BUTTON_HIGH));cancelButton.setFont(new java.awt.Font("宋体", 1, 20));cancelButton.addActionListener(e -> {dispose();});add(warningPanel);add(okButton);add(cancelButton);setVisible(true);}}

Java实现简单的本地QQ聊天系统相关推荐

  1. 复习Java第二个项目仿QQ聊天系统 03(两种通信类、登录以及注册功能完善) Java面试题并发编程相关知识生活【记录一个咸鱼大学生三个月的奋进生活】025

    记录一个咸鱼大学生三个月的奋进生活025 复习Java(仿QQ聊天系统03两种通信类.登录以及注册功能完善) TcpSocket类(与服务器进行通信) Server类(服务器类) TcpMessage ...

  2. 复习Java第二个项目仿QQ聊天系统 01(界面部分) Java面试题Redis的过期策略和内存淘汰策略生活【记录一个咸鱼大学生三个月的奋进生活】023

    记录一个咸鱼大学生三个月的奋进生活023 复习Java(仿QQ聊天系统01界面部分) 设置背景(ImgPanel)类 登录界面(LoginFrame)类 注册界面(RegisterFrame)类 好友 ...

  3. 一、JAVA发送简单邮件(QQ邮箱为例)

    以QQ邮箱为例 1.导入jar包:## <dependency><groupId>javax.mail</groupId><artifactId>mai ...

  4. java使用UDP数据报通讯方式QQ聊天系统:服务器、客户端

    全栈工程师开发手册 (作者:栾鹏) java教程全解 java实现UDP服务器.客户端 java实现qq聊天系统,需要知道彼此的ip地址和端口号. 数据传输使用UDP数据包,我们将首先发送数据的设为客 ...

  5. 仿QQ聊天系统(JAVA+MySQL)

    摘  要 仿QQ聊天系统是基于MySQL数据库开发工具进行设计,理论研究与实践相结合,利用计算机技术.数据库技术,java技术等对腾讯QQ聊天系统的开发设计进行研究 以JAVA技术为核心,利用计算机局 ...

  6. Java简单实现本地缓存

    Java简单实现本地缓存 注意:此代码不适合集群部署环境 import java.util.Map; import java.util.concurrent.ConcurrentHashMap;/** ...

  7. Java 开发的QQ聊天系统 窗体程序 功能非常齐全 完整源码

    今天为大家分享一个java语言编的QQ聊天系统,目前系统已经完成了初步功能,后续会进一步完善.整个系统界面漂亮,有完整得源码,希望大家可以喜欢.喜欢的帮忙点赞和关注.一起编程.一起进步 开发环境 开发 ...

  8. java mail张_javaMail操作QQ邮箱发送邮箱 Demo

    最近用到想给项目中加一个功能,就是当项目中某些模块发生异常,在捕获异常后不仅仅写日志抛异常等!还要追加一个发送邮件给该模块的负责人,方便运维定位问题: maven管理的配置: javax.mail m ...

  9. 使用comm在java程序中管理本地端口[回钦波:高级软件工程师]

    使用comm在java程序中管理本地端口 最近在做电信项目,遇到一些专业性的问题,在这里和大家一起分享下comm包下的类及如何使用等问题: 在java程序中,为了实现同本地系统的硬件端口(RS-232 ...

最新文章

  1. Truncate 删除数据
  2. 橘子CPS联盟操作手册2021.09
  3. python3精要(49)-生成器
  4. 批量绘制train和test关于特征上的差异
  5. 【MobX】390- MobX 入门教程(上)
  6. django的url控制系统
  7. mvc mysql linq_MVC3+Linq to sql 显示数据库中数据表的数据
  8. 客户喜欢比价怎么办呢?怎么让客户了解你产品的价值?
  9. Javascript特效:普通倒计时
  10. html 怎么几秒后自动隐藏,js设定DIV显示时间并动态显示时间倒计时多少秒,倒计时完了之后该层自动隐藏。...
  11. 想在Win11上安装数据库?以下连招,轻松解决(全网详细巨无霸)
  12. 用C语言写迎风飘扬的旗帜,驻村旗帜迎风飘扬
  13. 浙江工商大学计算机技术专硕,浙江工商大学会计专硕(MPAcc)项目介绍
  14. Endnote 导出中英文参考文献到Word
  15. phpstudy启动不了mysql数据库_phpstudy的数据库服务不能正常启动该怎么办
  16. 学计算机课业压力,关于学习负担、学业负担及课业负担的一段解释
  17. Android wifi信号强度与图标对应关系
  18. java实现图片上传至本地
  19. Nasdaq股票代码表 zt
  20. c语言把char转化为string,浅析string 与char* char[]之间的转换

热门文章

  1. 优课联盟 实境英语 Test for Unit 8
  2. ps2018下载安装ico格式插件
  3. 安卓开发Java版——UI界面的设计
  4. 出售金鱼。买买提将养的一缸金鱼分五次出售
  5. win8 破解myeclipse10.x
  6. oracle官方最新补丁,最新oracle 补丁下载
  7. [C#]键盘↑↓←→控制图片加速移动
  8. 安装和卸载Django
  9. 轻松将扫描图片转文字
  10. 脚本中如何优雅避免MySQL登录提示信息