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

和上一篇博客差不多,只不过是在群里的基础之上增加了私聊的功能,我们约定,私聊格式为:@xxx:msg

如何实现私聊呢,加入客户端c给服务器发送消息,服务器不再是把消息转发给所以除c以外的客户端,而是解析数据格式,转发给与name(“:”之前,“@”之后的字符串)有相同名字的客户端即可。

群聊功能与之前类似,这里不再说说明。

下面是所有类的代码:

(1)Server类的代码:

package chat4;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

/**注意:实现私聊(约定数据格式为:@xxx:msg
 * 在线聊天室:使用容器实现群聊
 * 服务器
 * @author Administrator
 * 需要修改和遍历数据的时候使用list会操作不方便
 * 问题:为什么加了static,在主函数里面就没错啦?
 * 
 */
public class Server {
	//CopyOnWriteArrayList和list用法一样
	private static CopyOnWriteArrayList<Channel> all=new CopyOnWriteArrayList<Channel>();
	public static void main(String[] args) throws IOException {
		System.out.println("----------server----------");
		
		//创建服务器
		ServerSocket server=new ServerSocket(8977);
		
		while(true) {
			//阻塞式等待客户端的连接
			Socket client=server.accept();
			System.out.println("一个客户端连接上。。。");
			Channel c=new Channel(client);
			all.add(c);//容器管理所有的成员
			new Thread(c).start();
		}
	}
	
	//一个客户代表一个Channel
	static class Channel implements Runnable{
		private DataInputStream dis ;
		private DataOutputStream dos;
		private Socket client;
		private boolean isRunning;//程序是否停下来的标志
		private String name;
		public Channel(Socket client) {
			this.client=client;
			try {
				dis = new DataInputStream(client.getInputStream());
				dos=new DataOutputStream(client.getOutputStream());
				isRunning=true;
				
				//获取名称
				name=receiveMsg();
				this.sendMsg(name+"同学,"+"欢迎您的到来");
				sendOther(this.name+"加入群聊天室",true);
				
			} catch (IOException e) {
				System.out.println("-----1   出问题----");//自己方便观察哪里出问题
				release();//注意:这里做了修改
			}
		}
		
		//接收消息(服务器读取客户端发过来的信息)
		private String receiveMsg() {
			String msg="";
			try {
				msg=dis.readUTF();
			} catch (IOException e) {
				System.out.println("-----receive 出问题----");//自己方便观察哪里出问题
				release();//注意:这里做了修改
			}
			return msg;
		}
		
		//发送消息(服务器反馈信息给客户端)
		private void sendMsg(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				System.out.println("-----send 出问题----");//自己方便观察哪里出问题
				release();//注意:这里做了修改
			}
		}
		
		//发送消息(服务器反馈信息(其实就是服务器读取的当前客户端的消息)给其他所有的客户端(除了当前客户端)
		private void sendOther(String msg,boolean isSysMsg) {
			boolean isPrivate=msg.startsWith("@");
			if(isPrivate) {//是私聊消息
				int idx=msg.indexOf(":");
				//获取目标和数据
				String targetName=msg.substring(1,idx);
				msg=msg.substring(idx+1);
				System.out.println("服务器收到的私聊信息为:"+targetName+"&"+msg);
				for(Channel other:all) {
					if(other.name.equals(targetName)) {//找到目标
						other.sendMsg(this.name+":"+msg);
					}
				}
			}else {
				for(Channel other:all) {//服务器把收到的客户端的消息发给其余所有的客户端(除了当前客户端)
					if(other!=this) {
						if(isSysMsg) {
							other.sendMsg(msg);//系统消息
						}else {
							other.sendMsg(this.name+":"+msg);//群聊消息
						}
					}
				}
			}
			
		}
		
		//释放资源
		private void release() {
			this.isRunning=false;//释放资源意味着该该客户端要停下来
			Utils.close(dis,dos,client);//自己写的一个工具类Utils
			all.remove(this);//当前客户端退出
			sendOther(this.name+"离开了聊天室。。。", true);
		}

		@Override
		public void run() {
			while(isRunning) {
				//流程:服务器读取客户端消息-服务器发送反馈消息给客户端-释放资源
				String msg=receiveMsg();
				//System.out.println("服务器读取到的信息为:"+msg);
				if(!msg.equals("")) {
					//sendMsg(msg);
					sendOther(msg,false);//群聊消息
				}
				
			}
			//release();//老师这里没有释放
		}
	}
}

(2)Client类的代码(下面的Receive类和Send类为Client类所用):

package chat4;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**注意:
 * 在线聊天室:使用容器实现群聊
 * 客户端
 * @author Administrator
 *
 */
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.println("请输入用户名:");
		String name=br.readLine();
		//建立套接字
		Socket client=new Socket("localhost", 8977);
		
		//客户端发送消息(先读取控制台的内容,再写入到客户端的输出流里)
		new Thread(new Send(client,name)).start();
		new Thread(new Receive(client)).start();
	}
}

(3)Receive类的代码:

package chat4;

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

/**
 *作用: 被MutiClient类用到,是客户端用来接收服务器反馈的消息的
 *客户端读取服务器反馈的消息,并显示输出
 * @author Administrator
 *
 */
public class Receive implements Runnable {
	private DataInputStream dis;
	private Socket client;
	private boolean isRunning;
	public Receive(Socket client) {
		this.client=client;
		isRunning=true;
		try {
			dis=new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("客户端的Receive构造器有问题");
			release();
		}
	}
	
	@Override
	public void run() {
		while(isRunning) {
			String msg=receiveMsg();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	
	private String receiveMsg() {
		String msg="";
		try {
			msg=dis.readUTF();
		} catch (IOException e) {
			System.out.println("客户端的receiveMsg有问题");
			release();
		}
		return msg;
	}
	//释放资源
	private void release() {//内部类的使用
		this.isRunning=false;//释放资源意味着该该客户端要停下来
		Utils.close(dis,client);//自己写的一个工具类Utils
	}
}

(4)Send类的代码:

package chat4;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 作用:被MutiClient类用到,是客户端用来发送消息的
 * 具体操作是:客户端读取控制台消息--写入到client的输出流
 * @author Administrator
 *
 */
public class Send implements Runnable {
	private BufferedReader console;
	private DataOutputStream dos;
	private Socket client;
	private boolean isRunning;
	private String name;
	public Send(Socket client,String name) {
		this.client=client;
		this.name=name;
		console=new BufferedReader(new InputStreamReader(System.in));
		try {
			dos=new DataOutputStream(client.getOutputStream());
			
			sendMsg(name);//发送名字
			isRunning=true;
		} catch (IOException e) {
			System.out.println("客户端的send构造器有问题");
			this.release();
		}
	}
	
	@Override
	public void run() {
		while(isRunning) {
			//客户端读取控制台消息--写入到client的输出流
			String msg=ConsoleMsg();
			if(!msg.equals("")) {
				sendMsg(msg);
			}
			
		}
	}
	private String ConsoleMsg() {//先读取控制台的内容
		String msg="";
		try {
			msg=console.readLine();
		} catch (IOException e) {
			System.out.println("客户端的ConsoleMsg有问题");
			this.release();
		}
		return msg;
		
	}
	private void sendMsg(String msg) {//再写入到客户端的输出流里
		try {
			dos.writeUTF(msg);
			dos.flush();//这个一定不能忘记啦
		} catch (IOException e) {
			System.out.println("客户端的sendMsg有问题");
			this.release();
		}
	}
	//释放资源
	private void release() {//内部类的使用
		this.isRunning=false;//释放资源意味着该该客户端要停下来
		Utils.close(dos,client);//自己写的一个工具类Utils
	}
}

(5)Utils类的代码(编写工具类,方便资源的释放):

package chat4;

import java.io.Closeable;

/**
 * 自己写一个释放资源的工具类
 * @author Administrator
 *
 */
public class Utils {
	public static void close(Closeable... targets) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			}catch(Exception e){
				
			}
		}
	}
}

运行结果如下:

java代码微信群聊天记录提取 java群聊功能实现_网络编程