TCP聊天室练习之网络编程

网络编程之TCP

TCP协议时安全的, 可靠的, 但性能相对UDP要低

TCP实现聊天室

最终实现效果
效果图

思路:

客户端-->控制台录入数据
	 -->写出数据到服务端
服务端-->读取数据
	 -->写出数据
客户端-->读取服务端数据
	 -->打印到控制台	 

客户端录入数据加入循环实现多条信息发送.
服务端加入多线程, 每连接一个客户端就启用一个线程来响应数据.
聊天室, 一个客户端说话, 所有人都可以看到, 需要借助容器, 获取每一个客户端, 然后在输出时, 把数据发送给每一个人(除了自己), 其他客户带接收到数据后会打印到控制台, 实现聊天室

客户端姓名, 在每一个客户端建立连接之前, 从控制台接收一个姓名
初始化时, 将这个姓名说:发送给每一个客户端, 后面接上要发送的数据.
加入聊天室, 退出聊天室分别在初始化时, 和客户端退出时进行打印即可
私聊消息加入数据判断, 分析数据, 然后发送给指定的客户端, 而不是发送给所有客户端

代码:

/**
 * 	释放资源工具类
 * @author ASUS
 *
 */
public class CloseUtil {
	
	/**	释放资源方法
	 * @param closeables
	 */
	public static void close(Closeable...closeables ) {
		for (Closeable closeable : closeables) {
			try {
				if(null != closeable)
					closeable.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}


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

import com.net.chat03.CloseUtil;

/**
 * 	TCP聊天室客户端, 加入私聊功能
 * @author ASUS
 *
 */
public class Client {
	public static void main(String[] args) throws Exception {
		System.out.println("-----client------");
		
//		在每一个客户端连接之前, 从控制台接收客户端姓名
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("请输入用户名:");
		String name = br.readLine();
		
		Socket client = new Socket("localhost",8888);
		
		new Thread(new Send(client, name)).start();
		new Thread(new Receive(client)).start();
		
	}
	
//	发送数据
	static class Send implements Runnable{
		private DataOutputStream dos;
		private Socket client;
		private BufferedReader console;
		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());
				send(this.name);	//发送姓名
				isRunning = true;
			} catch (IOException e) {
				e.printStackTrace();
				release();
			}
		}

		@Override
		public void run() {
			
			while(isRunning) {
				String msg = getMsg();
				if(!"".equals(msg)) {
					send(msg);
				}
			}
			
		}
		
//		发送控制台录入的数据
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
//		获取控制台的数据
		private String getMsg() {
			try {
				return console.readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return "";
		}
		
//		释放资源
		private void release() {
			isRunning = false;
			CloseUtil.close(dos,client);
		}

	}
	
//	接收数据
	static 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) {
				e.printStackTrace();
				release();
			}
		}

		@Override
		public void run() {
			
			while(isRunning) {
				String msg = receive();
				System.out.println(msg);
			}
			
		}
		
//		接收数据
		private String receive() {
			try {
				return dis.readUTF();
			} catch (IOException e) {
				e.printStackTrace();
				release();
			}
			return "";
		}
		
//		释放资源
		private void release() {
			isRunning = false;
			CloseUtil.close(dis,client);
		}

	}
	
}

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;

import com.net.chat03.CloseUtil;

/**
 * 	TCP聊天室服务端, 加入私聊功能
 * @author ASUS
 *
 */
public class Server {
	
	public static void main(String[] args) throws Exception {
		System.out.println("-----server-----");
		ServerSocket server = new ServerSocket(8888);
		
		while(true) {
			Socket client = server.accept();
			System.out.println("一个客户端建立了连接");
			Channel c = new Channel(client);
			Channel.users.add(c);
			new Thread(c).start();;
		}
		
	}
	
	static class Channel implements Runnable{

//		CopyOnWriteArrayList用于存储客户端, 用于并发修改
		public static CopyOnWriteArrayList<Channel> users = new CopyOnWriteArrayList<>();
		private Socket client;
		private DataOutputStream dos;
		private DataInputStream dis;
		private boolean isRunning;
		private String name;	//客户端名字
		
		public Channel(Socket client) {
			this.client = client;
			try {
				dis = new DataInputStream(client.getInputStream());
				dos = new DataOutputStream(client.getOutputStream());
				
				this.name = receive();				//初始化时先接收这个名字
				this.send(name + " 欢迎你的到来");		//接收到名字后就打印出来
				this.sendOthers(name + "来到了聊天室", true);	//在聊天室对所有人说我来了
				
				isRunning = true;
			} catch (IOException e) {
				e.printStackTrace();
				release();
			}
		}
		

		/**
		 * 	给其他人发送消息
		 * 在容器中遍历所有客户端, 并向每一个客户端发送消息(除了自己)
		 *  私聊
		 * 格式: @XXX:xxxx
		 * @param msg 数据
		 * @param isSys 系统消息(是系统消息就为true, 不是系统消息为false)
		 */
		private void sendOthers(String msg, boolean isSys) {
			boolean isPrivate = msg.startsWith("@");	//数据@开头代表私聊
			
			if(isPrivate) {		
				int index = msg.indexOf(":");			//判断:索引位置
				String name = msg.substring(1,index);	//通过索引位置, 确定name
				msg = msg.substring(index + 1);			//截取索引+1位置及后面的内容, 确定msg
				
				for(Channel c : users) {
					if(c.name.equals(name)) {			//容器中的姓名和私聊姓名相同
						c.send(this.name + "悄悄地对你说:" + msg);
					}
				}
				
			}else {
				
				for(Channel c : users) {
					if(c == this) {	//容器中的对象是自己的时候不打印
						continue;
					}
					if(isSys) {		//系统消息, 直接打印
						c.send(msg);
					}else {			//不是系统消息, 某某说:什么什么
						c.send(name + "说:" + msg);
					}
				}
				
			}
		}
		
//		发送数据
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
				release();
			}
		}
		
//		接收数据
		private String receive() {
			String msg = "";
			try {
				msg = dis.readUTF();
			} catch (IOException e) {
				System.out.println("客户端退出");
				release();
			}
			return msg;
		}
		
//		释放资源
		private void release() {
			isRunning = false;
			CloseUtil.close(dos,dis,client);
			users.remove(this);	//退出后删除容器中对象
			sendOthers(name + "离开了聊天室...", true);
		}
		
		@Override
		public void run() {

			while(isRunning) {
				String msg = receive();
				if(!msg.equals("")) {
					//send(msg); 不再向给自己这个客户端发送消息
					sendOthers(msg,false);	//给所有其他的客户端发送消息
				}
			}
			
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值