TCP创建多人聊天室

群聊-聊天室

群聊:任何时候,任何一个客户端都可以向其它客户端发送和接受数据,服务器只起到转发的作用。

1、首先创建一个聊天室的简易版(版本1)。

需求:可以多个用户同时访问服务端,并且可以不断各自请求服务端获取响应的数据。

可以多个用户同时访问服务端:这个需要在服务端创建多线程,使服务端的监听套接字,可以被多个客户端使用。

可以不断各自请求服务端获取响应的数据:这个只需要在客户端的数据发送和接受处加上一层死循环,在服务端的外层套上一层死循环即可。

需要改进的不足之处:

1、客户端只能自己对自己说话,还没有实现群聊的效果。

2、代码比较多,不易于维护。

3、客户端的接收和发送数据没有分离,必须等到指定者发送数据,才能接收指定者的消息。

服务端

代码:

 1 package 在线聊天室;
 2 
 3 import java.io.DataInputStream;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.net.ServerSocket;
 7 import java.net.Socket;
 8 
 9 /**
10  * 模拟单人聊天室
11  * @author liuzeyu12a
12  *
13  */
14 public class Chat {
15     public static void main(String[] args) throws Exception {
16         //建立服务器端套接字,绑定本地端口
17         ServerSocket server = new ServerSocket(9999);
18             while(true){
19                 new Thread(()->{
20                     Socket client = null;
21                     try {
22                         //监听客户端
23                         client = server.accept();
24                     } catch (IOException e) {
25                         e.printStackTrace();
26                     }
27                     System.out.println("一个客户端建立了连接...");
28                     
29                     //接受客户端的消息
30                     DataInputStream dis = null;
31                     DataOutputStream dos = null;
32                     try {
33                         dis = new DataInputStream(client.getInputStream());
34                         dos = new DataOutputStream(client.getOutputStream());
35                     } catch (IOException e) {
36                         e.printStackTrace();
37                     }
38                     
39                     boolean isRunning = true;
40                     while(isRunning) {
41                         String msg = null;
42                         try {
43                             msg = dis.readUTF();
44                         } catch (IOException e) {
45                             e.printStackTrace();
46                         }
47                         //返回回去给客户端    
48                         try {
49                             dos.writeUTF(msg);
50                             dos.flush();
51                         } catch (IOException e) {
52                             isRunning = false;  //客户端断开即停止读写数据
53                         }
54                     }
55                     
56                     //释放资源
57                     try {
58                         if(null!=dos)
59                         dos.close();
60                     } catch (IOException e) {
61                         e.printStackTrace();
62                     }
63                     try {
64                         if(null!=dis)
65                         dis.close();
66                     } catch (IOException e) {
67                         e.printStackTrace();
68                     }
69                     try {
70                         if(null!=client)
71                         client.close();
72                     } catch (IOException e) {
73                         e.printStackTrace();
74                     }
75                 }).start();
76                 
77             }
78     }
79 }
View Code

客户端

代码:

 1 package 在线聊天室;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.DataInputStream;
 5 import java.io.DataOutputStream;
 6 import java.io.IOException;
 7 import java.io.InputStreamReader;
 8 import java.net.Socket;
 9 
10 /**
11  * TCP模拟单人聊天室
12  * @author liuzeyu12a
13  *
14  */
15 public class Client {
16     public static void main(String[] args) throws IOException, IOException {
17         //建立客户端套接字
18         Socket client = new Socket("localhost",9999);
19         
20         //发送数据
21         BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
22         DataInputStream dis = new DataInputStream(client.getInputStream());
23         DataOutputStream dos = new DataOutputStream(client.getOutputStream());
24         boolean isRunning = true;
25         while(isRunning) {
26             String msg = reader.readLine();
27             dos.writeUTF(msg);
28             //客户端接收服务器的响应
29     
30             String respond = dis.readUTF();
31             System.out.println(respond);
32         }
33         //关闭
34         client.close();
35     }
36 }
View Code

2、我们将版本1的2,3问题进行改善一下(版本2)。

封装:

1)将服务器的接受和发送数据用一个Channel 类进行封装,这样子一个client 就对应了一个Channel对象了

2)将客户端的接收和发送分离开,使用两个线程进行分割,这样子接收数据和发送数据就可以不用同时进行了(为群聊做准备)。

将关闭资源的系列用一个ChatUtils工具类包装起来,使用Closeable接口。

 1 package 在线聊天室;
 2 
 3 import java.io.Closeable;
 4 import java.io.IOException;
 5 /**
 6  * 用于聊天室一些流释放资源
 7  * @author liuzeyu12a
 8  *
 9  */
10 public class ChatUtils {
11 
12     static public void close(Closeable...closeables) {
13         for(Closeable target :closeables) {
14             if(null!=target) {
15                 try {
16                     target.close();
17                 } catch (IOException e) {
18                     e.printStackTrace();
19                 }
20             }
21         }
22     }
23 }

利用面向对象思想对服务端进行封装

 1 package 在线聊天室;
 2 
 3 import java.io.DataInputStream;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.net.ServerSocket;
 7 import java.net.Socket;
 8 
 9 /**
10  * 1)将服务器的接受和发送数据用一个Channel 类进行封装,
11  * 这样子一个client 就对应了一个Channel对象了
12 
13 2)将客户端的接收和发送分离开,
14 使用两个线程进行分割,这样子接收数据和发送数据就可以不用同时进行了
15 (为群聊做准备)。
16  * @author liuzeyu12a
17  *
18  */
19 public class Chat2 {
20     public static void main(String[] args) throws Exception {
21         System.out.println("-----Server-----");
22         //建立服务器端地址,并绑定本地端口
23         ServerSocket server = new ServerSocket(8989);
24         
25         //这边加上死循环是为了接受多个客户的请求
26         while(true) {
27                 //监听
28                 Socket    client = server.accept();        
29                 System.out.println("一个客户端建立了连接");
30                 new Thread(new Channel(client)).start();
31         }
32         
33     }
34     //静态内部类,封装处理客户端的数据
35     static class Channel implements Runnable{
36         private DataInputStream dis;
37         private DataOutputStream dos;
38         private Socket client;
39         private boolean isRunning;
40         //构造器
41         public Channel(Socket client) {
42             this.client = client;
43             this.isRunning = true;
44             try {
45                 dis = new DataInputStream(
46                         client.getInputStream());
47                 dos = new DataOutputStream(
48                         client.getOutputStream());
49             } catch (IOException e) {
50                 release();
51             }
52         }
53         @Override
54         public void run() {
55             while(isRunning) {
56                 String msg = receive();
57                 if(!msg.equals(""))
58                 send(msg);
59             }
60         }
61         
62         //发送数据
63         public void send(String msg) {
64             try {
65                 dos.writeUTF(msg);
66                 dos.flush();
67             } catch (IOException e) {
68                 System.out.println("发送数据失败");
69                 release();
70             }
71         }
72         
73         //接受数据
74         public String receive() {
75             try {
76                 String msg = "";
77                 msg = dis.readUTF();
78                 return msg;
79             } catch (IOException e) {
80                 isRunning = false;
81                 System.out.println("接受数据失败");
82                 release();
83             }
84             return "";
85         }
86         
87         //释放资源
88         public void release() {
89             ChatUtils.close(client,dos,dis);
90         }
91     }
92 }
View Code 

客户端的发送:

 1 package 在线聊天室;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.io.InputStreamReader;
 7 import java.net.Socket;
 8 
 9 /**
10  * 为聊天室Client2 的发送功能服务
11  * @author liuzeyu12a
12  *
13  */
14 public class Send implements Runnable{
15     //准备数据
16     private BufferedReader reader;
17     //发送数据
18     private DataOutputStream dos;
19     private Socket client;
20     private boolean isRunning;
21     //构造器
22     public Send(Socket client) {
23         this.client = client ;
24         this.isRunning = true;
25         reader= new BufferedReader(
26                 new InputStreamReader(System.in));
27         try {
28             dos= new DataOutputStream(client.getOutputStream());
29         } catch (IOException e) {
30             System.out.println("DataOutputStream对象创建失败");
31             release();
32         }    
33     }
34     @Override
35     public void run() {
36         while(isRunning) {
37             send("");
38         }
39     }
40     
41     //发送数据
42     public void send(String msg) {
43         try {
44             msg = getMsgFromConsole();
45             dos.writeUTF(msg);
46             dos.flush();
47         } catch (IOException e) {
48             System.out.println("数据发送失败");
49             release();
50         }
51     }
52     
53     public String getMsgFromConsole(){
54         try {
55             return reader.readLine();
56         } catch (IOException e) {
57             e.printStackTrace();
58         }
59         return null;
60     }
61     //释放资源
62     public void release() {
63         isRunning = false;
64         ChatUtils.close(dos,client,reader);
65     }
66 }
View Code

客户端的接收:

 1 package 在线聊天室;
 2 
 3 import java.io.DataInputStream;
 4 import java.io.IOException;
 5 import java.net.Socket;
 6 
 7 /**
 8  * 为聊天室Client2 的接收功能服务
 9  * @author liuzeyu12a
10  *
11  */
12 public class Receive implements Runnable{
13 
14     private boolean isRunning;
15     private DataInputStream dis; 
16     private Socket  client;
17     //构造器
18     public Receive(Socket  client) {
19         this.client = client;
20         this.isRunning = true;
21         try {
22             dis = new DataInputStream(client.getInputStream());
23         } catch (IOException e) {
24             System.out.println("DataInputStream对象创建失败失败");
25             release();
26         }
27     }
28     @Override
29     public void run() {
30         while(isRunning) {
31             String msg = "";
32             msg = recevie();
33             if(!msg.equals(""))
34             System.out.println(msg);
35         }        
36     }
37     
38     public String recevie() {
39         String respone = null;
40         try {
41             respone = dis.readUTF();
42             return respone;
43         } catch (IOException e) {
44             release();
45             System.out.println("数据接收失败");
46         }
47         return null;
48     }
49     
50 
51     //释放资源
52     public void release() {
53         isRunning = false;
54         ChatUtils.close(dis,client);
55     }
56 }
View Code

客户端的封装:

 1 package 在线聊天室;
 2 
 3 import java.io.IOException;
 4 import java.net.Socket;
 5 
 6 /**
 7  * 1)将服务器的接受和发送数据用一个Channel 类进行封装,
 8  * 这样子一个client 就对应了一个Channel对象了
 9 
10 2)将客户端的接收和发送分离开,
11 使用两个线程进行分割,这样子接收数据和发送数据就可以不用同时进行了
12 (为群聊做准备)。
13  * @author liuzeyu12a
14  *
15  */
16 public class Client2 {
17     public static void main(String[] args) throws IOException, IOException {
18         System.out.println("-----Server-----");
19         //创建Socket套接字,绑定服务器端口
20         Socket  client  =new Socket("localhost",8989);
21         
22         //发送数据
23         new Thread(new Send(client)).start();
24         //接收数据
25         new Thread(new Receive(client)).start();
26     }
27 }
View Code

3、实现简单的群聊功能。

服务器可以实现数据的转发功能,客户端不再局限于自己对话自己,是一个典型的群聊案例,重点理解如何将name进行传递。

客户端接受:

 1 package 在线聊天室过渡版;
 2 
 3 import java.io.DataInputStream;
 4 import java.io.IOException;
 5 import java.net.Socket;
 6 
 7 /**
 8  * 为聊天室Client2 的接收功能服务
 9  * @author liuzeyu12a
10  *
11  */
12 public class Receive implements Runnable{
13 
14     private boolean isRunning;
15     private DataInputStream dis; 
16     private Socket  client;
17     //构造器
18     public Receive(Socket  client) {
19         this.client = client;
20         this.isRunning = true;
21         try {
22             dis = new DataInputStream(client.getInputStream());
23         } catch (IOException e) {
24             System.out.println("DataInputStream对象创建失败失败");
25             release();
26         }
27     }
28     @Override
29     public void run() {
30         while(isRunning) {
31             String msg = "";
32             msg = receive();
33             if(!msg.equals("")) {
34                 System.out.println(msg);
35             }
36 
37         }        
38     }
39     public String receive() {
40         String respone = null;
41         try {
42             respone = dis.readUTF();
43             return respone;
44         } catch (IOException e) {
45             release();
46             System.out.println("数据接收失败");
47         }
48         return "";
49     }
50     
51     //释放资源
52     public void release() {
53         isRunning = false;
54         ChatUtils.close(dis,client);
55     }
56 }
View Code

客户端发送:

 1 package 在线聊天室过渡版;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.io.InputStreamReader;
 7 import java.net.Socket;
 8 
 9 /**
10  * 为聊天室Client2 的发送功能服务
11  * @author liuzeyu12a
12  *
13  */
14 public class Send implements Runnable{
15     //准备数据
16     private BufferedReader reader;
17     //发送数据
18     private DataOutputStream dos;
19     private Socket client;
20     private boolean isRunning;
21     private String name;
22     //构造器
23     public Send(Socket client,String name) {
24         this.client = client ;
25         this.isRunning = true;
26         //获取名称
27         this.name = name;
28         reader= new BufferedReader(
29                 new InputStreamReader(System.in));
30         try {
31             dos= new DataOutputStream(client.getOutputStream());
32             send(name); //发送名称
33         } catch (IOException e) {
34             System.out.println("DataOutputStream对象创建失败");
35             release();
36         }    
37     }
38     @Override
39     public void run() {
40         while(isRunning) {
41             String msg = null;
42             try {
43                 msg = reader.readLine();
44             } catch (IOException e) {
45                 System.out.println("数据写入失败");
46                 release();
47             }
48             send(msg);
49         
50         }
51     }
52     public void send(String msg) {
53         try {
54             dos.writeUTF(msg);
55             dos.flush();
56         } catch (IOException e) {
57             System.out.println("数据发送失败");
58             release();
59         }
60     }
61     //释放资源
62     public void release() {
63         ChatUtils.close(dos,client,reader);
64     }
65 }
View Code

客户端封装:

 1 package 在线聊天室过渡版;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStreamReader;
 6 import java.net.Socket;
 7 
 8 /**
 9  * 可以实现简单的群聊了。
10  * @author liuzeyu12a
11  *
12  */
13 public class Client2 {
14     public static void main(String[] args) throws IOException, IOException {
15         System.out.println("-----Client-----");
16         System.out.println("请输入用户名:");
17         BufferedReader reader = new BufferedReader(
18                 new InputStreamReader(System.in));
19         String name = reader.readLine();
20 
21         //创建Socket套接字,绑定服务器端口
22         Socket  client  =new Socket("localhost",8989);
23         
24         //发送数据
25         new Thread(new Send(client,name)).start();
26         //接收数据
27         new Thread(new Receive(client)).start();
28     }
29 }
View Code

服务端:

  1 package 在线聊天室过渡版;
  2 
  3 import java.io.DataInputStream;
  4 import java.io.DataOutputStream;
  5 import java.io.IOException;
  6 import java.net.ServerSocket;
  7 import java.net.Socket;
  8 import java.util.concurrent.CopyOnWriteArrayList;
  9 
 10 /**
 11  * 可以实现简单的群聊了。
 12  * @author liuzeyu12a
 13  *
 14  */
 15 public class Chat2 {
 16     //用于存储客户端的容器,涉及到多线程的并发操作,
 17     //使用CopyOnWriteArrayList保证线程的安全
 18     private static CopyOnWriteArrayList<Channel> all = 
 19             new CopyOnWriteArrayList<Channel>();
 20     public static void main(String[] args) throws Exception {
 21         System.out.println("-----Server-----");
 22         //建立服务器端地址,并绑定本地端口
 23         ServerSocket server = new ServerSocket(8989);
 24         
 25         //这边加上死循环是为了接受多个客户的请求
 26         while(true) {
 27                 //监听
 28                 Socket    client = server.accept();
 29                 Channel c = new Channel(client);
 30                 all.add(c);   //添加一个客户端
 31                 System.out.println("一个客户端建立了连接");
 32                 new Thread(c).start();
 33         }        
 34     }
 35     //静态内部类,封装处理客户端的数据
 36     static class Channel implements Runnable{
 37         private DataInputStream dis;
 38         private DataOutputStream dos;
 39         private Socket client;
 40         private boolean isRunning;
 41         private String name;
 42         //构造器
 43         public Channel(Socket client) {
 44 
 45             this.client = client;
 46             this.isRunning = true;
 47             try {
 48                 dis = new DataInputStream(
 49                         client.getInputStream());
 50                 dos = new DataOutputStream(
 51                         client.getOutputStream());
 52                 this.name = receive(); //接收客户端的名称
 53                 this.send("欢迎光临聊天室..");
 54                 this.sendOther(this.name+"来到了聊天室...", true);
 55             } catch (IOException e) {
 56                 release();
 57             }
 58         }
 59         @Override
 60         public void run() {
 61             while(isRunning) {
 62                 String msg = receive();
 63                 if(!msg.equals("")) {
 64                     //send(msg);
 65                     sendOther(msg,false);
 66                 }
 67 
 68             }
 69         }
 70         
 71         //发送数据
 72         public void send(String msg) {
 73             try {
 74                 dos.writeUTF(msg);
 75                 dos.flush();
 76             } catch (IOException e) {
 77                 System.out.println("发送数据失败");
 78                 release();
 79             }
 80         }
 81         /**
 82          * 获取自己的消息,然后发送给其它人
 83          * boolean isSys表示是否为系统消息
 84          * @return
 85          */
 86         public void sendOther(String msg,boolean isSys) {
 87             for(Channel other:all) {
 88                 if(other == this) {  //不再自己发给自己了
 89                     continue;
 90                 }            
 91                 if(!isSys) {
 92                     other.send(this.name+"对大家说:"+msg); //发送给自己
 93                 }else {
 94                     other.send(msg);
 95                 }
 96             }
 97         }
 98         
 99         //接受数据
100         public String receive() {
101             try {
102                 String msg = "";
103                 msg = dis.readUTF();
104                 return msg;
105             } catch (IOException e) {
106                 isRunning = false;
107                 System.out.println("接受数据失败");
108                 release();
109             }
110             return "";
111         }
112         
113         //释放资源
114         public void release() {
115             this.isRunning = false;
116             ChatUtils.close(client,dos,dis);
117             all.remove(this);
118             this.sendOther(this.name+"离开了聊天室...", true);
119         }
120     }
121 }
View Code

4、群聊功能升级(可以实现私聊某一个人@)。

只需要在发送消息的地方动手脚,其它的代码不变即可。

约定私聊的格式:@xxx:消息内容

 1 /**
 2          * 获取自己的消息,然后发送给其它人
 3          * boolean isSys表示是否为系统消息
 4          * 添加私聊的功能:可以向某一特定的用户发送数据
 5          * 约定格式:@xxx:数据
 6          * @return
 7          */
 8         public void sendOther(String msg,boolean isSys) {
 9             //判断数据是否以@开头
10             boolean isPrivate = msg.startsWith("@");
11             if(isPrivate) {
12                 //寻找:
13                 int index = msg.indexOf(":");
14                 //截取名字
15                 String targetName = msg.substring(1, index);
16                 //截取消息内容
17                 String datas = msg.substring(index+1);
18                 for(Channel other :all) {
19                     if(other.name.equals(targetName)) {
20                         other.send(this.name+"悄悄对你说:"+datas);
21                     }
22                 }
23                 
24             }else {
25                 for(Channel other:all) {
26                     if(other == this) {  //不再自己发给自己了
27                         continue;
28                     }            
29                     if(!isSys) {
30                         other.send(this.name+"对大家说:"+msg); //发送给自己
31                     }else {
32                         other.send(msg);
33                     }
34                 }
35             }
36             
37         }

服务端:

  1 package 在线聊天室终极版;
  2 
  3 import java.io.DataInputStream;
  4 import java.io.DataOutputStream;
  5 import java.io.IOException;
  6 import java.net.ServerSocket;
  7 import java.net.Socket;
  8 import java.util.concurrent.CopyOnWriteArrayList;
  9 
 10 /**
 11  * 可以实现简单的群聊了。
 12  * @author liuzeyu12a
 13  *
 14  */
 15 public class Chat2 {
 16     //用于存储客户端的容器,涉及到多线程的并发操作,
 17     //使用CopyOnWriteArrayList保证线程的安全
 18     private static CopyOnWriteArrayList<Channel> all = 
 19             new CopyOnWriteArrayList<Channel>();
 20     public static void main(String[] args) throws Exception {
 21         System.out.println("-----Server-----");
 22         //建立服务器端地址,并绑定本地端口
 23         ServerSocket server = new ServerSocket(8989);
 24         
 25         //这边加上死循环是为了接受多个客户的请求
 26         while(true) {
 27                 //监听
 28                 Socket    client = server.accept();
 29                 Channel c = new Channel(client);
 30                 all.add(c);   //添加一个客户端
 31                 System.out.println("一个客户端建立了连接");
 32                 new Thread(c).start();
 33         }        
 34     }
 35     //静态内部类,封装处理客户端的数据
 36     static class Channel implements Runnable{
 37         private DataInputStream dis;
 38         private DataOutputStream dos;
 39         private Socket client;
 40         private boolean isRunning;
 41         private String name;
 42         //构造器
 43         public Channel(Socket client) {
 44 
 45             this.client = client;
 46             this.isRunning = true;
 47             try {
 48                 dis = new DataInputStream(
 49                         client.getInputStream());
 50                 dos = new DataOutputStream(
 51                         client.getOutputStream());
 52                 this.name = receive(); //接收客户端的名称
 53                 this.send("欢迎光临聊天室..");
 54                 this.sendOther(this.name+"来到了聊天室...", true);
 55             } catch (IOException e) {
 56                 release();
 57             }
 58         }
 59         @Override
 60         public void run() {
 61             while(isRunning) {
 62                 String msg = receive();
 63                 if(!msg.equals("")) {
 64                     //send(msg);
 65                     sendOther(msg,false);
 66                 }
 67 
 68             }
 69         }
 70         
 71         //发送数据
 72         public void send(String msg) {
 73             try {
 74                 dos.writeUTF(msg);
 75                 dos.flush();
 76             } catch (IOException e) {
 77                 System.out.println("发送数据失败");
 78                 release();
 79             }
 80         }
 81         /**
 82          * 获取自己的消息,然后发送给其它人
 83          * boolean isSys表示是否为系统消息
 84          * 添加私聊的功能:可以向某一特地呢的用户发送数据
 85          * 约定格式:@xxx:数据
 86          * @return
 87          */
 88         public void sendOther(String msg,boolean isSys) {
 89             //判断数据是否以@开头
 90             boolean isPrivate = msg.startsWith("@");
 91             if(isPrivate) {
 92                 //寻找:
 93                 int index = msg.indexOf(":");
 94                 //截取名字
 95                 String targetName = msg.substring(1, index);
 96                 //截取消息内容
 97                 String datas = msg.substring(index+1);
 98                 for(Channel other :all) {
 99                     if(other.name.equals(targetName)) {
100                         other.send(this.name+"悄悄对你说:"+datas);
101                     }
102                 }
103                 
104             }else {
105                 for(Channel other:all) {
106                     if(other == this) {  //不再自己发给自己了
107                         continue;
108                     }            
109                     if(!isSys) {
110                         other.send(this.name+"对大家说:"+msg); //发送给自己
111                     }else {
112                         other.send(msg);
113                     }
114                 }
115             }
116             
117         }
118         
119         //接受数据
120         public String receive() {
121             try {
122                 String msg = "";
123                 msg = dis.readUTF();
124                 return msg;
125             } catch (IOException e) {
126                 isRunning = false;
127                 System.out.println("接受数据失败");
128                 release();
129             }
130             return "";
131         }
132         
133         //释放资源
134         public void release() {
135             this.isRunning = false;
136             ChatUtils.close(client,dos,dis);
137             all.remove(this);
138             this.sendOther(this.name+"离开了聊天室...", true);
139         }
140     }
141 }
View Code

截图:

小结:

1、模拟多人聊天室,代码比较多,重点在于多线程,TCP数据的传递,面向对象的封装。

2、利用面向对象的思想可以对代码进行封装,简化代码,提高代码的可维护性。

3、多个客户端可以在服务端使用容器进行装载。

3、在使用容器中,多线程如果涉及到多个同步的操作,如聊天室中可能在聊天中忽然有人退出,有人加入,

容易造成数据的错误发送和接收,可以使用JUC并发容器CopyOnWriteArrayList(再操作容器前copy一个副本存起来,一旦数据有错,就启用副本数据

来保证数据的安全性)。

原文地址:https://www.cnblogs.com/liuzeyu12a/p/10406715.html