上班聊天,摸鱼神器,手写一款即时通讯工具(附源码!!!)

文章目录

  • 即时通讯工具
  • 客户端
  • 服务端
    • 1、链接
    • 2、登录
    • 3、其他方法
      • 3.1、读取客户端的消息
      • 3.2、给客户端发送消息
      • 3.3、日志记录
      • 3.4、工具集合
      • 3.5、ChatSocket
  • 服务端部署
  • 优点与缺点
  • 最后

认真工作不叫做赚钱,那叫做用劳动换取报酬,上班摸鱼才是真的赚钱。

在这里插入图片描述

即时通讯工具

如果上班有空闲时间,最喜欢做的事情自然是和熟悉的朋友一起聊聊天,互相吐槽工作中遇到的人和事,缓解工作的压力。

如果直接在桌面上打开 QQ 或者微信,那目标无疑是巨大的,QQ 和微信的桌面客户端明晃晃地占据整个电脑桌面,只要有同事或者领导从你身边经过,或是在你后面看一眼,就立刻能够知道你在上班摸鱼,那场面不亚于公开处刑…… (@_@)

领导:看来工作还是不饱和啊 ┑( ̄Д  ̄)┍

针对这种情况,技术人自然不甘落后,总是可以想出各种方法躲避同事和领导发现你在上班摸鱼 ≡ω≡

思量再三,最终还是放弃了 IDEA 的各种插件,转而决定还是自己手写一款简易的即时通信工具。

既然要自己动手,那自然也要先对这款即时通讯工具做个简单的规划。

  • 这款即时通讯工具分为客户端和服务端的,每个用户可以使用客户端进行即时通讯。
  • 通讯工具尽可能简单,只依赖于 JDK,即完全使用 Java 网络编程功能实现,不依赖其他的第三方库。
  • 通讯工具不需要桌面,使用 Java 自带的 Scanner 控制台输入即可。

这样一款基于 Java 网络编程的即时通讯工具,只要在 IDEA 运行客户端代码,即可在控制台与其他朋友快乐地聊天。只要不是同事或者领导贴着你的电脑屏幕观看,他绝对想不到你是在使用 IDEA 上班摸鱼聊天。

在这里插入图片描述

客户端

客户端是给使用这款即时通讯工具的用户使用的,从安全和用户体验的角度上来说,客户端应该尽可能精简,只负责发送和接受数据即可。

因为这是一款即时通讯工具,客户端需要做的有两件事:

  • 监听客户端的输入和发送。
  • 监听服务端发送过来的消息。

因为我们使用 JDK 自带的 Scanner 类来进行客户端的输入,而这个输入是一个阻塞的操作,所以我们需要创建一条额外的线程来进行服务端的监听工作。

客户端需要两条用户线程:

  • main 线程用来监听客户端的输入和发送。
  • 另外创建一条线程用来监听服务端的消息发送。

思路已经设计好了,可以使用代码来实现了:

/*** 聊天室客户端** @author herenpeng* @since 2021-07-09 12:00:00*/
public class ChatClient {public static void main(String[] args) {try (Socket socket = new Socket("127.0.0.1", 12345)) {// 读取服务端发的消息new Thread(() -> readMsg(socket)).start();OutputStream os = socket.getOutputStream();Scanner scanner = new Scanner(System.in);System.out.println("请输入您的聊天室昵称:");while (true) {String chat = scanner.next();System.out.println("---------------------------");os.write(chat.getBytes());}} catch (Exception e) {System.out.println("【系统消息】聊天室炸了,BUG之神降临了");e.printStackTrace();System.exit(0);}}private static void readMsg(Socket socket) {try {while (true) {InputStream is = socket.getInputStream();byte[] bytes = new byte[1024];int len = is.read(bytes);System.out.println(new String(bytes, 0, len));}} catch (Exception e) {System.out.println("【系统消息】你已退出聊天室,开始认真工作吧");System.exit(0);}}}

服务端

服务端的设计比客户端要困难很多,为了便于开发和理解,我直接使用了 Java 阻塞式的网络 IO 来进行实现,即每一个客户端连接都创建一个线程来进行处理。

这种阻塞式的网络 IO 的好处在于便于理解和开发,而缺点也非常明显,因为这是一个通讯工具,即每一个链接都是长链接。

即每个客户端用户链接服务端后,都会在服务端专门有一个线程处理这个客户端相关的网络 IO 操作。如果用户量少的情况下还比较好,但是用户一旦多了起来,服务端将会创建 N 多个线程,而且在客户端不主动断开的情况下,服务器这些线程会一直占用服务器资源,服务器将会消费非常大的资源,而且很容易崩溃。

基于这种情况,我后面也实现了一个 Java NIO 版本的客户端和服务端,在文章末尾也会一起附上源码。

在这里插入图片描述

我将服务端的操作分为两个步骤:

1、链接

服务端阻塞地等待客户端的链接请求。

/*** 聊天室服务端** @author herenpeng* @since 2021-07-09 12:00:00*/
public class ChatServer {/*** 启动类** @param args 启动参数* @throws IOException 抛出IO异常*/public static void main(String[] args) throws IOException {ServerSocket server = new ServerSocket(12345);new Thread(() -> start(server)).start();// 加载配置CHAT_CFG_RELOAD_PASSWORD = UUID.randomUUID().toString();logInfo("【系统消息】聊天室配置加载密钥:" + CHAT_CFG_RELOAD_PASSWORD);reloadChatCfg(args.length == 1 ? args[0] : null, null);logInfo("【系统消息】聊天室启动成功了!");}/*** 服务开始方法** @param server 服务对象*/private static void start(ServerSocket server) {try {while (true) {// 链接操作ChatSocket chatSocket = connection(server);// 登录操作login(chatSocket);}} catch (Exception e) {logInfo("【系统消息】聊天室发生了异常……");e.printStackTrace();} finally {logInfo("【系统消息】正在关闭聊天室资源……");close(server);}}/*** 链接客户端** @param server 服务对象* @return ChatSocket 对象* @throws IOException 抛出异常*/private static ChatSocket connection(ServerSocket server) throws IOException {Socket socket = server.accept();ChatSocket chatSocket = new ChatSocket(socket);userDB.add(chatSocket);sendMsgToUser(socket, "============================\n" +"1、本聊天室仅为娱乐,请勿在该聊天室内谈论敏感内容,比如涉政,涉黄,账号密码等等!\n" +"2、聊天室内容明文传输,聊天信息泄露本聊天室概不负责!\n" +"3、本聊天室内容后台不做任何存储,聊天信息如果需要请自行保留!\n" +"4、最终解释权归本聊天室所有!\n" +"============================");return chatSocket;}/*** 保存所有用户socket的集合*/private static final List<ChatSocket> userDB = new LinkedList<>();}

2、登录

服务端获取到客户端请求后,将 Socket 包装为我们自定义的 ChatSocket,便于我们进行登录操作。

/*** 用户登录方法** @param chatSocket ChatSocket 对象*/
private static void login(ChatSocket chatSocket) {// 给每个用户一个线程处理new Thread(() -> {Socket socket = chatSocket.getSocket();String username = null;try {InputStream is = socket.getInputStream();byte[] bytes = new byte[1024];int len = readMsg(is, bytes);if (len == -1) {logout(chatSocket);return;}username = new String(bytes, 0, len);chatSocket.setUsername(username);// 刷新配置if (CHAT_CFG_RELOAD_PASSWORD.equals(username)) {reloadChatCfg(is, bytes, socket);return;}loginTip(username, socket);// 机器人欢迎robotWelcome(username);while (true) {len = readMsg(is, bytes);if (len == -1) {logout(chatSocket);return;}String msg = new String(bytes, 0, len);sendMsgToOtherUser(username, socket, msg);// 机器人回复消息randomRobotReply(msg);}} catch (IOException e) {try {logout(chatSocket);} catch (Exception ex) {remove(socket);ex.printStackTrace();}e.printStackTrace();}}).start();
}

3、其他方法

3.1、读取客户端的消息


/*** 读取消息的方法** @param is    输入流* @param bytes 字节数组* @return 读取的长度*/
private static int readMsg(InputStream is, byte[] bytes) {int len;try {len = is.read(bytes);} catch (Exception e) {return -1;}return len;
}

3.2、给客户端发送消息

/*** 给所有的用户发送系统消息** @param msg 系统消息* @throws IOException 抛出异常*/
private static void sendSysMsg(String msg) throws IOException {for (ChatSocket chatSocket : userDB) {String sysMsg = getCurrentTime() + "\n" + msg + "\n" + chatSeparate;Socket socket = chatSocket.getSocket();sendMsgToUser(socket, sysMsg);}
}/*** 发送消息给其他用户** @param username 消息发送用户名称* @param self     消息发送的用户socket* @param msg      消息* @throws IOException 抛出异常*/
private static void sendMsgToOtherUser(String username, Socket self, String msg) throws IOException {for (ChatSocket chatSocket : userDB) {Socket socket = chatSocket.getSocket();if (socket.equals(self)) {continue;}String sendMsg = "(" + username + ") " + getCurrentTime() + "\n" + msg + "\n" + chatSeparate;sendMsgToUser(socket, sendMsg);}
}/*** 给指定的用户发送消息,文本消息** @param socket  消息发送的用户socket* @param sendMsg 消息* @throws IOException 抛出异常*/
private static void sendMsgToUser(Socket socket, String sendMsg) throws IOException {OutputStream os = socket.getOutputStream();os.write(sendMsg.getBytes());
}

3.3、日志记录

虽然服务端不需要记录用户的聊天信息,但是还是需要记录一些服务器的日志信息。

/*** 打印日志** @param message 日志信息*/
private static void logInfo(String message) {System.out.println(getCurrentDateTime() + " " + message);
}

3.4、工具集合

/*** 获取当前在线的所有玩家名称** @return 当前在线的所有玩家名称*/
private static List<String> getLoginUsernames() {return userDB.stream().map(ChatSocket::getUsername).filter(Objects::nonNull).collect(Collectors.toList());
}/*** 判断时间是否是 11:00 - 04:59 晚上** @return 是返回true,否则返回false*/
private static boolean isNight() {Calendar calendar = Calendar.getInstance();int hour = calendar.get(Calendar.HOUR_OF_DAY);return hour >= 23 || hour <= 4;
}/*** 时间格式化对象*/
private static final SimpleDateFormat timeSdf = new SimpleDateFormat("HH:mm:ss");
private static final SimpleDateFormat DateTimeSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");/*** 获取当前的时间的格式化字符串** @return 当前的时间的格式化字符串*/
private static synchronized String getCurrentTime() {return timeSdf.format(new Date());
}/*** 获取当前的日期时间的格式化字符串** @return 当前的日期时间的格式化字符串*/
private static synchronized String getCurrentDateTime() {return DateTimeSdf.format(new Date());
}/*** 判断一个字符串是否为空** @param string 字符串* @return 为空返回true,否则返回false*/
private static boolean isEmpty(String string) {return string == null || string.length() == 0;
}/*** 判断一个字符串是否不为空** @param string 字符串* @return 不为空返回true,否则返回false*/
private static boolean isNotEmpty(String string) {return !isEmpty(string);
}

3.5、ChatSocket

/*** 封装的 ChatSocket*/
private static class ChatSocket {private final Socket socket;private String username;public ChatSocket(Socket socket) {this.socket = socket;}public Socket getSocket() {return socket;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}
}

服务端部署

为了方便在服务器上运行服务端代码,我还特意写了一个 Shell 脚本用来处理服务端代码的运行、停止、重启、查找等操作。

在这里插入图片描述

CHAT_SERVER_DIR=/usr/app/chat
CHAT_SERVER=ChatServer
CHAT_LOG_FILE=${CHAT_SERVER_DIR}/chat.log
# 聊天室启动参数
CHAT_CFG=${2}help() {echo "=================="echo "start 启动服务"echo "stop 停止服务"echo "restart 重启服务"echo "find 查找服务"echo "help 帮助"echo "=================="
}start() {javac -encoding UTF-8 ${CHAT_SERVER}\.javanohup java -Dfile.encoding=UTF-8 ${CHAT_SERVER} ${CHAT_CFG} >>${CHAT_LOG_FILE} 2>&1 &echo "服务${CHAT_SERVER}已启动"
}stop() {PID=$(ps -ef | grep java | grep ChatServer | awk '{print $2}')if [ "${PID}" == "" ]thenecho "服务${CHAT_SERVER}已停止"elsekill ${PID}echo "服务${CHAT_SERVER}已停止"fi
}restart() {stopsleep 3startecho "服务${CHAT_SERVER}已重启"
}find() {PID=$(ps -ef | grep java | grep ChatServer | awk '{print $2}')if [ "${PID}" == "" ]thenecho "服务${CHAT_SERVER}已停止"elseecho "服务${CHAT_SERVER}正在运行:PID=${PID}"fi
}case ${1} in"")echo "=== 参数错误 ===";;start)start;;stop)stop;;restart)restart;;find)find;;*)help;;
esacexit 0

优点与缺点

优点

  • 简单便捷,无论是客户端还是服务端,都只依赖了 JDK 的环境,没有任何第三方依赖,客户端的代码只在复制到有 JDK 环境的电脑上即可运行,方便快捷。
  • 足够隐蔽,客户端在 CMD 或者 IDEA 环境下都可以运行,这样你身边的同事只要不仔细观察你的电脑屏幕,绝对想不到你是在和朋友聊天,只以为你是在认真工作。

缺点

  • 服务端基于阻塞式网络 IO 开发,服务端只能够承受有限个的客户端链接。(Java NIO 版本可以解决这个缺点)
  • 太过简陋,因为只是单纯地进行网络 IO 的写入和读取,所以对于一些复杂的网络环境问题都没有进行处理,比如网络黏包的问题,在客户端连接较多的情况下,可能会发生网络黏包的问题,导致一些消息粘黏在一起,发送给客户端。
  • 需要一个服务器,因为这款即使通讯工具是 CS 模式,需要一个服务器运行服务端代码。

最后

虽然这款即时通讯工具确实还不够完善,但是如果只是用于几个朋友之间简单地进行聊天,这款即时通讯工具还是非常给力的。

这款即时通讯工具的源代码已经被托管到了 GitHub 上,同时还附带了这款即时通讯工具的 Java NIO 版本,有兴趣的同学的可以访问我的 GitHub 下载源码。

GitHub:https://github.com/herenpeng/chat.git

同时,我还在 Gitee 上提供了仓库镜像。

Gitee:https://gitee.com/herenpeng/chat.git

如果你喜欢这款即时通讯工具,希望各位同学可以给我的 GitHub 或者 Gitee 仓库点一个 Star,非常感谢!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/23499.html

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

相关文章

闲鱼软件监控搜索采集hook请求签名算法-X-Sign和强制聊天

抓包方法和X-Sign算法 详情页所有数据都有 这些内容是的整个详情页的字段 以及聊天抓包分析 直接用易语言写成了 成品 可以直接读取数据

我其实一直都懂你|闲鱼聊天机器人

项目背景 闲鱼是一个闲置共享交易社区&#xff0c;为了增加交易乐趣&#xff0c;闲鱼在产品设计中有意识的通过买卖双方的互动交流推动商品的成交。无论是商品下单需要先聊一聊&#xff0c;还是鱼塘话题等通过群组互动分享交流商品&#xff0c;都是这一设计思路的体现。 但是&a…

一键导出微信所有聊天记录的小工具

本次分享一个小工具&#xff0c;该工具惟一的作用是把微信电脑版的聊天记录一键导出到表格上&#xff0c;每个人或者每个群的记录保存到一个表格中&#xff0c;方便备份和管理 特别说明&#xff1a; 1&#xff0c;该软件只能导出自己的微信聊天记录&#xff0c;自己登录不了的…

闲鱼自动化软件——筛选/发送系统 V20已经测试完毕

做程序&#xff0c;就是不断地改&#xff0c;不断地优化。 当改动达到一定程序&#xff0c;已经和前面形成断代&#xff0c;程序的升级时刻便到了。 V20做了哪些更改或优化。 1。优化抓取&#xff1a; 在抓取环境优化参数&#xff0c;使抓取更顺滑&#xff0c;抓取数据效果上更…

写了一个闲鱼助手app,可以在手机端查看闲鱼最新发布

因为闲鱼的程序算是比较熟了&#xff0c;最近又在做其他安卓APP开发&#xff0c;顺便就写了一个闲鱼助手APP。 不同于官方的闲鱼APP&#xff0c;只可以设置手机品类&#xff0c;此款APP可以设置任何精确关键词&#xff0c;价格&#xff0c;返回最新发布数据&#xff0c;可以在手…

写一个闲鱼助手的助手工具

较新版本的闲鱼推出了找货助手功能。 虽然目前该功能只对手机大类开放&#xff0c;但也算是官方推出了闲鱼助手工具。 比较遗憾的是&#xff0c;除了目前不能添加其他类&#xff0c;还有该功能不能自动刷新 所以打算开发一款闲鱼助手的自动化工具。 所以&#xff0c;或许可以…

搜索引擎变天了!谷歌宣布开放「生成式搜索平台」!AI 大模型颠覆搜索体验

作者 | 小戏、兔子酱 搜索引擎&#xff0c;可能真的要变天了&#xff01; Google 终于要迎来它 25 年来最大的改变&#xff0c;谷歌宣布了开始内测开放【生成式搜索平台&#xff08;Search Generative Experience&#xff0c;SGE&#xff09;】&#xff0c;并逐步舍弃那些甚至是…

算法chatgpt回答

算法 红黑树和AVL树区别 红黑树和AVL树区别

5分钟利用ChatGPT4+MindShow制作一个演讲稿PPT

今天有个朋友的小孩对chatGPT非常感兴趣&#xff0c;准备写一个关于ChatGPT4的演讲稿&#xff0c;到学校里面演讲&#xff0c;朋友委托我做一个PPT&#xff0c;我今天就给大家介绍如何利用ChatGPT4和Mindshow在5分钟内完成一个完整的演讲PPT&#xff1b; 1、制作的步骤 一、…

前端性能优化之缓存利用

前言 越来越多的公司开始做PWA&#xff0c;渐进式web应用了。我的博客源码是2014年年初写的&#xff0c;近期有时间正准备优化一下&#xff0c;也改成PWA的web应用。关于PWA渐进式web应用&#xff0c;我在博客改版之后再来详细介绍&#xff01;今天主要详细介绍一下性能优化之…

ChatGPT教你如何解决复杂高并发系统缓存设计(上)

ChatGPT教你如何解决复杂高并发系统缓存设计(上) ❗缓存&#xff0c;消息队列&#xff0c;分库分表是高并发解决方案三剑客。 为什么需要设计系统缓存 设计系统缓存的主要目的是提高系统的性能和可伸缩性&#xff0c;同时减轻底层资源&#xff08;如数据库、网络&#xff09;的…

NPM报错 Error: EPERM: operation not permitted, unlink......解决办法和清除缓存。

由于国内外环境因素&#xff0c;npm install安装依赖的时候经常会出现各种问题&#xff0c;特别是“Error: EPERM: operation not permitted, unlink…”这个错误。 这个错误因为报错信息的误导性&#xff0c;导致很多网上提出的解决办法都是什么设置权限&#xff0c;以管理员…

人工智能趋势——2023 年综述

随着DALLE 2 于 2022 年 4 月的宣布&#xff0c;关于2022 年初第三个 AI 冬天——或 AI 撞墙——的预言过时得很快而且效果不佳&#xff0c;随后出现了更多主要由扩散模型驱动的文本到图像应用程序&#xff0c;这是一个非常多产的领域用于计算机视觉研究及其他领域。AI 的 2022…

王炸 ChatGPT又更新,能联网同5000+应用交互

都说ChatGPT是AI的“iPhone时刻”&#xff0c;现在属于它的“应用商店”来了。 OpenAI刚刚又甩出一个王炸—— 宣布推出插件功能&#xff0c;赋予ChatGPT使用工具、联网、运行计算的能力。 例如在官方演示中&#xff0c;ChatGPT一旦接入数学知识引擎Wolfram Alpha&#xff0…

tab栏切换的实现

Tab导航栏切换在网页场景中十分常见&#xff0c;本文将介绍如何用js来实现交互的导航栏。 分析&#xff1a; 1.当鼠标点击上面相应的选项卡&#xff08;tab&#xff09;&#xff0c;下面盒子的内容跟随变化 2.点击某一个选项&#xff0c;当前这一个底色会变成红色&#xff0c;字…

Ubuntu16.04如何将桌面上左边任务栏移到屏幕下方

操作步骤 1&#xff09;移动到桌面的下方: 打开终端&#xff0c;&#xff08;快捷键:CtrlAltT&#xff09;然后输入命令:gsettings set com.canonical.Unity.Launcher launcher-position Bottom效果图如下所示: 2&#xff09;移动到桌面的左方: 打开终端&#xff0c;输入命令:g…

简单的tap栏切换

效果图&#xff1a; HTML部分 <div class"tab"><div class"tab_top"><ul><li class"current">水果</li><li>家电</li><li>书籍</li><li>服装</li></ul></div>&l…

Ubuntu图形桌面切换到命令行界面

Ubuntu提供两种进入方式&#xff0c;一个是我们平常最熟悉的图形界面形式&#xff0c;还有一种是纯命令行方式。 1、按 Ctrl Alt (F1~F6中的任意一个)即可进入纯命令行模式。 进入后&#xff0c;需要输入用户名&#xff0c;密码(此处包括root用户与非root用户)。 注意&#x…

如何利用chatgpt做到大型桌面应用无痛换肤

有一个换肤的需求&#xff0c;但是颜色实在太多了&#xff0c;虽然有主题色&#xff0c;但是除了主题色的颜色还是好多好多。利用chatgtp做到尽快&#xff0c;快速换肤。 1.先要求chatgpt帮你写一个脚本。 文件夹下的的所有文件包括文件夹下的文件夹内的所有文件&#xff0c;…

firefox网页自动翻译

firefox自带翻译组件 1. 在应用程序菜单中选择扩展和主题 2. 附加组件中直接搜素相应的翻译组件 推荐第二个 我使用的很顺手 !! 有网页翻译和划词翻译 3. 基础设置 设置方便 但是有一部分翻译源是需要向源申请的,需要一部分自己的信息,不过操作简单,按照流程就可以 4. 其他…