网络编程模拟在线聊天室,实现群聊与私聊

基本的思想

在线聊天室:服务器端和客户端

  • 目标是实现多个客户可以收发多条信息,加入多线程,可以开启多个用户端
    如果直接用lamda写入线程可能存在问题:
    线程代码太多,不好维护
    客户端读写没有分开,必须先写后读。
    所以将线程代码放入一个类中,进行封装
    群聊:
    加入容器实,将创建的客户端都加入client容器中,在进行发送消息的时候,遍历整个容器,依次将消息发给各个客户端。
    私聊:
    规定一种私聊的方式,例如@xxx,然后遍历容器,找到目标客户,将信息发出:
    服务器端
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;


public class THChat3 {
	
	//线程安全的容器,允许在用容器的时候进行修改,保证数据的一致性
	private static CopyOnWriteArrayList<Channal> all=new CopyOnWriteArrayList<Channal>();
	public static void main(String[] args) throws IOException {
	System.out.println("------Server------");
	//1.指定端口。使用ServerSocket创建服务器
	ServerSocket server=new ServerSocket(8888);
	 // 2.阻塞式等待连接accept
	//创建一个客户端就进行连接
	while(true) {
		Socket client=server.accept();
		System.out.println("一个客户端建立了");
		Channal c=new Channal(client);
		all.add(c);//管理所有的客户端成员
		new Thread(c).start();
		
	}
		
		
}	
	
	
	
	static class Channal implements Runnable{
		private DataInputStream dis;
		private DataOutputStream dos;
		private Socket client;
		private boolean isRunnable;
		private String name;
		public Channal(Socket client) {
			super();
			this.client = client;
			try {
				dis = new DataInputStream(client.getInputStream());
				dos=new DataOutputStream(client.getOutputStream());
				isRunnable=true;
				//获取名称:
				name=receive();
				this.send("欢迎加入聊天室");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("初始化出错");
				release();//只要出错了就停止
			}
			
		}
		
		//接受消息
		private String receive() {
			String msg ="";//避免空指针
			try {
				msg = dis.readUTF();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("接受出错");
				release();
			}
			return msg;
		}
		//发送消息,
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("发送出错");
				release();
			}
		}
		//发送消息,群聊,将自己的消息发给其他人
		//私聊:约定数据格式:@xxx:msg;
			private void sendOther(String msg) {
				boolean isPrivate=msg.startsWith("@");
				if(isPrivate) {//私聊
					//获取目标和数据
					int index=msg.indexOf(":");
					String targetName=msg.substring(1,index);
					msg=msg.substring(index+1);
					for(Channal others:all) {
						if(others.name.equals(targetName)) {
							others.send(this.name+"私聊你说"+msg);
						}
					}
				}else {//群聊
					for(Channal others:all) {
						if(others==this) {//如果是自己,则表示是不发
							continue;
						}
						else {
							others.send(this.name+"说"+msg);
						}
					}
				}
			}
		//释放资源
		private void release() {
			this.isRunnable=false;
			chatUtils.close(dis,dos,client);
			all.remove(this);
			sendOther(this.name+"退出聊天室");
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(isRunnable) {
				//读
				String msg=receive();
				if(!msg.equals("")) {
					//发送
					//send(msg);
					sendOther(msg);
				}
			}
		}
	}
	
	
	
}

客户端

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 java.net.UnknownHostException;


public class THClient3 {
public static void main(String[] args) throws UnknownHostException, IOException {
	System.out.println("------client------");
	BufferedReader console=new BufferedReader(new InputStreamReader(System.in));
	System.out.println("请输入用户名:");
	String name=console.readLine();
//	 1.建立连接:使用Socket创建客户端,指定服务器的地址和端口
	Socket client=new Socket("localhost",8888);//指定本机的服务器,端口号
	//客户端发送消息,接收消息
	//利用线程实现收发信息的同步
	new Thread(new Send(client,name)).start();
	new Thread(new Receive(client)).start();
}
}

封装的发送端和接受端

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


public class Receive implements Runnable{
	private BufferedReader console;
	private Socket client;
	private DataInputStream dis;
	private boolean isRunning=true;
	
	public Receive(Socket client) {
		this.client=client;
		try {
			dis = new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("recive");
			release();
		}
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(isRunning) {
			String msg=receive();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	
	//接受消息
		private String receive() {
			String msg ="";//避免空指针
			try {
				msg = dis.readUTF();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("接受出错");
				release();
			}
			return msg;
		}
	
	private void release() {
		this.isRunning=false;
		chatUtils.close(this.dis,client);
	}
	
}

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


public class Send  implements Runnable{
	private BufferedReader console;
	private Socket client;
	private DataOutputStream dos;
	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(this.client.getOutputStream());
			isRunning=true;
			//只要管道一建立好就将名字发送过去。
			send(name);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("send");
			release();
		}
		 
	}
	//从控制台获取消息
	private String getStrFromConsole() {
		try {
			return console.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "";
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(isRunning) {
			String msg=getStrFromConsole();
			if(!msg.equals("")) {
				send(msg);
			}
		}
	}
	
	//发送消息
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				System.out.println("发送出错");
				release();
			}
		}
	private void release() {
		this.isRunning=false;
		chatUtils.close(this.dos,client);
	}

}

用于释放资源的工具类

/**
 * 
 * 工具类:释放资源实现了Closeable接口
 * 
 * 
 * @author lsy
 *
 */
import java.io.Closeable;


public class chatUtils {

	//IO中都实现了Closeable接口,Socket也实现了
	public static void close(Closeable... targets) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			}catch(Exception e) {
				
			}
		}
	}
}

你可能感兴趣的:(java学习)