Java小项目——聊天室(多线程版本)
目录
1. 前言
2. 功能实现
3. 模块划分
4. 功能分析
4.1 前期分析
4.2 具体实现
5. 使用技术
6. 代码
1. 前言
之前写过单线程版本的聊天室,这次对之前的版本进行扩展与优化,将其升级为一个多线程版本的聊天室,更好的贴近一个真实的聊天工具。
在这个版本中,聊天室具有注册、私聊、群聊以及退出的功能。
2. 功能实现
- 服务器需要实现
- 用户注册
- 用户私聊
- 用户群聊
- 用户退出
- 客户端需要实现
- 发送信息
- 接收信息
3. 模块划分
- 服务器主线程:创建服务器、创建线程池
- 任务线程:进行业务处理
- 客户端主线程:创建客户端、分发任务(输入、输出)
- 两个任务线程:数据写入服务器、从服务器读取线程
4. 功能分析
该版本需要实现一个服务器同时处理多个客户端的请求 -> 利用多线程进行处理
4.1 前期分析
客户端和服务器的创建与连接类似于单线程版本
为了避免硬编码,利用参数确定端口号和地址(也可利用外部文件、交互式输入)
- 利用线程池约束线程创建的数量,防止负载过高。
- 线程池的选择
- 与用户交互相关,任务周期无法确定,不能用 CachedThreadPool
- 该版本需要多线程,不能用 SingleThreadPool
- 选择固定大小线程池 FixedThreadPool
- 线程池大小
- 通过 Runtime.getRuntime().availableProcessors() 方法获得当前设备的CPU个数(Ncpu)
- I/O密集型任务线程并应配置尽可能多的线程,如2*Ncpu个线程的线程池
- 让线程池专门去处理业务,客户端关闭,业务终止。
- 线程池的选择
- 利用循环实现多客户端的支持
- 当一个客户端发出请求时,服务器启动一个线程,去处理数据的传输。
- 数据的传输由一个读数据线程和一个写数据线程完成
4.2 具体实现
服务端实现:
1.循环监听客户端连接
2.维护所有在线的客户端,记录在线人数
3.注册功能:将客户端名称添加到服务器客户端集合中
4.群聊功能:接收客户端发送的消息,在发送给所有客户端(除过自己)
5.私聊功能:客户端与指定客户端间的数据传送
6.退出功能:从服务器客户端集合中移除客户端
客户端实现:
1.命令行的交互式输入输出
2.注册功能:创建Socket,给服务器发送注册消息
3.群聊功能:客户端发送和接收数据
4.私聊功能:客户端指定客户端,发送和接收数据
5.退出功能:向服务器发送退出指令
5. 使用技术
- Socket编程
- I/O
- 多线程
6. 代码
package com.qqy.chat.client.mul;import java.io.IOException;
import java.net.Socket;/*** 客户端* Author:qqy*/
public class MulClient {public static void main(String[] args) {try {String host="127.0.0.1";int port=65521;//先读取地址,再读取端口号if(args.length==2){host=args[0];try{port=Integer.parseInt(args[1]);}catch (NumberFormatException e){System.out.println("指定端口号格式错误,采用默认端口号"+port);port=65521;}}Socket clientSocket=new Socket(host,port);System.out.println("端口号为"+clientSocket.getLocalPort()+"的客户端已创建");System.out.println("已连接上端口号为"+clientSocket.getPort()+"的服务器...");System.out.println("\n请按照提示信息进行操作:");System.out.println("\t请求按行读取");System.out.println("\t注册: register:<userName> 例如: register:lila");System.out.println("\t群聊: groupChat:<message> 例如: groupChat:大家好");System.out.println("\t私聊: privateChat:<userName>:<message> 例如: privateChat:nina:你好呀");System.out.println("\t退出: bye\n");new WriteDatatoServer(clientSocket).start();new ReadDataFromServer(clientSocket).start();} catch (IOException e) {e.printStackTrace();}}
}
package com.qqy.chat.client.mul;import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;/*** 客户端给服务端发送数据的线程* Author:qqy*/
public class WriteDatatoServer extends Thread {private Socket clientSocket;public WriteDatatoServer(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {try {OutputStream out = clientSocket.getOutputStream();OutputStreamWriter writer = new OutputStreamWriter(out);Scanner scanner = new Scanner(System.in);System.out.println("请输入需求—————— ");while (true) {String data=scanner.nextLine();writer.write(data+"\n");writer.flush();if(data.equals("bye")){break;}}clientSocket.close();} catch (IOException e) {e.printStackTrace();}}
}
package com.qqy.chat.client.mul;import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;/*** 客户端从服务端读取数据的线程* Author:qqy*/
public class ReadDataFromServer extends Thread {private Socket clientSocket;public ReadDataFromServer(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {try {InputStream in=clientSocket.getInputStream();Scanner scanner=new Scanner(in);while (true){System.out.println("来自服务器的消息:\n\t"+scanner.nextLine());System.out.println("\n请输入需求—————— ");}} catch (IOException e) {e.printStackTrace();}}
}
package com.qqy.chat.server.mul;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 服务器* Author:qqy*/
public class MulServer {public static void main(String[] args) {try {int port=65521;if(args.length==1){try{port=Integer.parseInt(args[0]);}catch (NumberFormatException e){System.out.println("指定端口号格式错误,采用默认端口号"+port);port=65521;}}ServerSocket serverSocket = new ServerSocket(port);System.out.println("端口号为" + serverSocket.getLocalPort() + "服务器已创建,等待客户端的连接...");//创建线程池ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);//利用循环实现多线程,以支持多用户while (true) {//客户端连接Socket clientSocket = serverSocket.accept();System.out.println("接收到客户端"+clientSocket.getRemoteSocketAddress()+"的连接");/*不在循环中直接进行业务处理∵accept()是阻塞方法,若在循环中直接处理业务,每次循环不能很快结束,阻塞了其他客户端的连接*///线程池分配线程//每次有客户端连接到服务器的时候,就创建一个HandleClient的实例化对象来处理具体的业务executorService.execute(new HandleClient(clientSocket));}} catch (IOException e) {e.printStackTrace();}}
}
package com.qqy.chat.server.mul;import java.io.*;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;/*** Author:qqy*/
public class HandleClient implements Runnable {private static final Map<String, Socket> ONLINE_CLIENT = new ConcurrentHashMap<>();private final Socket client;public HandleClient(Socket client) {this.client = client;}@Overridepublic void run() {try {//获取客户端的输入InputStream in = client.getInputStream();//将字符流转换为字节流Scanner scanner = new Scanner(in);while (true) {String data = scanner.nextLine();if (data.startsWith("register")) {if (data.split(":").length == 2) {String userName = data.split(":")[1];//若当前用户存在if (ONLINE_CLIENT.containsKey(userName)) {sendMsg(this.client, "该用户名已存在,请重新注册!!!", false);} else{register(userName);}} else {sendMsg(this.client, "请输入需要注册的用户名!!!", false);}continue;}if (data.startsWith("groupChat")) {String msg = data.split(":")[1];groupChat(msg);continue;}if (data.startsWith("privateChat")) {String[] require = data.split(":");String object = require[1];String msg = require[2];privateChat(object, msg);continue;}if (data.equals("bye")) {bye();break;}}} catch (IOException e) {e.printStackTrace();}}/*** 客户端退出*/private void bye() {for (Map.Entry<String, Socket> entry : ONLINE_CLIENT.entrySet()) {Socket target = entry.getValue();if (target == this.client) {ONLINE_CLIENT.remove(entry.getKey());break;}}printOnline();}/*** 打印当前在线人数*/private void printOnline() {System.out.println("当前在线人数:" + ONLINE_CLIENT.size());System.out.println("在线用户为:");for (String userName : ONLINE_CLIENT.keySet()) {System.out.println(userName);}}/*** 私聊** @param object 私聊对象* @param msg 发送信息*/private void privateChat(String object, String msg) {Socket target = ONLINE_CLIENT.get(object);if (target == null) {sendMsg(this.client, "用户" + object + "不存在!", false);} else {sendMsg(target, msg, true);}}/*** 群聊** @param msg 发送信息*/private void groupChat(String msg) {for (Map.Entry<String, Socket> entry : ONLINE_CLIENT.entrySet()) {Socket target = entry.getValue();if (target != this.client) {sendMsg(target, msg+"(来自群聊)", true);}}}/*** 用户注册** @param userName 用户名*/private void register(String userName) {//TODO 用户名相同如何处理ONLINE_CLIENT.put(userName, this.client);printOnline();sendMsg(this.client, "恭喜" + userName + ",注册成功", false);}/*** 获取用户名** @return*/private String getUserName() {for (Map.Entry<String, Socket> entry : ONLINE_CLIENT.entrySet()) {Socket target = entry.getValue();if (target == this.client) {return entry.getKey();}}return "";}private void sendMsg(Socket target, String msg, boolean flag) {try {OutputStream out = target.getOutputStream();OutputStreamWriter writer = new OutputStreamWriter(out);if (flag) {writer.write("<" + getUserName() + "说>" + "\t" + msg + "\n");} else {writer.write(msg + "\n");}writer.flush();} catch (IOException e) {e.printStackTrace();}}
}
Java小项目——聊天室(多线程版本)相关推荐
- Android小项目————聊天室(UI篇)
Android小项目----聊天室(UI篇) 一.前言 这是所做的第二个android项目,主要目的对暑假所学的java和android知识点进行复习巩固和实践,由于知识所限,目前这个聊天室并不是很完 ...
- Android小项目——聊天室
聊天室简介 简单介绍 更换图标 网络权限 登录界面 activity_main.xml MainActivity.java 选择头像 activity_choose_picture.xml Choos ...
- Linux C小项目 —— 聊天室
多线程的聊天室 服务器端: 实现多用户群体聊天功能(人数上限可设置): 每个用户所发送的消息,其他已连接服务器的用户均可以收到: 用户输入"bye"退出,服务器输入"qu ...
- C++小项目(聊天室)——select模型+mysql+花生壳端口映射打造一个可以用外网连接的小qq
成品展示: B站视频链接 这个小软件是我初学网络编程写的小玩具,记录一下,等学完完成端口模型再利用完成端口写别的好玩的软件,看的课程是这个老师,真的强烈推荐,课程28块钱,老师讲的巨棒,很细,很适合新 ...
- java网络编程-聊天室
目录 V01 # 聊天室客户端(V1) # 聊天室服务端(V1) V02 # 聊天室客户端(V2) # 聊天室服务端(V2) V03 V04 # 聊天室客户端(V4) # 聊天室服务端(V4) V05 ...
- java 实现wobsocket 聊天室
文章目录 一.java 实现wobsocket 聊天室 二.使用步骤 1.pom.xml 引入jar包 2.java 代码(WsServer.java) 3.web 页面代码(room.html) 一 ...
- java web聊天室论文_基于Java网页版聊天室的设计与实现毕业论文含开报告及文献综述.doc...
基于Java网页版聊天室的设计与实现毕业论文含开报告及文献综述 本科生毕业论文(设计) 题 目: 基于Java网页版聊天室的设计与实现 姓 名:学 院: 理学院 专 业: 信息与计算科学 班 级: 信 ...
- java 简单的聊天室_Java简单聊天室
实现Java简单的聊天室 所用主要知识:多线程+网络编程 效果如下图 /** * * @author Administrator * *简单的多人聊天系统--重点:同时性,异步性 *1.客户端:发送消 ...
- java web聊天室论文_基于java网页版聊天室的设计与实现毕业论文含开题报告及文献综述.doc...
基于java网页版聊天室的设计与实现毕业论文含开题报告及文献综述.doc 还剩 52页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价 ...
最新文章
- 手机的秘密--按几个键就知道
- 卷积神经网络的“封神之路
- A - TOYS POJ - 2318
- html如何太假icon图标,CSS3 icon font完全指南(CSS3 font 会取代icon图标)
- 图像处理(二十四)Gradient Domain High Dynamic Range Compression学习笔记
- python opencv pdf脚本之家_OpenCV 3和Qt5计算机视觉应用开发 PDF 影印含源码版
- latex中文模板_都8012年了还不用LaTex编辑论文就out了!!
- 分布式:分布式系统的设计
- html里面判断字段显示,HTML特殊字符显示
- 【UE4】 自定义编辑器的放置Actor窗口
- WinCC 中批量绑定变量
- 计算机原材料费,计算机基础考试原材料
- 如何区分电梯卡为id卡ic卡_门禁卡是选择IC卡好还是ID卡好
- H5数独游戏开发——游戏通关及重玩
- 2016 计蒜之道 初赛 第二场 联想公司的 logo 设计(计蒜客)
- java根据前序和中序建树_(Java实现)二叉树---根据前序、中序、后序数组还原二叉树...
- Android Google登录接入
- SQL Server最受欢迎技巧:解读DBA
- wordpress html插件安装,WordPress 插件安装教程及方法
- xbel文档_什么是.recently-used.xbel?如何永久删除它?
热门文章
- mysql里hdr是什么的缩写_HDR 是什么?有哪些具体介绍?
- 华为IPSec高可靠性
- 更换笔记本电脑的C壳(华硕笔记本连体键盘更换)
- 【每日新闻】AI发展的重要机制; 联想发布数字化转型经验!
- 爬虫简单爬取网站信息
- ctfshow web入门 命令执行 web29~web77 web118~web124
- 百度地图发布《2022年第2季度中国城市交通报告》
- PVE 7 虚拟化 Intel UHD630 WIN10 UEFI 关机后 无法启动问题 显卡无法安装问题 intel 显核驱动 错误代码8 问题 解决方案
- 华米变心:只因大健康前景太勾引人?
- javascript------H5调用手机相机录像拍照并回显(input操作)