当前位置: 首页 > news >正文

Java基础-完成局域网内沟通软件的开发

目录

案例要求:

实现思路:

itheima-chat-server包

src

com.itheima

Constant类:

Server类:

ServerReaderThread类:

itheima-chat-system包

src

com.itheima.ui

ChatEntryFrame类:

ClientChatFrame类:

ClientReaderThread类:

Constant类:

APP启动类:

总结:


案例要求:

实现思路:

itheima-chat-server包

src

com.itheima
Constant类:
package com.itheima;public class Constant {public static final int PORT = 6666;
}
Server类:
package com.itheima;import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;public class Server {// 定义一个集合容器存储所有登录进来的客户端管道,以便将来群发消息给他们.// 定义一个Map集合,键是存储客户端的管道,值是这个管道的用户名称。public static final Map<Socket, String> onLineSockets = new HashMap<>();public static void main(String[] args) {System.out.println("启动服务端系统.....");try {// 1、注册端口。ServerSocket serverSocket = new ServerSocket(Constant.PORT);// 2、主线程负责接受客户端的连接请求while (true) {// 3、调用accept方法,获取到客户端的Socket对象System.out.println("等待客户端的连接.....");Socket socket = serverSocket.accept();// 把这个管道交给一个独立的线程来处理:以便支持很多客户端可以同时进来通信。new ServerReaderThread(socket).start();System.out.println("一个客户端连接成功.....");}} catch (Exception e) {e.printStackTrace();}}
}
ServerReaderThread类:
package com.itheima;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {// 接收的消息可能有很多种类型:1、登录消息(包含昵称) 2、群聊消息 3、私聊消息// 所以客户端必须声明协议发送消息// 比如客户端先发1,代表接下来是登录消息。// 比如客户端先发2,代表接下来是群聊消息。// 先从socket管道中接收客户端发送来的消息类型编号DataInputStream dis = new DataInputStream(socket.getInputStream());while (true) {int type = dis.readInt(); // 1、2、3switch (type){case 1:// 客户端发来了登录消息,接下来要接收昵称数据,再更新全部在线客户端的在线人数列表。String nickname = dis.readUTF();// 把这个登录成功的客户端socket存入到在线集合。Server.onLineSockets.put(socket, nickname);// 更新全部客户端的在线人数列表updateClientOnLineUserList();break;case 2:// 客户端发来了群聊消息,接下来要接收群聊消息内容,再把群聊消息转发给全部在线客户端。String msg = dis.readUTF();sendMsgToAll(msg);break;case 3:// 客户端发来了私聊消息,接下来要接收私聊消息内容,再把私聊消息转发给指定客户端。break;}}} catch (Exception e) {System.out.println("客户端下线了:"+ socket.getInetAddress().getHostAddress());Server.onLineSockets.remove(socket); // 把下线的客户端socket从在线集合中移除updateClientOnLineUserList(); // 下线了用户也需要更新全部客户端的在线人数列表。}}// 给全部在线socket推送当前客户端发来的消息private void sendMsgToAll(String msg) {// 一定要拼装好这个消息,再发给全部在线socket.StringBuilder sb = new StringBuilder();String name = Server.onLineSockets.get(socket);// 获取当前时间LocalDateTime now = LocalDateTime.now();DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a");String nowStr = dtf.format(now);String msgResult = sb.append(name).append(" ").append(nowStr).append("\r\n").append(msg).append("\r\n").toString();// 推送给全部客户端socketfor (Socket socket : Server.onLineSockets.keySet()) {try {// 3、把集合中的所有用户名称,通过socket管道发送给客户端DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(2); // 1代表告诉客户端接下来是在线人数列表信息  2 代表发的是群聊消息dos.writeUTF(msgResult);dos.flush(); // 刷新数据!} catch (Exception e) {e.printStackTrace();}}}private void updateClientOnLineUserList() {// 更新全部客户端的在线人数列表// 拿到全部在线客户端的用户名称,把这些名称转发给全部在线socket管道。// 1、拿到当前全部在线用户昵称Collection<String> onLineUsers = Server.onLineSockets.values();// 2、把这个集合中的所有用户都推送给全部客户端socket管道。for (Socket socket : Server.onLineSockets.keySet()) {try {// 3、把集合中的所有用户名称,通过socket管道发送给客户端DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(1); // 1代表告诉客户端接下来是在线人数列表信息  2 代表发的是群聊消息dos.writeInt(onLineUsers.size()); // 告诉客户端,接下来要发多少个用户名称for (String onLineUser : onLineUsers) {dos.writeUTF(onLineUser);}dos.flush();} catch (Exception e) {e.printStackTrace();}}}
}

itheima-chat-system包

src

com.itheima.ui
ChatEntryFrame类:
package com.itheima.ui;import javax.swing.*;
import java.awt.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;public class ChatEntryFrame extends JFrame {private JTextField nicknameField;private JButton enterButton;private JButton cancelButton;private Socket socket; // 记住当前客户端系统的通信管道public ChatEntryFrame() {setTitle("局域网聊天室");setSize(350, 150);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);setResizable(false); // 禁止调整大小// 设置背景颜色getContentPane().setBackground(Color.decode("#F0F0F0"));// 创建主面板并设置布局JPanel mainPanel = new JPanel(new BorderLayout());mainPanel.setBackground(Color.decode("#F0F0F0"));add(mainPanel);// 创建顶部面板JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));topPanel.setBackground(Color.decode("#F0F0F0"));// 标签和文本框JLabel nicknameLabel = new JLabel("昵称:");nicknameLabel.setFont(new Font("楷体", Font.BOLD, 16));nicknameField = new JTextField(10);nicknameField.setFont(new Font("楷体", Font.PLAIN, 16));nicknameField.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.GRAY),BorderFactory.createEmptyBorder(5, 5, 5, 5)));topPanel.add(nicknameLabel);topPanel.add(nicknameField);mainPanel.add(topPanel, BorderLayout.NORTH);// 按钮面板JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));buttonPanel.setBackground(Color.decode("#F0F0F0"));enterButton = new JButton("登录");enterButton.setFont(new Font("楷体", Font.BOLD, 16));enterButton.setBackground(Color.decode("#007BFF"));enterButton.setForeground(Color.WHITE);enterButton.setBorderPainted(false);enterButton.setFocusPainted(false);cancelButton = new JButton("取消");cancelButton.setFont(new Font("楷体", Font.BOLD, 16));cancelButton.setBackground(Color.decode("#DC3545"));cancelButton.setForeground(Color.WHITE);cancelButton.setBorderPainted(false);cancelButton.setFocusPainted(false);buttonPanel.add(enterButton);buttonPanel.add(cancelButton);mainPanel.add(buttonPanel, BorderLayout.SOUTH);// 添加监听器enterButton.addActionListener(e -> {String nickname = nicknameField.getText(); // 获取昵称nicknameField.setText("");if (!nickname.isEmpty()) {try {login(nickname);// 进入聊天室逻辑: 启动聊天界面。把昵称传给聊天界面。new ClientChatFrame(nickname, socket);this.dispose(); // 关闭登录窗口} catch (Exception ex) {ex.printStackTrace();}} else {JOptionPane.showMessageDialog(this, "请输入昵称!");}});cancelButton.addActionListener(e -> System.exit(0));this.setVisible(true); // 显示窗口}public void login(String nickname) throws Exception {// 立即发送登录消息给服务端程序。// 1、创建Socket管道请求与服务端的socket链接socket = new Socket(Constant.SERVER_IP, Constant.SERVER_PORT );// 2、立即发送消息类型1 和自己的昵称给服务端DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(1); // 消息类型 登录dos.writeUTF(nickname);dos.flush();}public static void main(String[] args) {new ChatEntryFrame();}
}
ClientChatFrame类:
package com.itheima.ui;import javax.swing.*;
import java.awt.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;public class ChatEntryFrame extends JFrame {private JTextField nicknameField;private JButton enterButton;private JButton cancelButton;private Socket socket; // 记住当前客户端系统的通信管道public ChatEntryFrame() {setTitle("局域网聊天室");setSize(350, 150);setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);setLocationRelativeTo(null);setResizable(false); // 禁止调整大小// 设置背景颜色getContentPane().setBackground(Color.decode("#F0F0F0"));// 创建主面板并设置布局JPanel mainPanel = new JPanel(new BorderLayout());mainPanel.setBackground(Color.decode("#F0F0F0"));add(mainPanel);// 创建顶部面板JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));topPanel.setBackground(Color.decode("#F0F0F0"));// 标签和文本框JLabel nicknameLabel = new JLabel("昵称:");nicknameLabel.setFont(new Font("楷体", Font.BOLD, 16));nicknameField = new JTextField(10);nicknameField.setFont(new Font("楷体", Font.PLAIN, 16));nicknameField.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.GRAY),BorderFactory.createEmptyBorder(5, 5, 5, 5)));topPanel.add(nicknameLabel);topPanel.add(nicknameField);mainPanel.add(topPanel, BorderLayout.NORTH);// 按钮面板JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));buttonPanel.setBackground(Color.decode("#F0F0F0"));enterButton = new JButton("登录");enterButton.setFont(new Font("楷体", Font.BOLD, 16));enterButton.setBackground(Color.decode("#007BFF"));enterButton.setForeground(Color.WHITE);enterButton.setBorderPainted(false);enterButton.setFocusPainted(false);cancelButton = new JButton("取消");cancelButton.setFont(new Font("楷体", Font.BOLD, 16));cancelButton.setBackground(Color.decode("#DC3545"));cancelButton.setForeground(Color.WHITE);cancelButton.setBorderPainted(false);cancelButton.setFocusPainted(false);buttonPanel.add(enterButton);buttonPanel.add(cancelButton);mainPanel.add(buttonPanel, BorderLayout.SOUTH);// 添加监听器enterButton.addActionListener(e -> {String nickname = nicknameField.getText(); // 获取昵称nicknameField.setText("");if (!nickname.isEmpty()) {try {login(nickname);// 进入聊天室逻辑: 启动聊天界面。把昵称传给聊天界面。new ClientChatFrame(nickname, socket);this.dispose(); // 关闭登录窗口} catch (Exception ex) {ex.printStackTrace();}} else {JOptionPane.showMessageDialog(this, "请输入昵称!");}});cancelButton.addActionListener(e -> System.exit(0));this.setVisible(true); // 显示窗口}public void login(String nickname) throws Exception {// 立即发送登录消息给服务端程序。// 1、创建Socket管道请求与服务端的socket链接socket = new Socket(Constant.SERVER_IP, Constant.SERVER_PORT );// 2、立即发送消息类型1 和自己的昵称给服务端DataOutputStream dos = new DataOutputStream(socket.getOutputStream());dos.writeInt(1); // 消息类型 登录dos.writeUTF(nickname);dos.flush();}public static void main(String[] args) {new ChatEntryFrame();}
}
ClientReaderThread类:
package com.itheima.ui;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;public class ClientReaderThread extends Thread{private Socket socket;private DataInputStream dis;private ClientChatFrame win;public ClientReaderThread( Socket socket, ClientChatFrame win) {this.win = win;this.socket = socket;}@Overridepublic void run() {try {// 接收的消息可能有很多种类型:1、在线人数更新的数据 2、群聊消息dis = new DataInputStream(socket.getInputStream());while (true) {int type = dis.readInt(); // 1、2、3switch (type){case 1:// 服务端发来的在线人数更新消息updateClientOnLineUserList();break;case 2:// 服务端发送来的群聊消息getMsgToWin();break;}}} catch (Exception e) {e.printStackTrace();}}private void getMsgToWin() throws Exception {// 获取群聊消息String msg = dis.readUTF();win.setMsgToWin(msg);}// 更新客户端的在线用户列表private void updateClientOnLineUserList() throws Exception {// 1、读取有多少个在线用户int count = dis.readInt();// 2、循环控制读取多少个用户信息。String[] names = new String[count];for (int i = 0; i < count; i++) {// 3、读取每个用户的信息String nickname = dis.readUTF();// 4、将每个用户的信息添加到数组names[i] = nickname;}// 5、将集合中的数据展示到窗口上win.updateOnlineUsers(names);}}

Constant类:

package com.itheima.ui;public class Constant {public static final String SERVER_IP = "127.0.0.1";public static final int SERVER_PORT = 6666;
}

APP启动类:

import com.itheima.ui.ChatEntryFrame;public class App {public static void main(String[] args) {new ChatEntryFrame(); // 启动登录界面}
}


总结:

http://www.xdnf.cn/news/1271197.html

相关文章:

  • B.10.01.5-电商系统的设计模式应用实战
  • Day 8: 深度学习综合实战与进阶技术 - 从优化到部署的完整流程
  • 【Datawhale AI夏令营】从Baseline到SOTA:深度剖析金融问答RAG管道优化之路
  • Mybatis进阶
  • 机器学习第七课之支持向量机SVM
  • 本地进行语音文字互转
  • P1890 gcd区间
  • C++11中的移动语义
  • 【无标题】AI 赋能日常效率:实用案例与操作心得分享
  • B.10.01.6-DDD领域驱动设计:从理论到落地的完整指南
  • 数据挖掘2.6 Perceptron Modeling 感知器建模
  • Qdrant Filtering:must / should / must_not 全解析(含 Python 实操)
  • 心灵笔记:正念冥想
  • 解决python错误:playwright._impl._errors.TimeoutError: Timeout 30000ms exceeded.
  • 3.5.2_1 随机访问介质访问控制
  • Python中的Lambda函数详解
  • 【排序算法】④堆排序
  • NTP /Chrony 网络时间协议
  • Leetcode-19. 删除链表的倒数第 N 个结点
  • 比较useCallback、useMemo 和 React.memo
  • 机器学习 K-Means聚类 无监督学习
  • 第4章 程序段的反复执行for语句P115练习题(题及答案)
  • 元宇宙技术如何改变社交方式?
  • 哈希与安全
  • pgAdmin 仪表盘的system部分不能显示,报SYSTEM_STATS扩展没有安装
  • C++ 中的智能指针
  • Qt 综述:从基础到一般应用
  • 机器翻译中的语言学基础详解(包括包括语法、句法和语义学等)
  • 记一次奇异的bug
  • n8n 入门指南:更适合跨境出海搞钱的AI智能体