当前位置:   article > 正文

java 实现群聊私聊功能(网络编程+io流) 封装终极版_java实现私聊群聊的功能

java实现私聊群聊的功能

首先明确一下目的:实现一个服务端加多个客户端可用,带有群聊和私聊功能的小项目(通过控制台输入);

服务端

服务端起到了转发的作用,一个client通过发送消息给服务端,服务端接受到消息之后判断是要群发还是私发(私发有格式),然后将消息发送给所有在线的客户端;

明确了功能咱们来分析下,服务端是用来群发的,群发给谁?所有在线的client,那么这些client是需要上线就存储,下线就移除的,所以肯定是需要容器的,并且这个容器还是能够支持多线程等功能,我们选择的是CopyOnwriteArrayList那么服务端的功能代码如下:

public class NewServe {
	static CopyOnWriteArrayList<Channel> array = new CopyOnWriteArrayList(); //一个静态的全局变量,方便使用,用来存放开启的客户端的信息;
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(8888); //端口号是8888
		System.out.println("----------Server------------");
		while(true){
			Socket client = server.accept();
			Channel channel = new Channel(client);
			array.add(channel);
			new Thread(channel).start();
		}   //阻断式的接受就直接一个永真循环
	}
}
class Channel implements Runnable{
//channel封装了
	private String name = null;
	private boolean flag = true;
	private DataInputStream dis;
	private DataOutputStream dos;
	private Socket client;
	public Channel(Socket client) {
		this.client = client;
		try {
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			dis = new DataInputStream(client.getInputStream());
			name = dis.readUTF();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//接受消息,之所以定义就是封装一下,降低了run()方法里边的代码量;
	public String receive(){
		String msg = "";
		try {
			msg = dis.readUTF();
		} catch (IOException e) {
		//这里边捕获到异常之后做的事情,包括通知所有人还包括把这个关闭流等;
			sendAll("-----系统消息:"+name+"退出了群聊");
			reseve(dis);
			flag = false;
		}
		return msg;
	}
	//没用但是适合循序渐进的编程方式,先写出来这个send方法,之后。。。。自己发现了sendAll
	public void send(String msg){
		try {
			dos.writeUTF(msg);
			dos.flush();
		} catch (IOException e) {
			e.printStackTrace();
			reseve(dos);
			flag = false;
		}
	}
	//发送给所有人
	public void sendAll(String msg){
		if(msg.contains("@")) {
			String[] str1 = msg.split("@");
			for(Channel ch:NewServe.array){
				if(str1[0].equals(ch.name)) {
					ch.send("(来自私聊)"+name+":"+str1[1]);
				}//判断一下是不是用的是私聊格式的;
			}
		}else {
			for(Channel ch:NewServe.array){
				if(ch.client!=this.client){
					if(!ch.client.isClosed()) {
						ch.send(name+":"+msg);
					}else {
						NewServe.array.remove(ch); //知道有关着的客户端的时候立马把他从容器中移除;
					}
				}
			}
		}
	}
	//就是封装的一个能够关闭流的方法方便调用;
	public void reseve(Closeable... dis){
		for(Closeable d:dis){
			try {
				d.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	//因为是多线程,并且实现了Runnable接口所以必须重写这个方法的;
	@Override
	public void run() {
		while(flag){
			String msg = receive();
			sendAll(msg);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

以上就是服务端的全部代码,总的就是,服务端接受到一个socket 就创建一个channel对象并传入socket,同时容器内加上这个channel对象;channel是一个实现了Runnable接口的类,里边有唯一的socket 作用就是如果接受到这个socket的发送信息就直接调用类里边的sendAll方法发送给所有人(通过判断发送给私人),每一个socket都有一个channel对象与之对应;

客户端

在写网络编程的时候我说过,如果想实现收发同步那必须是要用多线程的,所以毫无疑问,不仅是多线程,还有做到收一个线程,发一个线程,以下是封装后的代码:

发送端

public class Send1 implements Runnable{
	private String name = "";
	private DataOutputStream dos ;
	private boolean flag = true;
	private Socket client;
	static Scanner in = new Scanner(System.in);
	public Send1(Socket client){
		this.client = client;
		try {
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		this.name = Thread.currentThread().getName();
		try {
			dos.writeUTF(name);
			dos.flush();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		while(flag){
			String msg = in.next();
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
				flag = false;
				reseve(dos);
			}
		}
	}
	public void reseve(Closeable... dis){
		for(Closeable d:dis){
			try {
				d.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

接受端

public class Receive1 implements Runnable{
	private DataInputStream dis ;
	private boolean flag = true;
	private Socket client;
	static Scanner in = new Scanner(System.in);
	public Receive1(Socket client){
		this.client = client;
		try {
			dis = new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		String msg = "";
		while(flag){
			try {
				msg = dis.readUTF();
				System.out.println(msg);
			} catch (IOException e) {
				e.printStackTrace();
				reseve(dis);
				flag = false;
			}
		}
	}
	public void reseve(Closeable... dis){
		for(Closeable d:dis){
			try {
				d.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

这两类都是实现了Runnable接口,并且两个类中封装的socket肯定是同一个(参考用客户端的时候怎么写);也就是稍微封装一下,两类的逻辑差不多,都是平平无奇的;
用客户端的时候长这样

public class Newclient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("--------------Client-------------");
		Socket client = new Socket("localhost",8888);
		new Thread(new Send1(client),"李世翔").start();
		new Thread(new Receive1(client)).start();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

由于个人水平我也就能写到这个地方了,有好多地方能够改的,写出来仅供参考;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/178638
推荐阅读
相关标签
  

闽ICP备14008679号