目录

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. 使用技术

  1. Socket编程
  2. I/O
  3. 多线程

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小项目——聊天室(多线程版本)相关推荐

  1. Android小项目————聊天室(UI篇)

    Android小项目----聊天室(UI篇) 一.前言 这是所做的第二个android项目,主要目的对暑假所学的java和android知识点进行复习巩固和实践,由于知识所限,目前这个聊天室并不是很完 ...

  2. Android小项目——聊天室

    聊天室简介 简单介绍 更换图标 网络权限 登录界面 activity_main.xml MainActivity.java 选择头像 activity_choose_picture.xml Choos ...

  3. Linux C小项目 —— 聊天室

    多线程的聊天室 服务器端: 实现多用户群体聊天功能(人数上限可设置): 每个用户所发送的消息,其他已连接服务器的用户均可以收到: 用户输入"bye"退出,服务器输入"qu ...

  4. C++小项目(聊天室)——select模型+mysql+花生壳端口映射打造一个可以用外网连接的小qq

    成品展示: B站视频链接 这个小软件是我初学网络编程写的小玩具,记录一下,等学完完成端口模型再利用完成端口写别的好玩的软件,看的课程是这个老师,真的强烈推荐,课程28块钱,老师讲的巨棒,很细,很适合新 ...

  5. java网络编程-聊天室

    目录 V01 # 聊天室客户端(V1) # 聊天室服务端(V1) V02 # 聊天室客户端(V2) # 聊天室服务端(V2) V03 V04 # 聊天室客户端(V4) # 聊天室服务端(V4) V05 ...

  6. java 实现wobsocket 聊天室

    文章目录 一.java 实现wobsocket 聊天室 二.使用步骤 1.pom.xml 引入jar包 2.java 代码(WsServer.java) 3.web 页面代码(room.html) 一 ...

  7. java web聊天室论文_基于Java网页版聊天室的设计与实现毕业论文含开报告及文献综述.doc...

    基于Java网页版聊天室的设计与实现毕业论文含开报告及文献综述 本科生毕业论文(设计) 题 目: 基于Java网页版聊天室的设计与实现 姓 名:学 院: 理学院 专 业: 信息与计算科学 班 级: 信 ...

  8. java 简单的聊天室_Java简单聊天室

    实现Java简单的聊天室 所用主要知识:多线程+网络编程 效果如下图 /** * * @author Administrator * *简单的多人聊天系统--重点:同时性,异步性 *1.客户端:发送消 ...

  9. java web聊天室论文_基于java网页版聊天室的设计与实现毕业论文含开题报告及文献综述.doc...

    基于java网页版聊天室的设计与实现毕业论文含开题报告及文献综述.doc 还剩 52页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价 ...

最新文章

  1. 手机的秘密--按几个键就知道
  2. 卷积神经网络的“封神之路
  3. A - TOYS POJ - 2318
  4. html如何太假icon图标,CSS3 icon font完全指南(CSS3 font 会取代icon图标)
  5. 图像处理(二十四)Gradient Domain High Dynamic Range Compression学习笔记
  6. python opencv pdf脚本之家_OpenCV 3和Qt5计算机视觉应用开发 PDF 影印含源码版
  7. latex中文模板_都8012年了还不用LaTex编辑论文就out了!!
  8. 分布式:分布式系统的设计
  9. html里面判断字段显示,HTML特殊字符显示
  10. 【UE4】 自定义编辑器的放置Actor窗口
  11. WinCC 中批量绑定变量
  12. 计算机原材料费,计算机基础考试原材料
  13. 如何区分电梯卡为id卡ic卡_门禁卡是选择IC卡好还是ID卡好
  14. H5数独游戏开发——游戏通关及重玩
  15. 2016 计蒜之道 初赛 第二场 联想公司的 logo 设计(计蒜客)
  16. java根据前序和中序建树_(Java实现)二叉树---根据前序、中序、后序数组还原二叉树...
  17. Android Google登录接入
  18. SQL Server最受欢迎技巧:解读DBA
  19. wordpress html插件安装,WordPress 插件安装教程及方法
  20. xbel文档_什么是.recently-used.xbel?如何永久删除它?

热门文章

  1. mysql里hdr是什么的缩写_HDR 是什么?有哪些具体介绍?
  2. 华为IPSec高可靠性
  3. 更换笔记本电脑的C壳(华硕笔记本连体键盘更换)
  4. 【每日新闻】AI发展的重要机制; 联想发布数字化转型经验!
  5. 爬虫简单爬取网站信息
  6. ctfshow web入门 命令执行 web29~web77 web118~web124
  7. 百度地图发布《2022年第2季度中国城市交通报告》
  8. PVE 7 虚拟化 Intel UHD630 WIN10 UEFI 关机后 无法启动问题 显卡无法安装问题 intel 显核驱动 错误代码8 问题 解决方案
  9. 华米变心:只因大健康前景太勾引人?
  10. javascript------H5调用手机相机录像拍照并回显(input操作)