JavaSE聊天室

今天学习了一下简单聊天程序(类似QQ那种)的编写过程,从最初的0.1版本到最后的1.3版本,功能不断地增强,下面对一天的学习内容进行梳理。

版本0.1

我们的需求是显示一个窗体,其他什么也不用做,其他功能逐步添加,我们这里用的就是AWT中的Frame;

具体代码实现:

 1 import java.awt.*;
 2 
 3 public class ChatClient extends Frame{
 4 
 5     /**
 6      * @param args
 7      */
 8     public static void main(String[] args) {
 9         new ChatClient().launchFrame();
10     }
11 
12     /**
13      * Version 0.1
14      */
15     public void launchFrame(){
16         //设置Frame位置
17         setLocation(400, 300);
18         //设置Frame大小
19         this.setSize(300, 300);
20         //窗口可见
21         setVisible(true);
22     }
23 }

版本0.2

我们的需求是在我们的Frame中添加两个部件TextField(用于输入)和TextArea(用于显示获取的内容),此时我们可以在输入框输入内容,但是显示框无法显示我们输入的内容.

 1 import java.awt.*;
 2 
 3 public class ChatClient extends Frame {
 4 
 5     TextField tfTxt = new TextField();
 6     TextArea taContent = new TextArea();
 7 
 8     public static void main(String[] args) {
 9         new ChatClient().launchFrame();
10     }
11 
12     /**
13      * Version 0.2
14      */
15     public void launchFrame() {
16         // 设置Frame位置
17         setLocation(400, 300);
18         // 设置Frame大小
19         this.setSize(300, 300);
20         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
21         add(tfTxt, BorderLayout.SOUTH);
22         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
23         add(taContent, BorderLayout.NORTH);
24         // 调整布局,处理多余空白框
25         pack();
26         // 窗口可见
27         setVisible(true);
28     }
29 }

版本0.3

因为我们的版本0.2中,显示的窗体无法关闭(除非把程序停掉,我们这里不考虑这种做法),我们添加窗口监听,使我们可以通过点击窗体的(X)符号进行关闭;

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 
 4 public class ChatClient extends Frame {
 5 
 6     TextField tfTxt = new TextField();
 7     TextArea taContent = new TextArea();
 8 
 9     public static void main(String[] args) {
10         new ChatClient().launchFrame();
11     }
12 
13     /**
14      * Version 0.3:添加窗口关闭的功能
15      */
16     public void launchFrame() {
17         // 设置Frame位置
18         setLocation(400, 300);
19         // 设置Frame大小
20         this.setSize(300, 300);
21         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
22         add(tfTxt, BorderLayout.SOUTH);
23         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
24         add(taContent, BorderLayout.NORTH);
25         // 调整布局,处理多余空白框
26         pack();
27         //添加窗口监听
28         this.addWindowListener(new WindowAdapter() {
29 
30             @Override
31             public void windowClosing(WindowEvent e) {
32                 System.exit(0);
33             }        
34         });
35         
36         // 窗口可见
37         setVisible(true);
38     }
39 }

版本0.4

我们实现的功能是将输入框输入的内容显示到显示框TextArea中,

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 
 4 public class ChatClient extends Frame {
 5 
 6     TextField tfTxt = new TextField();
 7     TextArea taContent = new TextArea();
 8 
 9     public static void main(String[] args) {
10         new ChatClient().launchFrame();
11     }
12 
13     /**
14      * Version 0.4
15      */
16     public void launchFrame() {
17         // 设置Frame位置
18         setLocation(400, 300);
19         // 设置Frame大小
20         this.setSize(300, 300);
21         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
22         add(tfTxt, BorderLayout.SOUTH);
23         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
24         add(taContent, BorderLayout.NORTH);
25         // 调整布局,处理多余空白框
26         pack();
27         // 添加窗口监听
28         this.addWindowListener(new WindowAdapter() {
29 
30             @Override
31             public void windowClosing(WindowEvent e) {
32                 System.exit(0);
33             }
34         });
35 
36         // 将监听器类TFListener添加到输入框TextField中
37         tfTxt.addActionListener(new TFListener());
38         // 窗口可见
39         setVisible(true);
40     }
41 
42     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
43     private class TFListener implements ActionListener {
44 
45         @Override
46         public void actionPerformed(ActionEvent e) {
47             //获得输入框的内容,并去除两端的空格
48             String s = tfTxt.getText().trim();
49             //将获取的输入内容放置到TextArea中
50             taContent.setText(s);
51             //每次输入结束,将输入框置空
52             tfTxt.setText("");
53         }
54     }
55 }

版本0.5

相比版本0.4,我们添加了Server端,使得Server作为中转站,把我们输入的内容显示在其他连接到Server端的客户端的的显示框中;

ChatClient

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 
 4 public class ChatClient extends Frame {
 5 
 6     TextField tfTxt = new TextField();
 7     TextArea taContent = new TextArea();
 8 
 9     public static void main(String[] args) {
10         new ChatClient().launchFrame();
11     }
12 
13     /**
14      * Version 0.5:处理输入框
15      */
16     public void launchFrame() {
17         // 设置Frame位置
18         setLocation(400, 300);
19         // 设置Frame大小
20         this.setSize(300, 300);
21         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
22         add(tfTxt, BorderLayout.SOUTH);
23         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
24         add(taContent, BorderLayout.NORTH);
25         // 调整布局,处理多余空白框
26         pack();
27         // 添加窗口监听
28         this.addWindowListener(new WindowAdapter() {
29 
30             @Override
31             public void windowClosing(WindowEvent e) {
32                 System.exit(0);
33             }
34         });
35 
36         // 将监听器类TFListener添加到输入框TextField中
37         tfTxt.addActionListener(new TFListener());
38         // 窗口可见
39         setVisible(true);
40     }
41 
42     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
43     private class TFListener implements ActionListener {
44 
45         @Override
46         public void actionPerformed(ActionEvent e) {
47             // 获得输入框的内容,并去除两端的空格
48             String s = tfTxt.getText().trim();
49             // 将获取的输入内容放置到TextArea中
50             taContent.setText(s);
51             // 每次输入结束,将输入框置空
52             tfTxt.setText("");
53         }
54     }
55 }
View Code

Server

 1 import java.io.IOException;
 2 import java.net.*;
 3 
 4 //server端
 5 public class ChatServer {
 6 
 7     /**
 8      * @param args
 9      */
10     public static void main(String[] args) {
11         try {
12             ServerSocket ss = new ServerSocket(8888);
13             while(true){
14                 Socket s=ss.accept();
15 System.out.println("a clint connected");
16             }
17         } catch (IOException e) {
18             e.printStackTrace();
19         }
20     }
21 }
View Code

此时,我们可以运行多个客户端,但是每个客户端输入的内容在其他客户端都无法显示,他们都是孤立的,还没有与Server端建立连接;

版本0.6

ChatClient

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 import java.io.*;
 4 import java.net.*;
 5 
 6 public class ChatClient extends Frame {
 7     Socket s = null;
 8 
 9     TextField tfTxt = new TextField();
10 
11     TextArea taContent = new TextArea();
12 
13     public static void main(String[] args) {
14         new ChatClient().launchFrame(); 
15     }
16 
17     /**
18      * Version 0.6
19      */
20     public void launchFrame() {
21         setLocation(400, 300);
22         this.setSize(300, 300);
23         add(tfTxt, BorderLayout.SOUTH);
24         add(taContent, BorderLayout.NORTH);
25         pack();
26         this.addWindowListener(new WindowAdapter() {
27 
28             @Override
29             public void windowClosing(WindowEvent arg0) {
30                 System.exit(0);
31             }
32             
33         });
34         tfTxt.addActionListener(new TFListener());
35         setVisible(true);
36         connect();
37     }
38     
39     public void connect() {
40         try {
41             s = new Socket("127.0.0.1", 8888);
42 System.out.println("connected!");
43         } catch (UnknownHostException e) {
44             e.printStackTrace();
45         } catch (IOException e) {
46             e.printStackTrace();
47         }
48         
49     }
50     
51     private class TFListener implements ActionListener {
52 
53         public void actionPerformed(ActionEvent e) {
54             String str = tfTxt.getText().trim();
55             taContent.setText(str);
56             tfTxt.setText("");
57             
58             try {
59                 DataOutputStream dos = new DataOutputStream(s.getOutputStream());
60                 dos.writeUTF(str);
61                 dos.flush();
62                 dos.close();
63             } catch (IOException e1) {
64                 e1.printStackTrace();
65             }
66             
67         }
68         
69     }
70 
71 }
View Code

Server

 1 import java.io.*;
 2 import java.net.*;
 3 
 4 public class ChatServer {
 5 
 6     public static void main(String[] args) {
 7         try {
 8             // 服务器端需要创建监听端口的 ServerSocket, ServerSocket 负责接收客户连接请求
 9             ServerSocket ss = new ServerSocket(8888);
10             while (true) {
11                 //服务器端应对客户端的每一个连接建立一个新的Socket(重要)
12                 Socket s = ss.accept();
13                 // 当有客户端连接上时,打印连接信息
14                 System.out.println("a client connected!");
15 
16                 //获取Socket s的输入流
17                 DataInputStream dis = new DataInputStream(s.getInputStream());
18                 //获取输入流中的信息
19                 String str = dis.readUTF();
20                 System.out.println(str);
21                 //关闭流操作
22                 dis.close();
23             }
24         } catch (IOException e) {
25             e.printStackTrace();
26         }
27     }
28 
29 }
View Code

我们此时客户端输入的内容可以显示在Server端的控制台上,每个连接上的Client的输入只有第一次输入可以显示在控制台,每个客户端不可以接收其他客户端的信息。并且当我们某些关闭处于连接状态的客户端的时候Server端会报Socket异常;

版本0.7

ChatClient

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 import java.io.DataOutputStream;
 4 import java.io.IOException;
 5 import java.net.*;
 6 
 7 public class ChatClient extends Frame {
 8 
 9     // 暴露Socket
10     Socket s = null;
11     TextField tfTxt = new TextField();
12     TextArea taContent = new TextArea();
13 
14     public static void main(String[] args) {
15         new ChatClient().launchFrame();
16     }
17 
18     /**
19      * Version 0.7
20      */
21     public void launchFrame() {
22         // 设置Frame位置
23         setLocation(400, 300);
24         // 设置Frame大小
25         this.setSize(300, 300);
26         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
27         add(tfTxt, BorderLayout.SOUTH);
28         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
29         add(taContent, BorderLayout.NORTH);
30         // 调整布局,处理多余空白框
31         pack();
32         // 添加窗口监听
33         this.addWindowListener(new WindowAdapter() {
34 
35             @Override
36             public void windowClosing(WindowEvent e) {
37                 System.exit(0);
38             }
39         });
40 
41         // 将监听器类TFListener添加到输入框TextField中
42         tfTxt.addActionListener(new TFListener());
43         // 窗口可见
44         setVisible(true);
45         connect();
46     }
47 
48     // 建立連接的方法
49     public void connect() {
50         try {
51             s = new Socket("127.0.0.1", 8888);
52             System.out.println("connected!");
53         } catch (UnknownHostException e) {
54             e.printStackTrace();
55         } catch (IOException e) {
56             e.printStackTrace();
57         }
58     }
59 
60     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
61     private class TFListener implements ActionListener {
62 
63         @Override
64         public void actionPerformed(ActionEvent e) {
65             // 获得输入框的内容,并去除两端的空格
66             String string = tfTxt.getText().trim();
67             // 将获取的输入内容放置到TextArea中
68             taContent.setText(string);
69             // 每次输入结束,将输入框置空
70             tfTxt.setText("");
71             try {
72                 System.out.println(s);
73                 DataOutputStream dos = new DataOutputStream(s.getOutputStream());
74                 dos.writeUTF(string);
75                 dos.flush();
76                 dos.close();
77             } catch (IOException e1) {
78                 e1.printStackTrace();
79             }
80 
81         }
82     }
83 }
View Code

Server

 1 import java.io.DataInputStream;
 2 import java.io.IOException;
 3 import java.net.*;
 4 
 5 //server端
 6 public class ChatServer {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) {
12         try {
13             ServerSocket ss = new ServerSocket(8888);
14             while(true){
15                 Socket s=ss.accept();
16 System.out.println("a clint connected");
17             DataInputStream dis=new DataInputStream(s.getInputStream());
18             String str=dis.readUTF();
19             System.out.println(str);
20             dis.close();
21             }
22         } catch (IOException e) {
23             e.printStackTrace();
24         }
25     }
26 
27 }
View Code

相较于版本0.6,做了一些调试工作,每次打印System.out.println(s);观察出错信息,关闭客户端时,仍然会出现Socket is closed异常

版本0.8

ChatClient

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 import java.io.DataOutputStream;
 4 import java.io.IOException;
 5 import java.net.*;
 6 public class ChatClient extends Frame {
 7 
 8     //暴露Socket
 9     Socket s=null;
10     DataOutputStream dos=null;
11     TextField tfTxt = new TextField();
12     TextArea taContent = new TextArea();
13 
14     public static void main(String[] args) {
15         new ChatClient().launchFrame();
16     }
17 
18     /**
19      * Version 0.8
20      */
21     public void launchFrame() {
22         // 设置Frame位置
23         setLocation(400, 300);
24         // 设置Frame大小
25         this.setSize(300, 300);
26         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
27         add(tfTxt, BorderLayout.SOUTH);
28         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
29         add(taContent, BorderLayout.NORTH);
30         // 调整布局,处理多余空白框
31         pack();
32         // 添加窗口监听
33         this.addWindowListener(new WindowAdapter() {
34 
35             @Override
36             public void windowClosing(WindowEvent e) {
37                 //窗口关闭的时候释放连接资源
38                 disconnect();
39                 System.exit(0);
40             }
41         });
42 
43         // 将监听器类TFListener添加到输入框TextField中
44         tfTxt.addActionListener(new TFListener());
45         // 窗口可见
46         setVisible(true);
47         connect();
48     }
49     //建立連接的方法
50     public void connect(){
51         try {
52             s=new Socket("127.0.0.1",8888);
53             dos=new DataOutputStream(s.getOutputStream());
54 System.out.println("connected!");            
55         } catch (UnknownHostException e) {
56             e.printStackTrace();
57         } catch (IOException e) {
58             e.printStackTrace();
59         }
60     }
61     public void disconnect(){
62         try {
63             dos.close();
64             s.close();
65         } catch (Exception e) {
66             e.printStackTrace();
67         }
68     }
69 
70     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
71     private class TFListener implements ActionListener {
72 
73         @Override
74         public void actionPerformed(ActionEvent e) {
75             // 获得输入框的内容,并去除两端的空格
76             String string = tfTxt.getText().trim();
77             // 将获取的输入内容放置到TextArea中
78             taContent.setText(string);
79             // 每次输入结束,将输入框置空
80             tfTxt.setText("");
81             try {
82 System.out.println(s);        
83 //不同每次都获取一次连接
84 //                DataOutputStream dos=new DataOutputStream(s.getOutputStream());
85                 dos.writeUTF(string);
86                 dos.flush();
87 //                dos.close();
88             } catch (IOException e1) {
89                 e1.printStackTrace();
90             }
91             
92         }
93     }
94 }
View Code

Server

 1 import java.io.DataInputStream;
 2 import java.io.IOException;
 3 import java.net.*;
 4 
 5 //server端
 6 public class ChatServer {
 7 
 8     public static void main(String[] args) {
 9         //服务器端是否已经启动
10         boolean started=false;
11         try {
12             ServerSocket ss = new ServerSocket(8888);
13             //服务器端启动以后,started=true
14             started=true;
15             //服务器端启动以后才能不断接收客户端的连接
16             while(started){
17                 //定义boolean类型的变量,客户端时候建立连接
18                 boolean bConnected;
19                 Socket s=ss.accept();
20 System.out.println("a clint connected");
21             //客户端时候建立连接以后,bConnected=true;
22             bConnected=true; 
23             DataInputStream dis=new DataInputStream(s.getInputStream());
24             //客户端建立连接以后,不断的接收写来的数据
25             while(bConnected){        
26                 String str=dis.readUTF();
27                 System.out.println(str);
28             }
29             //没有连接上,关闭dis,释放资源
30             dis.close();
31             }
32         } catch (IOException e) {
33             e.printStackTrace();
34         }
35     }
36 
37 }
View Code

我们在这一版本中添加了连接标志,运行测试发现Server端可以接收多个客户端的连接,但是只能将第一个连接的客户端的输入显示在控制台,其他客户端依然无法获取到其他客户端的内容,仍会出现异常

版本0.9

ChatClient

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 import java.io.DataOutputStream;
 4 import java.io.IOException;
 5 import java.net.*;
 6 public class ChatClient extends Frame {
 7 
 8     //暴露Socket
 9     Socket s=null;
10     DataOutputStream dos=null;
11     TextField tfTxt = new TextField();
12     TextArea taContent = new TextArea();
13 
14     public static void main(String[] args) {
15         new ChatClient().launchFrame();
16     }
17 
18     /**
19      * Version 0.9
20      */
21     public void launchFrame() {
22         // 设置Frame位置
23         setLocation(400, 300);
24         // 设置Frame大小
25         this.setSize(300, 300);
26         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
27         add(tfTxt, BorderLayout.SOUTH);
28         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
29         add(taContent, BorderLayout.NORTH);
30         // 调整布局,处理多余空白框
31         pack();
32         // 添加窗口监听
33         this.addWindowListener(new WindowAdapter() {
34 
35             @Override
36             public void windowClosing(WindowEvent e) {
37                 //窗口关闭的时候释放连接资源
38                 disconnect();
39                 System.exit(0);
40             }
41         });
42 
43         // 将监听器类TFListener添加到输入框TextField中
44         tfTxt.addActionListener(new TFListener());
45         // 窗口可见
46         setVisible(true);
47         connect();
48     }
49     //建立連接的方法
50     public void connect(){
51         try {
52             s=new Socket("127.0.0.1",8888);
53             dos=new DataOutputStream(s.getOutputStream());
54 System.out.println("connected!");            
55         } catch (UnknownHostException e) {
56             e.printStackTrace();
57         } catch (IOException e) {
58             e.printStackTrace();
59         }
60     }
61     public void disconnect(){
62         try {
63             dos.close();
64             s.close();
65         } catch (Exception e) {
66             e.printStackTrace();
67         }
68     }
69 
70     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
71     private class TFListener implements ActionListener {
72 
73         @Override
74         public void actionPerformed(ActionEvent e) {
75             // 获得输入框的内容,并去除两端的空格
76             String string = tfTxt.getText().trim();
77             // 将获取的输入内容放置到TextArea中
78             taContent.setText(string);
79             // 每次输入结束,将输入框置空
80             tfTxt.setText("");
81             try {
82 System.out.println(s);        
83 //不同每次都获取一次连接
84 //                DataOutputStream dos=new DataOutputStream(s.getOutputStream());
85                 dos.writeUTF(string);
86                 dos.flush();
87 //                dos.close();
88             } catch (IOException e1) {
89                 e1.printStackTrace();
90             }
91             
92         }
93     }
94 }
View Code

Server

 1 import java.io.DataInputStream;
 2 import java.io.EOFException;
 3 import java.io.IOException;
 4 import java.net.*;
 5 
 6 //server端
 7 public class ChatServer {
 8 
 9     public static void main(String[] args) {
10         //服务器端是否已经启动
11         boolean started=false;
12         ServerSocket ss = null;
13         Socket s=null;
14         DataInputStream dis=null;
15         try {
16             //可能会产生端口绑定异常
17             ss = new ServerSocket(8888);
18         }catch(BindException e){
19             System.out.println("端口使用中");
20             System.out.println("关闭相关程序,并重新运行");
21             System.exit(0);
22         }catch(IOException e){
23             e.printStackTrace();
24         }
25         try {
26             //服务器端启动以后,started=true
27             started=true;
28             //服务器端启动以后才能不断接收客户端的连接
29             while(started){
30                 //定义boolean类型的变量,客户端时候建立连接
31                 boolean bConnected;
32                 s=ss.accept();
33 System.out.println("a clint connected");
34             //客户端时候建立连接以后,bConnected=true;
35             bConnected=true; 
36             dis=new DataInputStream(s.getInputStream());
37             //客户端建立连接以后,不断的接收写来的数据
38             while(bConnected){
39                 //readUTF是阻塞式的
40                 String str=dis.readUTF();
41                 System.out.println(str);
42             }
43             //没有连接上,关闭dis,释放资源
44 //            dis.close();
45             }
46             //如果是因为客户端的关闭而导致的连接中断,则做这样的处理
47         }catch(EOFException e){
48             System.out.println("Client closed");
49         //其他异常,直接打印异常信息
50         }catch (IOException e) {
51             e.printStackTrace();
52         }finally{
53             try {
54                 if(dis!=null){    
55                     dis.close();
56                 }
57                 if(s!=null){    
58                     s.close();
59                 }
60             } catch (IOException e1) {
61                 e1.printStackTrace();
62             }
63         }
64     }
65 
66 }
View Code

之前的版本,当我们关闭客户端的时候都会发生异常,在这个版本中我们做了相应的处理,去捕获这个异常,并将连接资源的关闭工作进行细化;这里我们在客户端进行了处理:将DataOutputStream提到了全局变量的位置。不用每次都去获取一次Socket的输出流;

版本1.0

ChatClient

 1 import java.awt.*;
 2 import java.awt.event.*;
 3 import java.io.DataOutputStream;
 4 import java.io.IOException;
 5 import java.net.*;
 6 
 7 public class ChatClient extends Frame {
 8 
 9     // 暴露Socket
10     Socket s = null;
11     DataOutputStream dos = null;
12     TextField tfTxt = new TextField();
13     TextArea taContent = new TextArea();
14 
15     public static void main(String[] args) {
16         new ChatClient().launchFrame();
17     }
18 
19     /**
20      * Version 1.0
21      */
22     public void launchFrame() {
23         // 设置Frame位置
24         setLocation(400, 300);
25         // 设置Frame大小
26         this.setSize(300, 300);
27         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
28         add(tfTxt, BorderLayout.SOUTH);
29         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
30         add(taContent, BorderLayout.NORTH);
31         // 调整布局,处理多余空白框
32         pack();
33         // 添加窗口监听
34         this.addWindowListener(new WindowAdapter() {
35 
36             @Override
37             public void windowClosing(WindowEvent e) {
38                 // 窗口关闭的时候释放连接资源
39                 disconnect();
40                 System.exit(0);
41             }
42         });
43 
44         // 将监听器类TFListener添加到输入框TextField中
45         tfTxt.addActionListener(new TFListener());
46         // 窗口可见
47         setVisible(true);
48         connect();
49     }
50 
51     // 建立連接的方法
52     public void connect() {
53         try {
54             s = new Socket("127.0.0.1", 8888);
55             dos = new DataOutputStream(s.getOutputStream());
56             System.out.println("connected!");
57         } catch (UnknownHostException e) {
58             e.printStackTrace();
59         } catch (IOException e) {
60             e.printStackTrace();
61         }
62     }
63 
64     public void disconnect() {
65         try {
66             dos.close();
67             s.close();
68         } catch (Exception e) {
69             e.printStackTrace();
70         }
71     }
72 
73     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
74     private class TFListener implements ActionListener {
75 
76         @Override
77         public void actionPerformed(ActionEvent e) {
78             // 获得输入框的内容,并去除两端的空格
79             String string = tfTxt.getText().trim();
80             // 将获取的输入内容放置到TextArea中
81             taContent.setText(string);
82             // 每次输入结束,将输入框置空
83             tfTxt.setText("");
84             try {
85                 System.out.println(s);
86                 // 不同每次都获取一次连接
87                 // DataOutputStream dos=new
88                 // DataOutputStream(s.getOutputStream());
89                 dos.writeUTF(string);
90                 dos.flush();
91                 // dos.close();
92             } catch (IOException e1) {
93                 e1.printStackTrace();
94             }
95 
96         }
97     }
98 }
View Code

Server

 1 import java.io.DataInputStream;
 2 import java.io.EOFException;
 3 import java.io.IOException;
 4 import java.net.*;
 5 import java.security.Principal;
 6 
 7 //server端
 8 public class ChatServer {
 9 
10     boolean started = false;
11     ServerSocket ss = null;
12 
13     public static void main(String[] args) {
14         new ChatServer().start();
15     }
16 
17     public void start() {
18 
19         try {
20             // 可能会产生端口绑定异常
21             ss = new ServerSocket(8888);
22             // 服务器端启动以后,started=true
23             started = true;
24         } catch (BindException e) {
25             System.out.println("端口使用中");
26             System.out.println("关闭相关程序,并重新运行");
27             System.exit(0);
28         } catch (IOException e) {
29             e.printStackTrace();
30         }
31         try {
32             // 服务器端启动以后才能不断接收客户端的连接
33             while (started) {
34                 Socket s = ss.accept();
35                 // 不能在静态方法里new一个动态的类
36                 Client c = new Client(s);
37                 System.out.println("a clint connected");
38                 new Thread(c).start();
39             }
40             // 如果是因为客户端的关闭而导致的连接中断,则做这样的处理
41         } catch (IOException e) {
42             e.printStackTrace();
43 
44         } finally {
45             try {
46                 ss.close();
47             } catch (IOException e) {
48                 e.printStackTrace();
49             }
50         }
51     }
52 
53 }
54 
55 class Client implements Runnable {
56     private Socket s;
57     private DataInputStream dis = null;
58     private boolean bConnected = false;
59 
60     public Client(Socket s) {
61         this.s = s;
62         try {
63             dis = new DataInputStream(s.getInputStream());
64             bConnected = true;
65         }catch (IOException e) {
66             e.printStackTrace();
67 
68         }
69     }
70 
71     @Override
72     public void run() {
73         try {
74             while (bConnected) {
75                 String str = dis.readUTF();
76                 System.out.println(str);
77             }
78         } catch (EOFException e) {
79             System.out.println("Client closed");
80         } catch (IOException e) {
81             e.printStackTrace();
82         } finally {
83             try {
84                 if (dis != null) {
85                     dis.close();
86                 }
87                 if (s != null) {
88                     s.close();
89                 }
90             } catch (Exception e2) {
91                 e2.printStackTrace();
92             }
93         }
94 
95     }
96 
97 }
View Code

版本1.0可以处理多个客户端同时连接并显示其输入内容的效果;将每个客户端封装到一个独立的线程中;

版本1.1

ChatClient

  1 import java.awt.*;
  2 import java.awt.event.*;
  3 import java.io.DataOutputStream;
  4 import java.io.IOException;
  5 import java.net.*;
  6 import java.util.*;
  7 import java.util.List;
  8 //转发给
  9 public class ChatClient extends Frame {
 10 
 11     // 暴露Socket
 12     Socket s = null;
 13     DataOutputStream dos = null;
 14     TextField tfTxt = new TextField();
 15     TextArea taContent = new TextArea();
 16 
 17     public static void main(String[] args) {
 18         new ChatClient().launchFrame();
 19     }
 20 
 21     /**
 22      * Version 1.1:转发给其他客户端,保存socket连接,用集合
 23      */
 24     public void launchFrame() {
 25         // 设置Frame位置
 26         setLocation(400, 300);
 27         // 设置Frame大小
 28         this.setSize(300, 300);
 29         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
 30         add(tfTxt, BorderLayout.SOUTH);
 31         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
 32         add(taContent, BorderLayout.NORTH);
 33         // 调整布局,处理多余空白框
 34         pack();
 35         // 添加窗口监听
 36         this.addWindowListener(new WindowAdapter() {
 37 
 38             @Override
 39             public void windowClosing(WindowEvent e) {
 40                 // 窗口关闭的时候释放连接资源
 41                 disconnect();
 42                 System.exit(0);
 43             }
 44         });
 45 
 46         // 将监听器类TFListener添加到输入框TextField中
 47         tfTxt.addActionListener(new TFListener());
 48         // 窗口可见
 49         setVisible(true);
 50         connect();
 51     }
 52 
 53     // 建立連接的方法
 54     public void connect() {
 55         try {
 56             s = new Socket("127.0.0.1", 8888);
 57             dos = new DataOutputStream(s.getOutputStream());
 58             System.out.println("connected!");
 59         } catch (UnknownHostException e) {
 60             e.printStackTrace();
 61         } catch (IOException e) {
 62             e.printStackTrace();
 63         }
 64     }
 65 
 66     public void disconnect() {
 67         try {
 68             dos.close();
 69             s.close();
 70         } catch (Exception e) {
 71             e.printStackTrace();
 72         }
 73     }
 74 
 75     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
 76     private class TFListener implements ActionListener {
 77 
 78         @Override
 79         public void actionPerformed(ActionEvent e) {
 80             // 获得输入框的内容,并去除两端的空格
 81             String string = tfTxt.getText().trim();
 82             // 将获取的输入内容放置到TextArea中
 83             taContent.setText(string);
 84             // 每次输入结束,将输入框置空
 85             tfTxt.setText("");
 86             try {
 87                 System.out.println(s);
 88                 // 不同每次都获取一次连接
 89                 // DataOutputStream dos=new
 90                 // DataOutputStream(s.getOutputStream());
 91                 dos.writeUTF(string);
 92                 dos.flush();
 93                 // dos.close();
 94             } catch (IOException e1) {
 95                 e1.printStackTrace();
 96             }
 97 
 98         }
 99     }
100 }
View Code

Server

  1 import java.io.DataInputStream;
  2 import java.io.DataOutputStream;
  3 import java.io.EOFException;
  4 import java.io.IOException;
  5 import java.net.*;
  6 import java.security.Principal;
  7 import java.util.ArrayList;
  8 import java.util.List;
  9 
 10 //server端
 11 public class ChatServer {
 12 
 13     boolean started = false;
 14     ServerSocket ss = null;
 15     List<Client> clients = new ArrayList<Client>();
 16 
 17     public static void main(String[] args) {
 18         new ChatServer().start();
 19     }
 20 
 21     public void start() {
 22 
 23         try {
 24             // 可能会产生端口绑定异常
 25             ss = new ServerSocket(8888);
 26             // 服务器端启动以后,started=true
 27             started = true;
 28         } catch (BindException e) {
 29             System.out.println("端口使用中");
 30             System.out.println("关闭相关程序,并重新运行");
 31             System.exit(0);
 32         } catch (IOException e) {
 33             e.printStackTrace();
 34         }
 35         try {
 36             // 服务器端启动以后才能不断接收客户端的连接
 37             while (started) {
 38                 Socket s = ss.accept();
 39                 // 不能在静态方法里new一个动态的类
 40                 Client c = new Client(s);
 41                 System.out.println("a clint connected");
 42                 new Thread(c).start();
 43                 clients.add(c);
 44             }
 45             // 如果是因为客户端的关闭而导致的连接中断,则做这样的处理
 46         } catch (IOException e) {
 47             e.printStackTrace();
 48 
 49         } finally {
 50             try {
 51                 ss.close();
 52             } catch (IOException e) {
 53                 e.printStackTrace();
 54             }
 55         }
 56     }
 57 
 58 class Client implements Runnable {
 59     private Socket s;
 60     private DataInputStream dis = null;
 61     private DataOutputStream dos = null;
 62     private boolean bConnected = false;
 63 
 64     public Client(Socket s) {
 65         this.s = s;
 66         try {
 67             dis = new DataInputStream(s.getInputStream());
 68             dos = new DataOutputStream(s.getOutputStream());
 69             bConnected = true;
 70         } catch (IOException e) {
 71             e.printStackTrace();
 72 
 73         }
 74     }
 75 
 76     // 发送的方法
 77     public void send(String str) {
 78         try {
 79             dos.writeUTF(str);
 80         } catch (IOException e) {
 81             e.printStackTrace();
 82         }
 83     }
 84 
 85     @Override
 86     public void run() {
 87         try {
 88             while (bConnected) {
 89                 String str = dis.readUTF();
 90                 System.out.println(str);
 91                 for(int i=0;i<clients.size();i++){
 92                     Client c=clients.get(i);
 93                     c.send(str);
 94                 }
 95             }
 96         } catch (EOFException e) {
 97             System.out.println("Client closed");
 98         } catch (IOException e) {
 99             e.printStackTrace();
100         } finally {
101             try {
102                 if (dis != null) {
103                     dis.close();
104                 }
105                 if (s != null) {
106                     s.close();
107                 }
108                 if(dos!=null){
109                     dos.close();
110                 }
111             } catch (Exception e2) {
112                 e2.printStackTrace();
113             }
114         }
115 
116     }
117 }
118 }
View Code

将每个连接上的客户端Client添加到我们的List集合中,方便我们将我们的输入内容发送到List集合保存的Client;

版本1.2

ChatClient

  1 import java.awt.BorderLayout;
  2 import java.awt.Frame;
  3 import java.awt.TextArea;
  4 import java.awt.TextField;
  5 import java.awt.event.ActionEvent;
  6 import java.awt.event.ActionListener;
  7 import java.awt.event.WindowAdapter;
  8 import java.awt.event.WindowEvent;
  9 import java.io.DataInputStream;
 10 import java.io.DataOutputStream;
 11 import java.io.IOException;
 12 import java.net.Socket;
 13 import java.net.SocketException;
 14 import java.net.UnknownHostException;
 15 
 16 //转发给
 17 public class ChatClient extends Frame {
 18 
 19     // 暴露Socket
 20     Socket s = null;
 21     DataOutputStream dos = null;
 22     DataInputStream dis = null;
 23     private boolean bConnected = false;
 24     TextField tfTxt = new TextField();
 25     TextArea taContent = new TextArea();
 26 
 27     Thread tRecv=new Thread(new RecvThread());
 28     public static void main(String[] args) {
 29         new ChatClient().launchFrame();
 30     }
 31 
 32     /**
 33      * Version 1.2
 34      */
 35     public void launchFrame() {
 36         // 设置Frame位置
 37         setLocation(400, 300);
 38         // 设置Frame大小
 39         this.setSize(300, 300);
 40         // 将输入框TextField加到Frame中,并放置到Frame布局的上面
 41         add(tfTxt, BorderLayout.SOUTH);
 42         // 将显示框TextArea加到Frame中,并放置到Frame布局的下面
 43         add(taContent, BorderLayout.NORTH);
 44         // 调整布局,处理多余空白框
 45         pack();
 46         // 添加窗口监听
 47         this.addWindowListener(new WindowAdapter() {
 48 
 49             @Override
 50             public void windowClosing(WindowEvent e) {
 51                 // 窗口关闭的时候释放连接资源
 52                 disconnect();
 53                 System.exit(0);
 54             }
 55         });
 56 
 57         // 将监听器类TFListener添加到输入框TextField中
 58         tfTxt.addActionListener(new TFListener());
 59         // 窗口可见
 60         setVisible(true);
 61         connect();
 62         
 63         tRecv.start();
 64     }
 65 
 66     // 建立连接的方法
 67     public void connect() {
 68         try {
 69             s = new Socket("127.0.0.1", 8888);
 70             dos = new DataOutputStream(s.getOutputStream());
 71             dis = new DataInputStream(s.getInputStream());
 72             System.out.println("connected!");
 73             bConnected = true;
 74         } catch (UnknownHostException e) {
 75             e.printStackTrace();
 76         } catch (IOException e) {
 77             e.printStackTrace();
 78         }
 79     }
 80 
 81     public void disconnect() {
 82         try {
 83             dos.close();
 84             dis.close();
 85             s.close();
 86         } catch (IOException e) {
 87             e.printStackTrace();
 88         }
 89     /*    try {
 90             bConnected=false;
 91             tRecv.join();
 92         } catch(InterruptedException e){
 93             e.printStackTrace();
 94         } finally{
 95             try {
 96                 dos.close();
 97                 dis.close();
 98                 s.close();
 99             } catch (IOException e) {
100                 e.printStackTrace();
101             }
102             
103         }*/
104     }
105 
106     private class RecvThread implements Runnable {
107 
108         @Override
109         public void run() {
110             try {
111                 while (bConnected) {
112                     String str = dis.readUTF();
113 //                    System.out.println(str);
114                     taContent.setText(taContent.getText()+str+"
");
115                 }
116             }catch(SocketException e){
117                 System.out.println("退出!over");
118             }catch (IOException e) {
119                 e.printStackTrace();
120             }
121         }
122     }
123 
124     // 建立私有的监听器类,TextField监听:将TextField内容放置到TextArea
125     private class TFListener implements ActionListener {
126 
127         @Override
128         public void actionPerformed(ActionEvent e) {
129             // 获得输入框的内容,并去除两端的空格
130             String string = tfTxt.getText().trim();
131             // 将获取的输入内容放置到TextArea中
132             taContent.setText(string);
133             // 每次输入结束,将输入框置空
134             tfTxt.setText("");
135             try {
136                 System.out.println(s);
137                 // 不同每次都获取一次连接
138                 // DataOutputStream dos=new
139                 // DataOutputStream(s.getOutputStream());
140                 dos.writeUTF(string);
141                 dos.flush();
142                 // dos.close();
143             } catch (IOException e1) {
144                 e1.printStackTrace();
145             }
146 
147         }
148     }
149 }
View Code

Server

  1 import java.io.DataInputStream;
  2 import java.io.DataOutputStream;
  3 import java.io.EOFException;
  4 import java.io.IOException;
  5 import java.net.BindException;
  6 import java.net.ServerSocket;
  7 import java.net.Socket;
  8 import java.util.ArrayList;
  9 import java.util.List;
 10 
 11 //server端
 12 public class ChatServer {
 13 
 14     boolean started = false;
 15     ServerSocket ss = null;
 16     List<Client> clients = new ArrayList<Client>();
 17 
 18     public static void main(String[] args) {
 19         new ChatServer().start();
 20     }
 21 
 22     public void start() {
 23 
 24         try {
 25             // 可能会产生端口绑定异常
 26             ss = new ServerSocket(8888);
 27             // 服务器端启动以后,started=true
 28             started = true;
 29         } catch (BindException e) {
 30             System.out.println("端口使用中");
 31             System.out.println("关闭相关程序,并重新运行");
 32             System.exit(0);
 33         } catch (IOException e) {
 34             e.printStackTrace();
 35         }
 36         try {
 37             // 服务器端启动以后才能不断接收客户端的连接
 38             while (started) {
 39                 Socket s = ss.accept();
 40                 // 不能在静态方法里new一个动态的类
 41                 Client c = new Client(s);
 42                 System.out.println("a clint connected");
 43                 new Thread(c).start();
 44                 clients.add(c);
 45             }
 46             // 如果是因为客户端的关闭而导致的连接中断,则做这样的处理
 47         } catch (IOException e) {
 48             e.printStackTrace();
 49 
 50         } finally {
 51             try {
 52                 ss.close();
 53             } catch (IOException e) {
 54                 e.printStackTrace();
 55             }
 56         }
 57     }
 58 
 59 class Client implements Runnable {
 60     private Socket s;
 61     private DataInputStream dis = null;
 62     private DataOutputStream dos = null;
 63     private boolean bConnected = false;
 64 
 65     public Client(Socket s) {
 66         this.s = s;
 67         try {
 68             dis = new DataInputStream(s.getInputStream());
 69             dos = new DataOutputStream(s.getOutputStream());
 70             bConnected = true;
 71         } catch (IOException e) {
 72             e.printStackTrace();
 73 
 74         }
 75     }
 76 
 77     // 发送的方法
 78     public void send(String str) {
 79         try {
 80             dos.writeUTF(str);
 81         } catch (IOException e) {
 82             e.printStackTrace();
 83         }
 84     }
 85 
 86     @Override
 87     public void run() {
 88         try {
 89             while (bConnected) {
 90                 String str = dis.readUTF();
 91                 System.out.println(str);
 92                 for(int i=0;i<clients.size();i++){
 93                     Client c=clients.get(i);
 94                     c.send(str);
 95                 }
 96             }
 97         } catch (EOFException e) {
 98             System.out.println("Client closed");
 99         } catch (IOException e) {
100             e.printStackTrace();
101         } finally {
102             try {
103                 if (dis != null) {
104                     dis.close();
105                 }
106                 if (s != null) {
107                     s.close();
108                 }
109                 if(dos!=null){
110                     dos.close();
111                 }
112             } catch (Exception e2) {
113                 e2.printStackTrace();
114             }
115         }
116 
117     }
118 }
119 }
View Code

此时我们已经可以实现我们的预期功能了,不同的客户端可以进行通信了;

版本1.3

ChatClient

  1 import java.awt.*;
  2 import java.awt.event.*;
  3 import java.io.*;
  4 import java.net.*;
  5 
  6 public class ChatClient extends Frame {
  7     Socket s = null;
  8     DataOutputStream dos = null;
  9     DataInputStream dis = null;
 10     private boolean bConnected = false;
 11 
 12     TextField tfTxt = new TextField();
 13 
 14     TextArea taContent = new TextArea();
 15 
 16     Thread tRecv = new Thread(new RecvThread());
 17 
 18     public static void main(String[] args) {
 19         new ChatClient().launchFrame();
 20     }
 21 
 22     public void launchFrame() {
 23         setLocation(400, 300);
 24         this.setSize(300, 300);
 25         add(tfTxt, BorderLayout.SOUTH);
 26         add(taContent, BorderLayout.NORTH);
 27         pack();
 28         this.addWindowListener(new WindowAdapter() {
 29 
 30             @Override
 31             public void windowClosing(WindowEvent arg0) {
 32                 disconnect();
 33                 System.exit(0);
 34             }
 35 
 36         });
 37         tfTxt.addActionListener(new TFListener());
 38         setVisible(true);
 39         connect();
 40 
 41         tRecv.start();
 42     }
 43 
 44     public void connect() {
 45         try {
 46             s = new Socket("127.0.0.1", 8888);
 47             dos = new DataOutputStream(s.getOutputStream());
 48             dis = new DataInputStream(s.getInputStream());
 49             System.out.println("connected!");
 50             bConnected = true;
 51         } catch (UnknownHostException e) {
 52             e.printStackTrace();
 53         } catch (IOException e) {
 54             e.printStackTrace();
 55         }
 56 
 57     }
 58 
 59     public void disconnect() {
 60         try {
 61             dos.close();
 62             dis.close();
 63             s.close();
 64         } catch (IOException e) {
 65             e.printStackTrace();
 66         }
 67 
 68         /*
 69          * try { bConnected = false; tRecv.join(); } catch(InterruptedException
 70          * e) { e.printStackTrace(); } finally { try { dos.close(); dis.close();
 71          * s.close(); } catch (IOException e) { e.printStackTrace(); } }
 72          */
 73     }
 74 
 75     private class TFListener implements ActionListener {
 76 
 77         public void actionPerformed(ActionEvent e) {
 78             String str = tfTxt.getText().trim();
 79             // taContent.setText(str);
 80             tfTxt.setText("");
 81 
 82             try {
 83                 // System.out.println(s);
 84                 dos.writeUTF(str);
 85                 dos.flush();
 86                 // dos.close();
 87             } catch (IOException e1) {
 88                 e1.printStackTrace();
 89             }
 90 
 91         }
 92 
 93     }
 94 
 95     private class RecvThread implements Runnable {
 96 
 97         public void run() {
 98             try {
 99                 while (bConnected) {
100                     String str = dis.readUTF();
101                     // System.out.println(str);
102                     taContent.setText(taContent.getText() + str + '
');
103                 }
104             } catch (SocketException e) {
105                 System.out.println("退出了,bye!");
106             } catch (EOFException e) {
107                 System.out.println("推出了,bye - bye!");
108             } catch (IOException e) {
109                 e.printStackTrace();
110             }
111 
112         }
113 
114     }
115 }
View Code

Server

  1 import java.io.*;
  2 import java.net.*;
  3 import java.util.*;
  4 
  5 public class ChatServer {
  6     boolean started = false;
  7     ServerSocket ss = null;
  8 
  9     List<Client> clients = new ArrayList<Client>();
 10 
 11     public static void main(String[] args) {
 12         new ChatServer().start();
 13     }
 14 
 15     public void start() {
 16         try {
 17             ss = new ServerSocket(8888);
 18             started = true;
 19         } catch (BindException e) {
 20             System.out.println("端口使用中....");
 21             System.out.println("请关掉相关程序并重新运行服务器!");
 22             System.exit(0);
 23         } catch (IOException e) {
 24             e.printStackTrace();
 25         }
 26 
 27         try {
 28 
 29             while (started) {
 30                 Socket s = ss.accept();
 31                 Client c = new Client(s);
 32                 System.out.println("a client connected!");
 33                 new Thread(c).start();
 34                 clients.add(c);
 35                 // dis.close();
 36             }
 37         } catch (IOException e) {
 38             e.printStackTrace();
 39         } finally {
 40             try {
 41                 ss.close();
 42             } catch (IOException e) {
 43                 // TODO Auto-generated catch block
 44                 e.printStackTrace();
 45             }
 46         }
 47     }
 48 
 49     class Client implements Runnable {
 50         private Socket s;
 51         private DataInputStream dis = null;
 52         private DataOutputStream dos = null;
 53         private boolean bConnected = false;
 54 
 55         public Client(Socket s) {
 56             this.s = s;
 57             try {
 58                 dis = new DataInputStream(s.getInputStream());
 59                 dos = new DataOutputStream(s.getOutputStream());
 60                 bConnected = true;
 61             } catch (IOException e) {
 62                 e.printStackTrace();
 63             }
 64         }
 65 
 66         public void send(String str) {
 67             try {
 68                 dos.writeUTF(str);
 69             } catch (IOException e) {
 70                 clients.remove(this);
 71                 System.out.println("对方退出了!我从List里面去掉了!");
 72                 // e.printStackTrace();
 73             }
 74         }
 75 
 76         public void run() {
 77             try {
 78                 while (bConnected) {
 79                     String str = dis.readUTF();
 80                     System.out.println(str);
 81                     for (int i = 0; i < clients.size(); i++) {
 82                         Client c = clients.get(i);
 83                         c.send(str);
 84                         // System.out.println(" a string send !");
 85                     }
 86                     /*
 87                      * for(Iterator<Client> it = clients.iterator();
 88                      * it.hasNext(); ) { Client c = it.next(); c.send(str); }
 89                      */
 90                     /*
 91                      * Iterator<Client> it = clients.iterator();
 92                      * while(it.hasNext()) { Client c = it.next(); c.send(str);
 93                      * }
 94                      */
 95                 }
 96             } catch (EOFException e) {
 97                 System.out.println("Client closed!");
 98             } catch (IOException e) {
 99                 e.printStackTrace();
100             } finally {
101                 try {
102                     if (dis != null)
103                         dis.close();
104                     if (dos != null)
105                         dos.close();
106                     if (s != null) {
107                         s.close();
108                         // s = null;
109                     }
110 
111                 } catch (IOException e1) {
112                     e1.printStackTrace();
113                 }
114 
115             }
116         }
117 
118     }
119 }
View Code

对程序出现的一些bug进行了修复,细化了关闭连接资源等操作,可以根据每个版本的差异梳理清楚代码的编写过程;

原文地址:https://www.cnblogs.com/ysw-go/p/5499630.html