TPC网络编程(实现群聊以及私聊)

/*
 * 
 * 在线聊天室:服务器
 * 目标:私聊
 * 
 */
public class Chat {
	// 一般在多线程里面使用容器,假设这个容器我们既要改又要遍历(修改和使用同时用),不建议使用 ArrayList
	// CopyOnWriteArrayList 并发容器
	private static CopyOnWriteArrayList<Channel> all = new CopyOnWriteArrayList<Channel>();

	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket 创建服务器
		ServerSocket server = new ServerSocket(9999);
		// 2、阻塞式等待连接 accept()
		while (true) {
			Socket client = server.accept();
			// 一个 accept() 就为一个连接
			System.out.println("一个客户端建立了连接");
			Channel c = new Channel(client);
			all.add(c);// 交给容器管理所有的成员
			new Thread(c).start();
		}
	}

	// 一个客户代表一个Channel
	static class Channel implements Runnable {
		private DataOutputStream dos;
		private DataInputStream dis;
		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;
				// 获取名称
				this.name = receive();
				// 欢迎你的到来
				this.send("欢迎你的到来");
				// true 代表是否为系统消息
				sendOthers(this.name + "来到了聊天室", true);
			} catch (IOException e) {
				System.out.println("-----构建出错-----");
				release();
			}
		}

		// 接收消息
		private String receive() {
			// 避免空指针
			String msg = "";
			try {
				msg = dis.readUTF();
			} catch (IOException e) {
				System.out.println("-----接收出错-----");
				release();
			}
			return msg;
		}

		// 发送消息
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				System.out.println("-----发送出错-----");
				release();
			}
		}

		/*
		 * 跳过自己发送消息给其他人 
		 * 私聊:获取自己的消息,发给其他人 
		 * 私聊:约定数据格式:@xxx:msg
		 * 
		 */
		private void sendOthers(String msg, boolean isSys) {
			// 是否以 @ 开头
			boolean isPrivate = msg.startsWith("@");
			if (isPrivate) {// 私聊
				// 第一次冒号出现的位置
				int idx = msg.indexOf(":");
				// 获取目标和数据
				// substring 左闭右开,不包含
				// @xxx @ 为 0
				String targetName = msg.substring(1, idx);
				msg = msg.substring(idx + 1);
				for (Channel other : all) {
					if (other.name.equals(targetName)) {// 找到目标 xxx
						other.send(this.name + "悄悄的对你说:" + msg);// 私聊消息
						break;//不往下走了
					}
				}
			} else {
				for (Channel other : all) {
					if (other == this) {// 自己
						continue;
					}
					if (!isSys) {
						// 群聊消息
						other.send(this.name + "对所有人说:" + msg);
					} else {
						other.send(msg); // 系统消息
					}
				}
			}
		}

		// 释放资源
		private void release() {
			this.isRunning = false;
			Utils.close(dis, dos, client);
			// 退出容器
			all.remove(this);
			sendOthers(this.name + "离开了群聊...", true);
		}

		@Override
		public void run() {
			while (isRunning) {
				String msg = receive();
				if (!msg.equals("")) {
					// 发给自己
					// send(msg);
					// 发给其他人
					sendOthers(msg, false);
				}
			}

		}
	}
}
/*
 * 
 * 在线聊天室:客户端
 * 目标:私聊
 * 
 */
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();
		// 1、建立连接:使用 Socket 创建客户端 + 服务器的地址和端口
		Socket client = new Socket("localhost", 9999);
		// 2、客户端发送消息
		new Thread(new Send(client,name)).start();
		new Thread(new Receive(client)).start();
	}
}
/*
 * 
 * 使用多线程封装:接收端
 * 1、接收消息
 * 2、释放资源
 * 3、重写run
 */
public class Receive implements Runnable{
	private DataInputStream dis;
	private Socket client;
	private boolean isRunning;
	public Receive( Socket client) {
		this.client = client;
		try {
			dis = new DataInputStream(client.getInputStream());
			isRunning = true;
		} 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(isRunning) {
			String msg = receive();
			if (!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	
	// 释放资源
		private void release() {
			this.isRunning = false;
			Utils.close(dis, client);
		}
}
/*
 * 
 * 使用多线程封装:发送端
 * 1、发送消息
 * 2、从控制台获取消息
 * 3、释放资源
 * 4、重写run
 */
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));
		isRunning = true;
		try {
			dos = new DataOutputStream(client.getOutputStream());
			// 发送名称
			send(name);
		} catch (IOException e) {
			System.out.println("=====1=====");
			this.release();
		}
	}

	@Override
	public void run() {
		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.isRunning = false;
		Utils.close(dos, client);
	}
}
/*
 * 工具类
 * 
 */
public class Utils {
	/*
	 * 释放资源
	 */
	// ...三个点代表可变参数
	// Io 和 Socket 都实现了 Closeable
	public static void close(Closeable... targets) {
		for (Closeable target : targets) {
			try {
				if (null != target) {
					target.close();
				}
			} catch (Exception e) {

			}
		}
	}
}
发布了45 篇原创文章 · 获赞 1 · 访问量 5220

猜你喜欢

转载自blog.csdn.net/weixin_42814000/article/details/104494670