分析:
聊天室需要多个客户端和一个服务端。
服务端负责转发消息。
客户端可以发送消息、接收消息。

消息分类:群聊消息:发送除自己外所有人私聊消息:只发送@的人系统消息:根据情况分只发送个人和其他人
技术方面:客户端和服务端收发消息,需要使用IO流,封装一个IOUtils工具类用来释放资源。客户端需要同时收发消息,需要启动发送和接收两个消息,互不干扰服务端需要接收每个客户端消息和对多个客户端发送消息,每连接上一个客户端需要启动一个线程,让后面进来的客户端不需要等待前面的客户端退出后才能建立连接。

……

还是上代码吧。

基础版:

搭建结构,实现多个客户端和服务端连接,保证服务端能正常转发消息。
我们约定:
当服务端在初始化、发送、接收时出现异常时分别输出:
------1------
------2------
------3------

当客户端,初始化发送线程、初始化接收线程、发送、接收异常时分别输出:
======1=====
======2=====
======3=====
======4=====

1、IO工具类

package com.xzlf.chat;import java.io.Closeable;
import java.io.IOException;/*** 工具类* @author xzlf**/
public class IOUtils {/*** 释放资源* @param closeables*/public static void close(Closeable...closeables) {for (Closeable closeable : closeables) {if(null != closeable) {try {closeable.close();} catch (IOException e) {e.printStackTrace();}}}}
}

2、服务端

package com.xzlf.chat;import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;/*** 聊天室:服务器* @author xzlf**/
public class TMultiChat {public static void main(String[] args) throws IOException {System.out.println("======server======");// 1、指定端口创建服务端ServerSocket server = new ServerSocket(8888);while(true) {// 2、每进来一个客户端启动一个线程Socket socket = server.accept();System.out.println("一个客户端建立了连接");new Thread(new Channel(socket)).start();}}// 一个Channel 代表一个客户端static class Channel implements Runnable{private Socket socket;private DataInputStream dis;private DataOutputStream dos;private boolean isRuning;public Channel(Socket socket) {this.socket = socket;this.isRuning = true;try {dis = new DataInputStream(socket.getInputStream());dos = new DataOutputStream(socket.getOutputStream());} catch (IOException e) {System.out.println("------1------");this.release();}}// 接收消息private String receive() {String msg = "";try {msg = dis.readUTF();} catch (IOException e) {System.out.println("------3------");this.release();}return msg;}// 发送消息private void send(String msg) {try {dos.writeUTF(msg);} catch (IOException e) {System.out.println("------2------");this.release();}}// 释放资源private void release() {this.isRuning = false;IOUtils.close(dis, dos, socket);}@Overridepublic void run() {while(isRuning) {String msg = this.receive();if (!msg.equals("")) {this.send(msg);}}}}
}

3、多线程封装发送端

package com.xzlf.chat;import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream.GetField;
import java.net.Socket;/*** 利用多线程封装发送端* * @author xzlf**/
public class Send implements Runnable{private Socket socket;private DataOutputStream dos;private BufferedReader console;private boolean isRuning;public Send(Socket socket) {this.socket = socket;this.isRuning = true;try {console = new BufferedReader(new InputStreamReader(System.in));dos = new DataOutputStream(socket.getOutputStream());} catch (IOException e) {System.out.println("======1=====");this.release();}}// 从控制台获取消息private String getStrFromConsole() {try {return console.readLine();} catch (IOException e) {e.printStackTrace();}return "";}// 发送消息public void send(String msg) {try {dos.writeUTF(msg);dos.flush();} catch (IOException e) {e.printStackTrace();System.out.println("======3=====");this.release();}}// 释放资源private void release() {this.isRuning = false;IOUtils.close(dos, console, socket);}@Overridepublic void run() {while(isRuning) {String msg = getStrFromConsole();if (!msg.equals("")) {this.send(msg);}}}}

4、多线程封装接收端

package com.xzlf.chat;import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;/*** 使用多线程封装接收端* @author xzlf**/
public class Receive implements Runnable {private Socket socket;private DataInputStream dis;private boolean isRuning;public Receive(Socket socket) {this.socket = socket;this.isRuning = true;try {dis = new DataInputStream(socket.getInputStream());} catch (IOException e) {System.out.println("======2=====");this.release();}}// 接收消息public String receive() {String msg = "";try {msg = dis.readUTF();} catch (IOException e) {System.out.println(e);System.out.println("======4=====");release();}return msg;}// 释放资源private void release() {this.isRuning = false;IOUtils.close(dis, socket);}@Overridepublic void run() {while(isRuning) {String msg = receive();if(!msg.equals("")) {System.out.println(msg);}}}
}

5、客户端

package com.xzlf.chat;import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;/*** 聊天室:客户端* @author xzlf**/
public class TMultiClient {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("======client======");// 1、指定ip + 端口 建立连接Socket socket = new Socket("localhost", 8888);// 2、客户端收发消息new Thread(new Send(socket)).start();new Thread(new Receive(socket)).start();}
}

运行服务端和客户端:

先每个客户端只能自己跟自己聊。

实现群聊:

1、加入容器(使用JUC包下的并发容器CopyOnWriteArrayList),并添加给其他用户发送消息方法
添加容器:

public class Chat {private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<Channel>();public static void main(String[] args) throws IOException {System.out.println("======server======");// 1、指定端口创建服务端ServerSocket server = new ServerSocket(8888);while(true) {// 2、每进来一个客户端启动一个线程Socket socket = server.accept();Channel c = new Channel(socket);all.add(c);System.out.println("一个客户端建立了连接");new Thread(c).start();}}
添加群发方法
// 群聊:发送消息给其他人private void sendOthers(String msg, boolean isSys) {for(Channel other : all) {if(other == this) {continue;}if (!isSys) {// 群聊消息other.send(this.name + "说:" + msg);}else {// 系统消息other.send(msg);}}}

2、在初始化发送端,写入自己用户名并在初始化就发送
客户端启动时,输入用户名

public class Client {public static void main(String[] args) throws UnknownHostException, IOException {System.out.println("======client======");BufferedReader br = new BufferedReader(new InputStreamReader(System.in));System.out.print("请输入用户名:");String name = br.readLine();// 1、指定ip + 端口 建立连接Socket socket = new Socket("localhost", 8888);// 2、客户端收发消息new Thread(new Send(socket, name)).start();new Thread(new Receive(socket)).start();}
}
发送线程初始化时立马发送用户名:
public class Send implements Runnable{private Socket socket;private DataOutputStream dos;private BufferedReader console;private boolean isRuning;private String name;public Send(Socket socket, String name) {this.socket = socket;this.name = name;try {this.isRuning = true;console = new BufferedReader(new InputStreamReader(System.in));dos = new DataOutputStream(socket.getOutputStream());// 发送用户名this.send(name);} catch (IOException e) {System.out.println("======1=====");this.release();}}
服务端的channel类中初始化时立马接收用户名并保存

3、服务端(静态内部类Channel类中)在初始化时立即获取用户名并给用户发送欢迎信息同时给其他用户发提示信息(系统消息)

static class Channel implements Runnable{private Socket socket;private DataInputStream dis;private DataOutputStream dos;private boolean isRuning;private String name;public Channel(Socket socket) {this.socket = socket;this.isRuning = true;try {dis = new DataInputStream(socket.getInputStream());dos = new DataOutputStream(socket.getOutputStream());// 获取用户名this.name = receive();this.send("欢迎你的到来");this.sendOthers(this.name + "来了xxx聊天室", true);} catch (IOException e) {System.out.println("------1------");this.release();}}

4、用户关闭线程,给其他用户发送提示信息,提示用户已离开

// 释放资源private void release() {this.isRuning = false;IOUtils.close(dis, dos, socket);// 退出all.remove(this);sendOthers(this.name + "离开了聊天室。。。", true);}

运行测试:

实现私聊:

通过判断用户输入信息是否包含“@xxx:”确定是否为私聊,修改群发方法:

/*** 群聊:获取自己的信息,发送消息给其他人* 私聊:约定数据格式: @xxx:msg* @param msg* @param isSys*/private void sendOthers(String msg, boolean isSys) {if(msg.startsWith("@")) {// 私聊int endIndex = msg.indexOf(":");String targetName = msg.substring(1, endIndex);String info = msg.substring(endIndex + 1);for(Channel other : all) {if(other.name.equals(targetName)) {other.send(this.name + "悄悄对你说:" + info);}}}else {// 群聊for(Channel other : all) {if(other == this) {continue;}if (!isSys) {// 群聊消息other.send(this.name + "说:" + msg);}else {// 系统消息other.send(msg);}}}}

好了,现在已经实现了私聊。
运行测试一下:

Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊相关推荐

  1. Java WebSocket实现网络聊天室(群聊+私聊)

    WebChat聊天室 2018.02.26 源码地址早就贴了呀, 留邮箱不如自己下载 项目地址: https://github.com/Amayadream/WebChat 2017.01.11更新 ...

  2. Java网络编程:TCP实现聊天

    客户端 package com.zhl.nett;import java.io.IOException; import java.io.OutputStream; import java.net.In ...

  3. PHP+AJAX高性能聊天室(群聊+私聊)

    无需服务端 Anlin_chat 一个多功能免费开源的网页聊天室,基于php+mysql+js运行 无需服务端,高效+极快的运行速度 (超高安全性)支持分类帖子推送兼容QQ内置,微信内置,Firefo ...

  4. BIO聊天室(群聊+私聊)

    功能:群聊+私发+上线提醒+下线提醒+查询在线用户 文件 Utils FinalValue Message Server Client Receive Send Utils package morem ...

  5. Java网络编程:TCP实现群聊私聊代码

    Java网络编程:TCP实现群聊&私聊代码 和上一篇博客差不多,只不过是在群里的基础之上增加了私聊的功能,我们约定,私聊格式为:@xxx:msg 如何实现私聊呢,加入客户端c给服务器发送消息, ...

  6. 网络编程——基于TCP协议的通讯录【课程设计】

    网络编程--基于TCP协议的通讯录[课程设计] 本文目录 网络编程--基于TCP协议的通讯录[课程设计] 一.设计题目和要求 设计目标: 课程设计系统组成及模块功能: 二.设计内容 服务端 客户端 S ...

  7. SpringBoot + Vue 实现基于 WebSocket 的聊天室(单聊)

    前言 在前一篇文章SpringBoot 集成 STOMP 实现一对一聊天的两种方法中简单介绍了如何利用 STOMP 实现单聊,本文则将以一个比较完整的示例展示实际应用,不过本文并未使用 STOMP,而 ...

  8. Java网络编程——基于UDP协议的聊天室

    UDP简述    UDP(User Datagram Protocol)协议是Internet 协议集支持的一个无连接的传输协议,中文名为用户数据报协议.它为应用程序提供了一种无需建立连接就可以发送封 ...

  9. 06 java GUI 网络编程:图形界面聊天室

    仿照第4篇笔记的形式,笔者决定将GUI和网络编程部分用综合练习的方式来总结.练习项目是有图形界面的聊天室,用到了GUI中的javax.swing包和网络编程中的TCP/socket编程.GUI部分的难 ...

  10. Linux网络编程——基于tcp/ip的模拟聊天(文件传输)工具

    开发平台:Linux 开发工具:Ubuntu, sourceInsight4.0 项目介绍: 本项目基于TCP/IP协议创建一个网络通信系统,可以实现客户之间的聊天通信以及文件传输,同时利用进程实现多 ...

最新文章

  1. 二、创作网站 (三) Creating custom content types
  2. python逐行读取json_如何用python读取json文件里指定的数据
  3. 【Rsyslog】 从json 中通过正则 key 获取 value值,rsyslog正则匹配获取key 的 value值
  4. 解决神州数码校园上网客服端软件不能在Windows 7 (5956版)安装的问题
  5. No resource found that matches the given name 'android:Widget.Material.ActionButton'.
  6. 20161212xlVBA文本文件多列合并
  7. IJCAI 2022 | 量化交易相关论文(附论文链接)
  8. 综合交易平台API技术开发指南
  9. 开发快手爬票项目(中)
  10. 业务流程管理(BPM)系统的九大必备特点
  11. 陌生人邀请我加入CS:GO游戏,我一接受就被盗号了
  12. Android快速SDK(24)第三方分享UmengShare【肌肉记忆,分钟接入】
  13. against fate
  14. matlab lpc求共振峰频率,用Python中的LPC估计共振峰
  15. Elasticsearch: date时间数据格式踩过的坑
  16. 中国软件与IT外包产业以印度为镜
  17. NetSuite SuiteAnalytics 高级搜索 全课程中文学习视频
  18. 【同济大学主办,中南大学与上海大学联合协办,JPCS出版】第六届先进算法与控制工程国际学术会议(ICAACE 2023)
  19. 浏览器隐藏滚动条(不影响内容滚动)
  20. net-java-php-python-社区书店管理信息计算机毕业设计程序

热门文章

  1. iOS Twitter登录无法收到回调数据
  2. 云计算与虚拟化技术课堂零碎(一)
  3. Windows XP怎样实现自动登录而无需输入密码
  4. el表达式设置option标签selected
  5. 看看最新BTA大厂的Java程序员的招聘技术标准,聪明人已经收藏了!
  6. 统计学基础——方差、协方差、标准差(标准偏差/均方差)、标准误、均方误差、均方根误差(标准误差)的区别
  7. java rrd 读取_rrd4j的使用详解1–数据保存入rrd文件 | 学步园
  8. php rrd 做统计数据,Ganlia采样、统计及RRD记录周期(频次、间隔)的配置和更改...
  9. 19年6月六级翻译词汇
  10. Java多线程并发笔记01 对象锁 类锁 对象锁的同步和异步 脏读