java实现建议聊天室
功能 介绍:私聊,群聊
模型介绍:
项目结构
1.服务端
package edu.cc.chat05;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.management.loading.PrivateClassLoader;
import com.sun.org.apache.bcel.internal.generic.AllocationInstruction;
/**
* 在线聊天室:服务端
*目标:私聊和群聊
*封装利于维护
* @author chunchun
*@date 2020年6月15日
* @projectname chatsocket_demo
*/
public class Chat {
//private List<Channel> all = new ArrayList<Channel>();可以使用,但在socket编程使用CopyOnWriteArrayList更好
private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<Channel>();
public static void main(String[] args) throws IOException {
System.out.println("-----server----");
//指定端口 使用ServerSocket 创建服务器
ServerSocket server = new ServerSocket(8888);
//阻塞式等待连接accept
//死循环使其可以连接多个客户端
while(true) {
//与相应客户端连接
Socket client = server.accept();
System.out.println("一个客户端建立连接");
//建立通信管道,用于消息发送与接收。连接一个客户端建立一个管道
Channel c = new Channel(client);
//把所有客户端添加到CopyOnWriteArrayList集合精选管理,为群聊和私聊做准备
all.add(c);///管理所有的成员
//new Thread(new Channel(client)).start();
//连接一个客户端建立一个线程,使每个客户端独立
new Thread(c).start();
}
}
//一个客户一个Channel
static class Channel implements Runnable{
private DataInputStream dis= null;
private DataOutputStream dos= null;
private Socket client;
private boolean isRuning;
private String name;
//构造函数,与客户端连接的同时初始化输入输出流并获取客户端的名字,同时通知已经进入的成员该成员进入
public Channel(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
isRuning =true;
//获取名称
this.name = receive();
//欢迎你的到来
this.send("欢迎你的到来");
//通知已经进入的成员该成员进入聊天室
sendOthers(this.name+"来到了春春聊天室",true);
} catch (IOException e) {
System.out.println("----1-----");
release();
}
}
//接收消息
private String receive(){
String msg ="";
try {
msg = dis.readUTF();
} catch (IOException e) {
System.out.println("----2-----");
release();//出异常就释放资源
}
return msg;
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("----3-----");
release();
}
}
//群聊
/**
* @param msg
* 获取自己的msg利用容器遍历其他人的Channel调用其他人的send方法
* 私聊:约定数据格式: @xxxx:msg
*/
private void sendOthers(String msg,boolean isSys) {
boolean isPrivate = msg.startsWith("@");
if(isPrivate) {//私聊
int idx = msg.indexOf(":");
//获取目标和数据
String targetName = msg.substring(1,idx);
msg = msg.substring(idx+1);
//遍历CopyOnWriteArrayList容器找到要私发的客户端并单独给次客户端发送消息
for(Channel other:all) {
if(other.name.equals(targetName)) {
other.send(this.name+"悄悄的对你说"+msg);//私聊消息内容
}
}
}else {//群聊
for(Channel other:all) {
if(other==this) {
continue;
}
if(!isSys) {
other.send(this.name+"对所有人说:"+msg);//群聊消息
}else {
other.send(msg);//系统消息
}
}
}
}
//释放资源
private void release() {
this.isRuning = false;
Cutils.close(dis,dos,client);
//退出
all.remove(this);
sendOthers(this.name+"离开春春聊天室", true);//退出通知
}
@Override
public void run() {
//死循环保证可以不断精选消息的发送
while(isRuning) {
String msg =receive();
if(!msg.equals("")) {
//send(msg);
sendOthers(msg,false);
}
}
}
}
}
2.客户端
package edu.cc.chat05;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
*
*目标:私聊和群聊
* @author chunchun
*@date 2020年6月15日
* @projectname chatsocket_demo
*/
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("----client----");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("亲输入用户名");
//获取客户端输入的用户名
String name =br.readLine();
//建立连接,使用SOcket创建客户端+服务器的地址和端口
Socket client =new Socket("localhost",8888);
//客户端发送消息
new Thread(new Send(client,name)).start();
//客户端接收消息
new Thread(new Receive(client)).start();
}
}
3.一些封装类
3.1Cutils 用于释放资源
package edu.cc.chat05;
import java.io.Closeable;
/**
* 工具类
* @author chunchun
*@date 2020年6月15日
* @projectname chatsocket_demo
*/
public class Cutils {
//释放资源
//使用Closeable来关闭接口
//参数Closeable ... targets传入可变参数数组
public static void close(Closeable ... targets) {
for(Closeable target:targets) {
try {
if(null!=target) {
target.close();
}
} catch (Exception e) {
}
}
}
}
3.2Receive用于接收服务器发来的消息
package edu.cc.chat05;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**客户端的接收器
* @author chunchun
*@date 2020年6月15日
* @projectname chatsocket_demo
*/
public class Receive implements Runnable{
private DataInputStream dis ;
private Socket client;
private boolean isRuning;
public Receive(Socket client) {
isRuning = true;
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
System.out.println("===2===");
release();
}
}
//接收消息
private String receive(){
String msg ="";
try {
msg = dis.readUTF();
} catch (IOException e) {
System.out.println("====4===");
release();//出异常就释放资源
}
return msg;
}
@Override
//次现场用于循环接收消息
public void run() {
while(isRuning) {
String msg = receive();
if(!msg.equals("")) {
System.out.println(msg);
}
}
}
private void release() {
this.isRuning = false;
Cutils.close(dis,client);
}
}
3.3用于发送消息到服务器
package edu.cc.chat05;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* 客户端的发送器
* @author chunchun
*@date 2020年6月15日
* @projectname chatsocket_demo
*/
public class Send implements Runnable{
private BufferedReader console;
private DataOutputStream dos;
private Socket client;
private boolean isRuning;
private String name;
public Send(Socket client,String name) {
this.client = client;
isRuning =true;
this.name=name;
console = new BufferedReader(new InputStreamReader(System.in));
try {
dos = new DataOutputStream(client.getOutputStream());
//发送名称
send(name);
} catch (IOException e) {
System.out.println("===1==");
this.release();
}
}
@Override
//此线程用于循环发送消息
public void run() {
System.out.println(isRuning);
while(isRuning) {
String msg = getStrFromConsole();
if(!msg.equals("")) {
send(msg);
}
}
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("===3==");
release();
}
}
//从控制台获取消息
private String getStrFromConsole() {
try {
return console.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private void release() {
this.isRuning = false;
Cutils.close(dos,client);
}
}
运行效果图
群聊效果图
私聊效果图
退出提示
代码可以直接运行,需要者可以前往我的GitHub下载代码,地址如下
https://github.com/zzc-2180886679/chat_socket.git