JAVA简单聊天室的实现

2023-11-15

鉴于之前有不少同学在跟我要客户端的代码,我近期整理了一下,把整个工程都传到github上了。地址:https://github.co/Alexlingl/Chatroom

里面有比较详细的工程运行教程,这篇博客则主要对工程的代码实现进行介绍,没有通信知识基础的同学,在看这篇博客之前可以先看下我通信板块的另外几篇博客:

《JAVA通信(一)——输入数据到客户端》

《JAVA通信(二)——实现客户机和服务器通信》

《JAVA通信(三)——实现多人聊天》

前面我们已经了解了通信技术的基本原理,也通过多线程实现了一个服务器同时与多个客户机通信的程序。今天我们来实现一个简单的聊天室。也就是当有客户机给服务器发消息时,这个消息必须同时被发送到其他的客户机。(注意:并不是直接让客户机之间进行连接)

 

一、待实现的聊天室构想

1.我们先来看一下QQ群是怎样运作的。

首先,用户需要通过验证加入到某一个群;加入之后,每个用户都会有自己的一个聊天室界面,这个界面中实时更新所有群成员发送的消息。

2.整体框架图

3.服务器和单一客户机交互图

A.用户信息正确

B.用户信息错误

二、代码架构

按照我们前面的分析,感觉只需要构建服务器和客户机这两个类就可以实现这个聊天室了。但是这样一来就会造成这两个类中包含了过多的方法,有悖于面向对象的“单一职责原则”。(《面向对象的三大特征和六大基本原则》)不利于我们后期对这个程序进行修改扩展。因此这里我们对这两个类进行了更加仔细的职责划分。总共分成以下五个类。

ChatServer类:服务器类,也是主类,里面包含服务器的创建方法setUpServer(int port)和主函数入口main。当程序开始运行时,它会把相应的端口port设置为服务器。并让其始终处于待连接状态。每当有客户机连接上来时,就实例化一个线程类(ServerThread)对象,并启动一个线程去处理。(也就相当于我们为每个用户提供了一个独立的线程)。

ServerThread类:客户端类。它是一个线程类。里面实现了线程的启动方法run()和客户机服务器的通信处理方法processSocket()。当然在通信之前我们必须要先验证这个用户信息是否正确。这个验证方法我们在DaoTool类中实现。这里直接调用它的验证方法即可

DaoTool:用户信息验证类。里面实现了用户信息的验证方法checkLogin()。并且它还储存了一个模拟的用户信息库userDB。

UserInfo:用户信息类。里面保存了每一个用户的信息,包括用户名和密码。定义了获取用户名和密码的方法。

ChatTools:聊天室类。负责保存当前登录的每一个用户,并且当某一个客户机给服务器发了消息,它需要立即把这条消息转发给其他客户机。

细分后的构图如下:

三、具体的代码实现

package communicatetest4;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class ChatServer {
	//主函数入口
	public static void main(String[] args) throws IOException {
		//实例化一个服务器类的对象
		ChatServer cs=new ChatServer();
		//调用方法,为指定端口创建服务器
		cs.setUpServer(9000);
	}

	private void setUpServer(int port) throws IOException {
		// TODO Auto-generated method stub
		ServerSocket server=new ServerSocket(port);
		//打印出当期创建的服务器端口号
		System.out.println("服务器创建成功!端口号:"+port);
		while(true) {
			//等待连接进入
			Socket socket=server.accept();
			System.out.println("进入了一个客户机连接:"+socket.getRemoteSocketAddress().toString());
			//启动一个线程去处理这个对象
			ServerThread st=new ServerThread(socket);
			st.start();
		}
	}
}
package communicatetest4;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;

/*
 * 每当有客户机和服务器连接时,都要定义一个接受对象来进行数据的传输
 * 从服务器的角度看,这个类就是客户端
 */
public class ServerThread extends Thread{
	private Socket client;//线程中的处理对象
	private OutputStream ous;//输出流对象
	private UserInfo user;//用户信息对象
	
	public ServerThread(Socket client) {
		this.client=client;
	}
	
	public UserInfo getOwerUser() {
		return this.user;
	}
	
	public void run() {
		try {
			processSocket();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	//在显示屏中打印信息,例如"用户名"、"密码"等等
	public void sendMsg2Me(String msg) throws IOException {
		msg+="\r\n";
		ous.write(msg.getBytes());
		ous.flush();
	}
	
	
	private void processSocket() throws IOException {
		// TODO Auto-generated method stub
		InputStream ins=client.getInputStream();
		ous=client.getOutputStream();
		BufferedReader brd=new BufferedReader(new InputStreamReader(ins));
		
		sendMsg2Me("欢迎你来聊天,请输入你的用户名:");
		String userName=brd.readLine();
		sendMsg2Me("请输入密码:");
		String pwd=brd.readLine();
		user=new UserInfo();
		user.setName(userName);
		user.setPassword(pwd);
		//调用数据库,验证用户是否存在
		boolean loginState=DaoTools.checkLogin(user);
		if(!loginState) {
			//如果不存在这个账号则关闭
			this.closeMe();
			return;
		}
		ChatTools.addClient(this);//认证成功,把这个用户加入服务器队列
		String input=brd.readLine();
		while(!input.equals("bye")) {
			System.out.println("服务器读到的是:"+input);
			ChatTools.castMsg(this.user, input);
			input=brd.readLine();
		}
		ChatTools.castMsg(this.user, "bye");
		this.closeMe();
	}
	
	//关闭当前客户机与服务器的连接。
	public void closeMe() throws IOException {
		client.close();
	}
	
	
}
package communicatetest4;

import java.io.IOException;
import java.util.ArrayList;

/*
 * 定义一个管理类,相当于一个中介,处理线程,转发消息
 * 这个只提供方法调用,不需要实例化对象,因此都是静态方法
 */
public class ChatTools {
	//保存线程处理的对象
	private static ArrayList<ServerThread> stList=new ArrayList();
	//不需要实例化类,因此构造器为私有
	private ChatTools() {}
	
	//将一个客户对应的线程处理对象加入到队列中
	public static void addClient(ServerThread st) throws IOException {
		stList.add(st);//将这个线程处理对象加入到队列中
		castMsg(st.getOwerUser(),"我上线了!目前人数:"+stList.size());
	}
	
	//发送消息给其他用户
	public static void castMsg(UserInfo sender,String msg) throws IOException {
		msg=sender.getName()+"说:"+msg;//加上说的对象
		for(int i=0;i<stList.size();i++) {
			ServerThread st=stList.get(i);
			st.sendMsg2Me(msg);//发消息给每一个客户机
		}
	}
}
package communicatetest4;

import java.util.HashMap;
import java.util.Map;

//定义一个处理用户登录信息的类
public class DaoTools {
	//内存用户信息数据库
	private static Map<String,UserInfo>userDB=new HashMap();
	//静态块:模拟生成内存中的用户数据,用户名为1~10
	//当程序启动时这段代码会自动执行向userDB放入数据
	static {
		for(int i=1;i<=10;i++) {
			UserInfo user=new UserInfo();
			user.setName("user"+i);
			user.setPassword("psw"+i);
			userDB.put(user.getName(), user);
		}
	}
	
	public static boolean checkLogin(UserInfo user) {
		//在只验证用户名是否存在
		if(userDB.containsKey(user.getName())) {
			return true;
		}
		System.out.println(user.getName()+"用户验证失败!");
		return false;
	}
}
package communicatetest4;
//定义一个用户信息的类
public class UserInfo {
	private String name;//用户名
	private String password;//密码
	private String loignTime;//登录时间
	private String address;//客户机端口名
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		// TODO Auto-generated method stub
		this.name=name;
	}
	
	public void setPassword(String psw) {
		this.password=password;
	}
}

四、程序实现图

五、小总结

1.服务器和客户机的连接。其实在验证用户名和密码之前,客户机就已经和服务器连接上了。如果没有连接,我们是无法把用户名和密码送给服务器进行验证的。只是当我们验证完用户信息后,如果发现这个用户名不存在或者密码不正确时,再去断开客户机和服务器的连接。

2.ChatTools里面都是静态属性和静态方法,因为我们不需要实例化这个类的对象,我们只想要调用它的方法,对静态方法不了解的同学可以看一下我的另一篇博客(《静态方法和非静态方法的区别JAVA》

3、这种基于BIO模型所写出来的服务器性能比较有限,最大并发数大约在2000到2300左右个客户端。后期我进一步提升了服务器的性能,详情可见博客(《C10k破局(一)——线程池和消息队列实现高并发服务器》)。当然使用线程池只是在BIO模型的基础上做了一定的优化,真想要要大幅度地提升服务器性能就只能使用NIO或者AIO模型进行重构,有兴趣的小伙伴可以看下我的另一篇博客(《基于netty NIO开发的聊天室》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA简单聊天室的实现 的相关文章

随机推荐

  • 递归递归递归

    function DG htmlDom n n for var i 0 i lt htmlDom length i var navSubmenu htmlDom i nav submenu var item htmlDom i if nav
  • 2023年Java面试题_Mongodb

    Index Mongodb 1 基本概念 1 1 文档 1 2 集合 1 3 数据类型 1 4 id 和 ObjectId 2 基本操作 3 索引介绍 4 应用场景 4 1 MySQL VS MongoDB 4 2 应用场景 4 3 压测结
  • MySQL——关系型数据库管理系统

    目录 01 数据库 02 SQL 结构化查询语言 关于SQL语句的分类 03 MySQL常用命令 1 退出mysql exit 2 查看mysql中有哪些数据库 3 选择使用某个数据库 4 创建数据库 5 查看某个数据库下有哪些表 6 查看
  • 操作系统读书笔记- 01 x86系统架构概览.md-html

    x86系统架构概览 真看不懂了 今天就写这些吧 2 0 处理器工作模式 一般来讲 x86 64处理器具有5种工作模式 实模式 Real address Mode 处理器以16位8086的方式工作 只能以简单的段地址 偏移地址方式进行寻址 地
  • 在Javascript中怎样判断用户按下的是回车键?

  • 本地chrome,访问(超链接跳转)本地文件解决方案

    问题和背景描述 1 用html php写了一个脚本 先从数据库中获取pdf文件的路径 然后将这个路径映射成一个html中的超链接 但是我在浏览器中点击这个超链接 死活跳转不了 2 经过多方调查 和搜索 最终找到了问题的原因 chrome中有
  • Java加密算法有几种?

    前言 编程中常见的加密算法有以下几种 你都知道是哪些吗 它们在不同场景中分别有应用 除信息摘要算法外 其它加密方式都会需要密钥 密钥 密钥 key 又常称金钥 是指某个用来完成加密 解密 完整性验证等密码学应用的秘密信息 密钥分类 加解密中
  • centos7更换和升级JDK版本

    卸载 查询是否安装 jdk rpm qa grep jdk rpm qa grep java 卸载安装的 jdk yum y remove java yum 查询支持的版本 可以先更新一下 yum 源 以便支持最新版本 yum y upda
  • 机器学习之线性回归

    什么是线性回归 线性回归利 回归 程 函数 对 个或多个 变量 特征值 和因变量 标值 之间 关系进 建模的 种分析 式 一般只有一个特征值的称之为单变量回归 多个特征值的称之为多变量回归 线性回归 线性回归可以分为两类 线性关系和非线性关
  • Leetcode42.接雨水——双指针法

    文章目录 引入 双指针法 引入 本题是这样的 42 接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图 计算按此排列的柱子 下雨之后能接多少雨水 题目给出的图片一眼就能了然题目要问的是什么 很明显 某一列能装多少水 取决于该列左
  • android ui状态栏高度,移动界面尺寸!安卓720*1280界面尺寸规范参考

    今天25学堂的老谭童鞋跟大家继续分享720 1080的界面设计尺寸规范 主要讲解 屏幕分辨 1280 720像素 720P APP界面设计规范 这样的手机又vivo智能收款机 三星Galaxy A5 华为荣耀等手机 这样的手机屏幕尺寸是 5
  • 4年经验来面试20K的测试岗,连基础都不会,还不如招应届生。。。

    公司前段时间缺人 也面了不少测试 结果竟然没有一个合适的 一开始瞄准的就是中级的水准 也没指望来大牛 提供的薪资在10 20k 面试的人很多 但平均水平很让人失望 看简历很多都是3 4年工作经验 但面试中 不提工具和编程 仅仅基础的技术很多
  • 解决Visual Studio设置C++标准 但是_cplusplus始终为199711

    目录 场景复现 Visual Studio官方说明 C 标准对应表 解决方案 方法一 恢复 cplusplus宏 方法二 使用 MSVC LANG宏 场景复现 我在VS2022偶然的一次测试C 标准开发环境 发现无论我怎么修改C 语言标准
  • List 集合 —— ArrayList

    ArrayList 简介 成员变量 构造方法 成员方法 增 删 其他 总结 参考 简介 ArrayList 是 Java 集合框架中比较常用的类 是用来存储数据的容器 可存储重复的元素 允许存储null值 底层基于数组实现容量大小动态变化
  • windows11安装cp210x驱动

    windows11安装cp210x驱动 1 第一步官网下载驱动 官网地址如下 CP210x USB to UART Bridge VCP Drivers Silicon Labs 第二步 解压文件夹并安装如图所示 3 第三步安装成功后会给你
  • mysql 建表 null,MySQL:唯一,但默认为NULL-创建表允许。允许插入多个NULL。为什么?...

    I ve just checked and it s allowed to create a table with a column that is NULL by default although it s a UNIQUE KEY at
  • Cocos Creator Android 平台接入 Google Firebase (Analytics功能)

    在项目推广运营过程中 经常有分析用户行为的需求 如用户安装 注册 充值等事件 因此需要接入Google Firebase Analytics功能 下面以 Android 平台接入为例 进行详细说明 一 准备工作 1 应用targetSdkV
  • 谐振子方程

    设谐振子质量为 m m m 弹簧弹性系数为 k k k 由胡克定律及牛顿运动定律 有 m x k x m ddot x kx mx kx 其中 x x x为偏离平衡位置的距离 x ddot x x 为 x x x对时间 t t t的二阶导数
  • 解决在Linux系统中创建qt项目时没有生成.cpp和.h的问题

    创建QT项目时没有生成 cpp和 h文件 在linux中的解决方法 1 Qt creater 工具 gt 构建和运行 先查看自己安装版本支持的构建套件 我这里是默认MinGW 32bit 2 新建项目时 选择对应的kits即可 至此 如果还
  • JAVA简单聊天室的实现

    鉴于之前有不少同学在跟我要客户端的代码 我近期整理了一下 把整个工程都传到github上了 地址 https github co Alexlingl Chatroom 里面有比较详细的工程运行教程 这篇博客则主要对工程的代码实现进行介绍 没