java结合GUI多线程实现TCP的 Socket聊天室

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
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个Java考试试题,主要考察多线程TCP网络编程相结合的知识: 题目描述: 请编写一个Java程序,实现一个基于TCP协议的简单聊天室,该聊天室可以同时支持多个客户端连接。要求使用多线程和同步机制实现客户端和服务器之间的数据传输和处理。 具体要求: 1. 服务器端应该创建一个ServerSocket对象并监听指定的端口,等待客户端的连接请求。 2. 当一个客户端连接到服务器时,服务器应该创建一个新的线程来处理该客户端的请求,线程负责处理该客户端的输入和输出,并将消息广播给其他客户端。 3. 客户端应该创建一个Socket对象,并连接到服务器指定的IP地址和端口。 4. 客户端可以通过Socket对象的输入流和输出流与服务器进行通信,客户端发送消息后,服务器应该将消息广播给其他客户端。 5. 聊天室应该具有基本的命令功能,例如:发送消息、退出聊天室等。 6. 服务器应该能够处理多个客户端的连接请求,可以使用线程池来管理多个线程。 7. 要求程序具有良好的可读性和可维护性,代码注释清晰,命名规范。 提示: 1. 可以使用JavaSocket、ServerSocket和ThreadPoolExecutor等类库实现TCP网络编程和多线程编程。 2. 可以使用Java的synchronized关键字或者Lock对象来实现同步机制,避免多线程竞争的情况。 3. 考虑如何处理异常和错误,例如网络连接异常、输入输出异常等。 4. 可以使用JavaGUI框架实现客户端的界面,例如Java Swing或者JavaFX等。 5. 可以使用Java的日志框架实现日志记录,例如log4j或者java.util.logging等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值