目录

案例要求:

实现思路:

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.pswp.cn/bicheng/92736.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92736.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/92736.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows内核研究(内存管理-线性地址的管理)

内存管理线性地址的管理 进程空间的地址划分分区x86 32位Windows空指针赋值区0x00000000 - 0x0000FFFF用户模式区0x00010000 - 0x7FFEFFFF64KB禁入区0x7FFF0000 - 0x7FFFFFFF内核0x80000000 - 0xFFFFFFFF线性地址有4GB&#xff0c;但是并不是所有的地方都能访问&#xff08;这里…

【问题解决】使用patch-package修改node-models中的源码

文章目录一、应用场景二、patch-package 和 postinstallpatch-packagepostinstall三、操作步骤1、使用yarn安装patch-package和postinstall-postinstall2、修改package.json3、修改node-model中源码、保存。4、找到修改文件对应的包名5、使用git将新增的patches文件同步到仓库6…

当配置项只支持传入数字,即无法指定单位为rem,需要rem转px

您好&#xff01;针对您 Vue 3 Element Plus 的技术栈&#xff0c;要优雅且符合大厂规范地解决这个问题&#xff0c;最佳实践是创建一个响应式的 Composition API (组合式函数)。 这个方法完全遵循 Vue 3 的设计哲学&#xff0c;具有高内聚、低耦合、可复用、类型安全&#xf…

谷歌搜索 sg_ss 逆向分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;部分python代码sg_ss cp.call(get_sg_…

一个“加锁无效“的诡异现象

加锁了还出问题&#xff1f;从"点击过快"到"状态可控"&#xff1a;多线程共享变量的并发陷阱与实战对策详情如下&#xff1a;在服务端开发中&#xff0c;多线程并发处理客户端请求是提升系统吞吐量的常见手段。最近有位开发者朋友遇到了一个令人费解的问题…

液体泄漏识别误报率↓76%:陌讯多模态融合算法实战解析

原创声明本文为原创技术解析&#xff0c;核心技术参数与架构设计引用自《陌讯技术白皮书》&#xff0c;禁止未经授权的转载与篡改。一、行业痛点&#xff1a;液体泄漏识别的现实挑战在化工生产、食品加工、仓储物流等场景中&#xff0c;液体泄漏的实时监测是保障安全生产的关键…

Y9000P跑开源模型(未完成)

环境信息 1、Y9000笔记本 2、1T空白硬盘 3、ubunut24.04桌面版 一、环境初始化 第一部分&#xff1a;系统初始化 1、安装基础软件 apt-get update apt-get -y install openssh-server openssh-client apt-utils freeipmi ipmitool sshpass ethtool zip unzip nano less git ne…

ARM体系结构

ARM体系结构 编程原理 从源代码到CPU执行过程 #mermaid-svg-M4xemCxDjIQVNNnW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:14px;fill:#333;}#mermaid-svg-M4xemCxDjIQVNNnW .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mer…

基于SpringBoot的高校社团管理系统的设计与实现(代码+LW文档+远程运行)

&#x1f4af;博主&#xff1a;✌全网拥有50W粉丝、博客专家、全栈领域优质创作者、平台优质Java创作者、专注于Java技术领域和毕业项目实战✌&#x1f4af; &#x1f497;开发技术&#xff1a;SpringBoot、Vue、SSM、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、…

F5发布业界首创集成式应用交付与安全平台,开启ADC 3.0新时代

在数字化转型加速与AI技术蓬勃发展的今天&#xff0c;企业对应用性能与安全的需求正经历革命性变革。传统应用架构已难以满足现代混合多云环境与AI驱动型业务场景的严苛要求。全球领先的应用安全和交付服务提供商F5&#xff08;NASDAQ: FFIV&#xff09;&#xff0c;持续推动 F…

SELinux 入门指南

SELinux(Security-Enhanced Linux)是 Linux 内核的一个安全模块&#xff0c;它提供了一种强制访问控制&#xff08;Mandatory Access Control, MAC&#xff09;机制。与传统的 Linux 自主访问控制&#xff08;Discretionary Access Control, DAC&#xff09;不同&#xff0c;SE…

ARMv8 MMU页表格式及地址转换过程分析

1.简介 CPU发出的虚拟地址经过MMU转换后得到物理地址&#xff0c;然后使用物理地址访问真实的硬件。虚拟地址和物理地址的映射关系保存在页表中&#xff0c;MMU需要遍历页表&#xff0c;才能将虚拟地址转换成物理地址。ARM64现在有两种大小的页表描述符&#xff0c;分别是ARMv8…

数据结构---二叉树(概念、特点、分类、特性、读取顺序、例题)、gdb调试指令、时间复杂度(概念、大O符号法、分类)

一、二叉树1、树1&#xff09;概念 树是 n(n > 0) 个结点的有限集合。若 n0 &#xff0c;为空树。在任意一个非空树中&#xff1a;&#xff08;1&#xff09;有且仅有一个特定的根结点&#xff1b;&#xff08;2&#xff09;当 n>1 时&#xff0c;其余结点可分为 …

安全基础DAY1-安全概述

信息安全现状及挑战常见术语信息安全的脆弱性及常见攻击网络环境的开放性其实就是人人可以上网&#xff0c;网上零成本。协议栈自身的脆弱性及常见攻击协议栈自身的脆弱性常见安全风险网络的基本攻击模式物理层--物理攻击前置知识 1.打开Apache服务 cd /etc/init.d ./apache2 s…

Claude Code 的核心能力与架构解析

技术分析介绍&#xff1a;Claude Code 的核心能力与架构解析一、概述 Claude Code 是由 Anthropic 推出的面向开发者的智能编码助手&#xff0c;它不仅仅是一个代码生成工具&#xff0c;更是一个具备记忆、工具调用、自主规划和环境感知能力的“智能代理”&#xff08;Agentic …

Mac 电脑放在环境变量中的通用脚本

mac电脑下放在环境变量中&#xff0c;方便提高效率执行 注&#xff1a;相关路径需要根据实际情况进行更新 需要在 .bash_profile 文件中定义如下&#xff08;路径需要做实际替换&#xff09;&#xff1a; source $HOME/software/scripts/base_profile.sh source $HOME/software…

UE蓝图节点Add Impulse和Add Torque in Radians

​​​​​​​Add Impulse&#xff1a;对刚体施加一次性的线性脉冲&#xff08;瞬时改变量&#xff09;&#xff0c;改变速度&#xff08;与质量有关&#xff0c;除非你勾 bVelChange&#xff09;。Add Torque (in Radians)&#xff1a;对刚体施加转矩/旋转力&#xff08;向量…

大型语言模型幻觉检测与缓解技术研究综述

摘要 本文系统综述了大型语言模型(LLMs)中的幻觉现象及其检测与缓解技术。研究首先从认知机制角度分析了幻觉产生的理论根源&#xff0c;包括模型对语言先验的过度依赖、训练数据偏差以及推理过程中的信息衰减等问题。在技术层面&#xff0c;综述将现有方法归纳为三类&#xff…

【数据结构初阶】--二叉树(二)

&#x1f618;个人主页&#xff1a;Cx330❀ &#x1f440;个人简介&#xff1a;一个正在努力奋斗逆天改命的二本觉悟生 &#x1f4d6;个人专栏&#xff1a;《C语言》《LeetCode刷题集》《数据结构-初阶》 前言&#xff1a;上篇博客我们学习了有关树的概念和相关术语的介绍&…

jmm 指令重排 缓存可见性 Volatile 内存屏障

CPU指令重排 CPU指令重排是指CPU为了提高指令执行效率&#xff0c;可能会对指令的执行顺序进行优化&#xff0c;使得&#xff08;单线程下&#xff09;指令的实际执行顺序与代码中的顺序不同&#xff0c;但结果是一致的。 这种优化是通过乱序执行和缓存读写重排来实现的。 乱序…