├── out
└── production
│ ├── Client
│ └── com
│ │ └── wxy
│ │ ├── ClientChat.class
│ │ ├── ClientJframe.class
│ │ ├── ClientJframe$1.class
│ │ └── ClientJframe$Receive.class
│ └── Server
│ └── com
│ └── wxy
│ ├── ServerChat.class
│ ├── ServerJframe.class
│ ├── ServerJframe$1.class
│ ├── ServerJframe$2.class
│ └── ServerJframe$ClientCoon.class
├── .idea
├── vcs.xml
├── .gitignore
├── misc.xml
├── modules.xml
└── inspectionProfiles
│ └── Project_Default.xml
├── ChatRoom.iml
├── Client
├── Client.iml
└── src
│ └── com
│ └── wxy
│ └── ClientChat.java
├── Server
├── Server.iml
└── src
│ └── com
│ └── wxy
│ └── ServerChat.java
├── README.md
└── 多人聊天系统 2021.03.02.md
/out/production/Client/com/wxy/ClientChat.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Client/com/wxy/ClientChat.class
--------------------------------------------------------------------------------
/out/production/Server/com/wxy/ServerChat.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Server/com/wxy/ServerChat.class
--------------------------------------------------------------------------------
/out/production/Client/com/wxy/ClientJframe.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Client/com/wxy/ClientJframe.class
--------------------------------------------------------------------------------
/out/production/Server/com/wxy/ServerJframe.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Server/com/wxy/ServerJframe.class
--------------------------------------------------------------------------------
/out/production/Client/com/wxy/ClientJframe$1.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Client/com/wxy/ClientJframe$1.class
--------------------------------------------------------------------------------
/out/production/Server/com/wxy/ServerJframe$1.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Server/com/wxy/ServerJframe$1.class
--------------------------------------------------------------------------------
/out/production/Server/com/wxy/ServerJframe$2.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Server/com/wxy/ServerJframe$2.class
--------------------------------------------------------------------------------
/out/production/Client/com/wxy/ClientJframe$Receive.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Client/com/wxy/ClientJframe$Receive.class
--------------------------------------------------------------------------------
/out/production/Server/com/wxy/ServerJframe$ClientCoon.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuxinyu3366/ChatRoom/HEAD/out/production/Server/com/wxy/ServerJframe$ClientCoon.class
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /../../../../../../:\WorkingSpace\Idea_workingspace\JAVASE\ChatRoom\.idea/dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ChatRoom.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Client/Client.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Server/Server.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ChatRoom
2 | 多人聊天系统 2021.03.02 实现了多个聊天客户端和一个服务器端的多人聊天
3 |
4 | 项目环境:
5 | IDEA 2020.3.1 JDK1.8.0_271
6 |
7 | 项目参考:
8 | # 多人聊天系统
9 |
10 | https://www.bilibili.com/video/BV1M4411A78q
11 |
12 | # TCP用多线程实现多人聊天的项目
13 |
14 | https://www.bilibili.com/video/BV16E411F79L?from=search&seid=12745764004109122366
15 |
16 | 课程学时: 8学时
17 |
18 | 知识要点: I0 编程、Socket 编程、多线程编程、异常处理、集合类的使用
19 |
20 | 编程思路:
21 |
22 | 1、客户端聊天窗口的创建
23 |
24 | 2、添加服务器与客户端的连接
25 |
26 | 3、连接上以后考虑将客户端的信息发送到服务器上
27 |
28 | 4、利用多线程实现连接多个客户端
29 |
30 | 5、多个客户端的信息发送到服务器上以后,考虑把服务器上的信息发送到每个客户端
31 |
32 | 6、在服务器端要取到每个客户端的socket,才可以把信息发送到每个客户端上,利用集合类存储客户的多个线程的连接
33 |
34 | 7、在客户端考虑利用多线程接受服务器上的信息
35 |
36 | 8、最后实现多人聊天室的效果
37 |
38 | 9、后期修改程序中的不足和发现bug
39 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
--------------------------------------------------------------------------------
/Client/src/com/wxy/ClientChat.java:
--------------------------------------------------------------------------------
1 | package com.wxy;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.event.ActionEvent;
6 | import java.awt.event.ActionListener;
7 | import java.io.DataInputStream;
8 | import java.io.DataOutputStream;
9 | import java.io.IOException;
10 | import java.net.Socket;
11 | import java.net.SocketException;
12 |
13 | /**
14 | * @author Administrator
15 | * @Auther: wuxy
16 | * @Date: 2021/3/2 - 03 - 02 - 20:16
17 | * @Description: com.wxy
18 | * @version: 1.0
19 | */
20 | public class ClientChat {
21 | //这是一个main方法,是程序的入口:
22 | public static void main(String[] args) {
23 | ClientJframe clientJframe = new ClientJframe();
24 | clientJframe.init();
25 | }
26 | }
27 |
28 | class ClientJframe extends JFrame {
29 | //GUI布局
30 | //聊天记录显示区
31 | private JTextArea ta = new JTextArea(10, 20);
32 | //聊天记录输入区
33 | private JTextField tf = new JTextField(20);
34 |
35 | //端口
36 | // 静态常量主机端口号
37 | private static final String CONNSTR = "127.0.0.1";
38 | // 静态常量服务器端口号
39 | private static final int CONNPORT = 8888;
40 | private Socket socket = null;
41 |
42 | //Client发送数据
43 | private DataOutputStream dataOutputStream = null;
44 |
45 | //客户端连接上服务器判断符号
46 | private boolean isConn = false;
47 |
48 | /**
49 | * 无参的构造方法 throws HeadlessException
50 | */
51 | public ClientJframe() throws HeadlessException {
52 | super();
53 | }
54 |
55 | public void init() {
56 | this.setTitle("客户端窗口");
57 | this.add(ta, BorderLayout.CENTER);
58 | this.add(tf, BorderLayout.SOUTH);
59 |
60 | this.setBounds(300, 300, 400, 400);
61 |
62 | // 添加监听,使回车键可以输入数据(判断数据合法性),
63 | // 并輸入到聊天框,换行
64 | tf.addActionListener(new ActionListener() {
65 |
66 | @Override
67 | public void actionPerformed(ActionEvent e) {
68 | String strSend = tf.getText();
69 | // 去掉空格判断长度是否为空
70 | if (strSend.trim().length() == 0) {
71 | return;
72 | }
73 | //客户端信息strSend发送到服务器上
74 | send(strSend);
75 | tf.setText("");
76 | //ta.append(strSend + "\n");
77 |
78 | }
79 | });
80 |
81 | //关闭事件
82 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
83 | ta.setEditable(false);//聊天区域不可以输入
84 | tf.requestFocus();//光标聚焦
85 |
86 | try {
87 | socket = new Socket(CONNSTR, CONNPORT);
88 | isConn = true;
89 | } catch (Exception e) {
90 | e.printStackTrace();
91 | }
92 |
93 | // 启动多线程
94 | new Thread(new Receive()).start();
95 |
96 | this.setVisible(true);
97 | }
98 |
99 | /**
100 | * 客户端发送信息到服务器上的方法
101 | */
102 | public void send(String str) {
103 | try {
104 | dataOutputStream = new DataOutputStream(socket.getOutputStream());
105 | dataOutputStream.writeUTF(str);
106 | } catch (IOException e) {
107 | // TODO Auto-generated catch block
108 | e.printStackTrace();
109 | }
110 | }
111 |
112 | /**
113 | * @author 武新宇
114 | * @deprecated 多线程的类,实现了Runnable接口的类
115 | */
116 | class Receive implements Runnable {
117 | @Override
118 | public void run() {
119 | try {
120 | while (isConn) {
121 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
122 | String str = dataInputStream.readUTF();
123 | //通讯消息
124 | ta.append(str);
125 | }
126 | } catch (SocketException e) {
127 | System.out.println("服务器意外终止了!");
128 | ta.append("服务器意外终止了!");
129 | } catch (IOException e) {
130 | // TODO Auto-generated catch block
131 | e.printStackTrace();
132 | }
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/Server/src/com/wxy/ServerChat.java:
--------------------------------------------------------------------------------
1 | package com.wxy;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.event.ActionEvent;
6 | import java.awt.event.ActionListener;
7 | import java.io.DataInputStream;
8 | import java.io.DataOutputStream;
9 | import java.io.IOException;
10 | import java.net.ServerSocket;
11 | import java.net.Socket;
12 | import java.net.SocketException;
13 | import java.util.ArrayList;
14 | import java.util.Iterator;
15 |
16 | /**
17 | * @author Administrator
18 | * @Auther: wuxy
19 | * @Date: 2021/3/2 - 03 - 02 - 20:54
20 | * @Description: com.wxy
21 | * @version: 1.0
22 | */
23 | public class ServerChat {
24 | //这是一个main方法,是程序的入口:
25 | public static void main(String[] args) throws Exception {
26 | ServerJframe serverJframe = new ServerJframe();
27 | serverJframe.init();
28 | }
29 | }
30 |
31 | class ServerJframe extends JFrame {
32 | //GUI相关属性
33 | JTextArea serverTa = new JTextArea();
34 | JPanel btnTool = new JPanel();
35 | JButton startBtn = new JButton("启动");
36 | JButton stopBtn = new JButton("停止");
37 | //端口
38 | private static final int PORT = 8888;
39 | //ServerSocket
40 | private ServerSocket serverSocket = null;
41 | private Socket socket = null;
42 |
43 | //Server接受数据
44 | private DataInputStream dataInputStream = null;
45 |
46 | // 多个客户端访问时,客户端对象存放入List中
47 | private ArrayList ccList = new ArrayList();
48 |
49 | // 服务器启动的标志 (其实ServerSocket ss 初始化出来时以为者服务器的启动)
50 | private boolean isStart = false;
51 |
52 | public void init() throws Exception {
53 | this.setTitle("服务器端窗口");
54 | this.add(serverTa, BorderLayout.CENTER);//流式布局
55 | btnTool.add(startBtn);
56 | btnTool.add(stopBtn);
57 | this.add(btnTool, BorderLayout.SOUTH);
58 |
59 | this.setBounds(0, 0, 500, 500);
60 |
61 | //判断服务器是否已经开启
62 | if (isStart) {
63 | System.out.println("服务器已经启动了\n");
64 | } else {
65 | System.out.println("服务器还没有启动,请点击启动服务器!\n");
66 | }
67 | //按钮监听监听服务器开启,置开始位false
68 | startBtn.addActionListener(new ActionListener() {
69 | @Override
70 | public void actionPerformed(ActionEvent e) {
71 | try {
72 | if (serverSocket == null) {
73 | serverSocket = new ServerSocket(PORT);
74 | }
75 | isStart = true;
76 | //startServer();
77 | serverTa.append("服务器已经启动啦! \n");
78 | } catch (Exception e1) {
79 | e1.printStackTrace();
80 | }
81 | }
82 | });
83 | // 终止按钮监听停止服务器,置开始位true
84 | stopBtn.addActionListener(new ActionListener() {
85 | @Override
86 | public void actionPerformed(ActionEvent e) {
87 | try {
88 | if (serverSocket != null) {
89 | serverSocket.close();
90 | isStart = false;
91 | }
92 | System.exit(0);
93 | serverTa.append("服务器断啦!!\n");
94 | System.out.println("服务器断啦!!\n");
95 |
96 | } catch (Exception e1) {
97 | // TODO Auto-generated catch block
98 | e1.printStackTrace();
99 | }
100 | }
101 | });
102 |
103 | /**
104 | * 服务器窗口关闭应该停止服务器,需改进的代码
105 | */
106 | // this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
107 | // serverTa.setEditable(false);
108 | this.setVisible(true);
109 | startServer();
110 | }
111 |
112 | /**
113 | * 服务器启动代码
114 | */
115 | public void startServer() throws Exception {
116 | try {
117 | try {
118 | serverSocket = new ServerSocket(PORT);
119 | isStart = true;
120 | } catch (Exception e) {
121 | e.printStackTrace();
122 | }
123 | // 可以接受多个客户端的连接
124 | // 接每一個信息时,服务器不可以终断,所以将其写入while()中,判断符为服务器开关的判断符
125 | while (isStart) {
126 | socket = serverSocket.accept();
127 | ccList.add(new ClientCoon(socket));
128 | System.out.println("\n" + "一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
129 | serverTa.append("\n" + "一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
130 | }
131 | //服务器接受客户端一句话
132 | /*receiveStream();*/
133 | } catch (SocketException e) {
134 | System.out.println("服务器终断了!!!");
135 | } catch (Exception e) {
136 | e.printStackTrace();
137 | }
138 | }
139 | /**
140 | * 服务器停止代码
141 | * */
142 |
143 | /**
144 | * 服务器接受数据的方法(客户端传送一句话),不适用多个客户端进行通话
145 | * */
146 | /*public void receiveStream(){
147 | try {
148 | dataInputStream = new DataInputStream(socket.getInputStream());
149 | String str = dataInputStream.readUTF();
150 | System.out.println(str);
151 | serverTa.append(str);
152 | }catch (Exception e){
153 | e.printStackTrace();
154 | }
155 | }*/
156 |
157 | /**
158 | * @author 武新宇
159 | * @deprecated 内部类声明 对象 这个对象是属于服务器端的一个连接对象
160 | */
161 | class ClientCoon implements Runnable {
162 | Socket socket = null;
163 |
164 | public ClientCoon(Socket socket) {
165 | this.socket = socket;
166 | /**
167 | * 线程启动在这里:
168 | * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
169 | */
170 | (new Thread(this)).start();
171 | }
172 |
173 | //接受客户端信息(多线程run()方法)
174 | @Override
175 | public void run() {
176 | try {
177 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
178 | // 为了让服务器能够接受到每个客户端的多句话
179 | while (isStart) {
180 | //readUTF()是一种阻塞方法,接一句就执行完了,所以循环中
181 | String str = dataInputStream.readUTF();
182 | System.out.println("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
183 | serverTa.append("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
184 | //服务器向每个客户端发送别的客户端发来的信息
185 | // 遍历ccList,调用send方法,在客户端里接受应该是多线程的接受
186 | String strSend = "\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n";
187 | Iterator iterator = ccList.iterator();
188 | while (iterator.hasNext()) {
189 | ClientCoon clientCoon = iterator.next();
190 | clientCoon.send(strSend);
191 | }
192 | }
193 | } catch (Exception e) {
194 | e.printStackTrace();
195 | }
196 | }
197 |
198 | // 服务器向每個连接对象发送数据的方法
199 | public void send(String str) {
200 | try {
201 | DataOutputStream dataOutputStream = new DataOutputStream(this.socket.getOutputStream());
202 | dataOutputStream.writeUTF(str);
203 | } catch (SocketException e) {
204 | e.printStackTrace();
205 | } catch (IOException e) {
206 | e.printStackTrace();
207 | }
208 | }
209 | }
210 |
211 |
212 | }
213 |
214 |
215 |
216 |
--------------------------------------------------------------------------------
/多人聊天系统 2021.03.02.md:
--------------------------------------------------------------------------------
1 | # 多人聊天系统
2 |
3 | https://www.bilibili.com/video/BV1M4411A78q
4 |
5 | # TCP用多线程实现多人聊天的项目
6 |
7 | https://www.bilibili.com/video/BV16E411F79L?from=search&seid=12745764004109122366
8 |
9 | 课程学时: 8学时
10 | 知识要点: I0 编程、Socket 编程、多线程编程、异常处理、集合类的使用
11 | 编程思路:
12 | 1、客户端聊天窗口的创建
13 | 2、添加服务器与客户端的连接
14 | 3、连接上以后考虑将客户端的信息发送到服务器上
15 | 4、利用多线程实现连接多个客户端
16 | 5、多个客户端的信息发送到服务器上以后,考虑把服务器上的信息发送到每个客户端
17 | 6、在服务器端要取到每个客户端的socket,才可以把信息发送到每个客户端上,利用集合
18 | 类存储客户的多个线程的连接
19 | 7、在客户端考虑利用多线程接受服务器上的信息
20 | 8、最后实现多人聊天室的效果
21 | 9、后期修改程序中的不足和发现bug
22 |
23 | 
24 |
25 | ## 编程思路:
26 |
27 | ### 1、客户端聊天窗口的创建
28 |
29 | 
30 |
31 | **ClientChat.java**
32 |
33 | ```java
34 | package com.wxy;
35 |
36 | import javax.swing.*;
37 | import java.awt.*;
38 | import java.awt.event.ActionEvent;
39 | import java.awt.event.ActionListener;
40 |
41 | /**
42 | * @author Administrator
43 | * @Auther: wuxy
44 | * @Date: 2021/3/2 - 03 - 02 - 20:16
45 | * @Description: com.wxy
46 | * @version: 1.0
47 | */
48 | public class ClientChat {
49 | //这是一个main方法,是程序的入口:
50 | public static void main(String[] args) {
51 | ClientJframe clientJframe = new ClientJframe();
52 | clientJframe.init();
53 | }
54 | }
55 |
56 | class ClientJframe extends JFrame {
57 | //GUI布局
58 | //聊天记录显示区
59 | private JTextArea ta = new JTextArea(10, 20);
60 | //聊天记录输入区
61 | private JTextField tf = new JTextField(20);
62 |
63 | public void init() {
64 | this.setTitle("客户端窗口");
65 | this.add(ta, BorderLayout.CENTER);
66 | this.add(tf, BorderLayout.SOUTH);
67 |
68 | this.setBounds(300, 300, 400, 400);
69 |
70 | // 添加监听,使回车键可以输入数据(判断数据合法性),
71 | // 并輸入到聊天框,换行
72 | tf.addActionListener(new ActionListener() {
73 |
74 | @Override
75 | public void actionPerformed(ActionEvent e) {
76 | String strSend = tf.getText();
77 | // 去掉空格判断长度是否为空
78 | if (strSend.trim().length() == 0) {
79 | return;
80 | }
81 | tf.setText("");
82 | ta.append(strSend + "\n");
83 |
84 | }
85 | });
86 |
87 | //关闭事件
88 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
89 | ta.setEditable(false);//聊天区域不可以输入
90 | tf.requestFocus();//光标聚焦
91 | this.setVisible(true);
92 | }
93 | }
94 | ```
95 |
96 | 
97 |
98 | #### 功能扩展
99 |
100 | 1.要求文字区域不能编辑
101 |
102 | ```java
103 | ta.setEditable(false);//聊天区域不可以输入
104 | ```
105 |
106 | 2.输入文字按回车发送到聊天内容区ta
107 |
108 | ```java
109 | // 添加监听,使回车键可以输入数据(判断数据合法性),
110 | // 并輸入到聊天框,换行
111 | tf.addActionListener(new ActionListener() {
112 |
113 | @Override
114 | public void actionPerformed(ActionEvent e) {
115 | String strSend = tf.getText();
116 | // 去掉空格判断长度是否为空
117 | if (strSend.trim().length() == 0) {
118 | return;
119 | }
120 | tf.setText("");
121 | ta.append(strSend + "\n");
122 |
123 | }
124 | });
125 | ```
126 |
127 | 
128 |
129 | ### 2、服务器窗口的创建
130 |
131 | **ServerChat.java**
132 |
133 | ```java
134 | package com.wxy;
135 |
136 | import javax.swing.*;
137 | import java.awt.*;
138 |
139 | /**
140 | * @author Administrator
141 | * @Auther: wuxy
142 | * @Date: 2021/3/2 - 03 - 02 - 20:54
143 | * @Description: com.wxy
144 | * @version: 1.0
145 | */
146 | public class ServerChat {
147 | //这是一个main方法,是程序的入口:
148 | public static void main(String[] args) {
149 | ServerJframe serverJframe = new ServerJframe();
150 | serverJframe.init();
151 | }
152 | }
153 | class ServerJframe extends JFrame{
154 | JTextArea serverTa = new JTextArea();
155 | JPanel btnTool = new JPanel();
156 | JButton startBtn = new JButton("启动");
157 | JButton stopBtn = new JButton("停止");
158 | public void init() {
159 | this.setTitle("服务器端窗口");
160 | this.add(serverTa, BorderLayout.CENTER);//流式布局
161 | btnTool.add(startBtn);
162 | btnTool.add(stopBtn);
163 | this.add(btnTool,BorderLayout.SOUTH);
164 |
165 | this.setBounds(0,0,500,500);
166 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
167 | serverTa.setEditable(false);//显示区域不可以输入文字
168 | this.setVisible(true);
169 | }
170 | }
171 |
172 | ```
173 |
174 |
175 |
176 | 
177 |
178 | ### 3、 添加服务器(分发和接受)与客户端(多线程)的连接
179 |
180 | #### 服务器
181 |
182 | 
183 |
184 | ```java
185 | //端口
186 | private static final int PORT = 8888;
187 | //ServerSocket
188 | private ServerSocket serverSocket = null;
189 | private Socket socket = null;
190 | ```
191 |
192 |
193 |
194 | ```java
195 | //按钮监听监听服务器开启
196 | startBtn.addActionListener(new ActionListener() {
197 | @Override
198 | public void actionPerformed(ActionEvent e) {
199 | try {
200 | startServer();
201 | }catch (IOException e1){
202 | e1.printStackTrace();
203 | }
204 | }
205 | });
206 |
207 |
208 | /**
209 | * 服务器启动代码
210 | * */
211 | public void startServer() throws IOException{
212 | serverSocket=new ServerSocket(PORT);
213 | socket = serverSocket.accept();
214 | System.out.println("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
215 | serverTa.append("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
216 | }
217 | /**
218 | * 服务器停止代码
219 | * */
220 | ```
221 |
222 | **ServerChat.java**
223 |
224 | ```java
225 | package com.wxy;
226 |
227 | import javax.swing.*;
228 | import java.awt.*;
229 | import java.awt.event.ActionEvent;
230 | import java.awt.event.ActionListener;
231 | import java.io.IOException;
232 | import java.net.ServerSocket;
233 | import java.net.Socket;
234 |
235 | /**
236 | * @author Administrator
237 | * @Auther: wuxy
238 | * @Date: 2021/3/2 - 03 - 02 - 20:54
239 | * @Description: com.wxy
240 | * @version: 1.0
241 | */
242 | public class ServerChat {
243 | //这是一个main方法,是程序的入口:
244 | public static void main(String[] args) {
245 | ServerJframe serverJframe = new ServerJframe();
246 | serverJframe.init();
247 | }
248 | }
249 | class ServerJframe extends JFrame{
250 | //GUI相关属性
251 | JTextArea serverTa = new JTextArea();
252 | JPanel btnTool = new JPanel();
253 | JButton startBtn = new JButton("启动");
254 | JButton stopBtn = new JButton("停止");
255 | //端口
256 | private static final int PORT = 8888;
257 | //ServerSocket
258 | private ServerSocket serverSocket = null;
259 | private Socket socket = null;
260 |
261 | public void init() {
262 | this.setTitle("服务器端窗口");
263 | this.add(serverTa, BorderLayout.CENTER);//流式布局
264 | btnTool.add(startBtn);
265 | btnTool.add(stopBtn);
266 | this.add(btnTool,BorderLayout.SOUTH);
267 |
268 | this.setBounds(0,0,500,500);
269 |
270 | //按钮监听监听服务器开启
271 | startBtn.addActionListener(new ActionListener() {
272 | @Override
273 | public void actionPerformed(ActionEvent e) {
274 | try {
275 | startServer();
276 | }catch (IOException e1){
277 | e1.printStackTrace();
278 | }
279 | }
280 | });
281 | //关闭客户端窗口的同时应该关闭服务器,要修改
282 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
283 | serverTa.setEditable(false);//显示区域不可以输入文字
284 | this.setVisible(true);
285 | }
286 | /**
287 | * 服务器启动代码
288 | * */
289 | public void startServer() throws IOException{
290 | serverSocket=new ServerSocket(PORT);
291 | socket = serverSocket.accept();
292 | System.out.println("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
293 | serverTa.append("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
294 | }
295 | /**
296 | * 服务器停止代码
297 | * */
298 |
299 | }
300 |
301 | ```
302 |
303 | #### 客户端
304 |
305 | 
306 |
307 | ```java
308 | //端口
309 | // 静态常量主机端口号
310 | private static final String CONNSTR = "127.0.0.1";
311 | // 静态常量服务器端口号
312 | private static final int CONNPORT = 8888;
313 | private Socket socket = null;
314 | ```
315 |
316 |
317 |
318 | ```java
319 | try {
320 | socket=new Socket(CONNSTR,CONNPORT);
321 | }catch (Exception e){
322 | e.printStackTrace();
323 | }
324 | ```
325 |
326 | **ServerChat.java**
327 |
328 | ```java
329 | package com.wxy;
330 |
331 | import javax.swing.*;
332 | import java.awt.*;
333 | import java.awt.event.ActionEvent;
334 | import java.awt.event.ActionListener;
335 | import java.io.IOException;
336 | import java.net.Socket;
337 |
338 | /**
339 | * @author Administrator
340 | * @Auther: wuxy
341 | * @Date: 2021/3/2 - 03 - 02 - 20:16
342 | * @Description: com.wxy
343 | * @version: 1.0
344 | */
345 | public class ClientChat {
346 | //这是一个main方法,是程序的入口:
347 | public static void main(String[] args) {
348 | ClientJframe clientJframe = new ClientJframe();
349 | clientJframe.init();
350 | }
351 | }
352 |
353 | class ClientJframe extends JFrame {
354 | //GUI布局
355 | //聊天记录显示区
356 | private JTextArea ta = new JTextArea(10, 20);
357 | //聊天记录输入区
358 | private JTextField tf = new JTextField(20);
359 |
360 | //端口
361 | // 静态常量主机端口号
362 | private static final String CONNSTR = "127.0.0.1";
363 | // 静态常量服务器端口号
364 | private static final int CONNPORT = 8888;
365 | private Socket socket = null;
366 |
367 | public void init() {
368 | this.setTitle("客户端窗口");
369 | this.add(ta, BorderLayout.CENTER);
370 | this.add(tf, BorderLayout.SOUTH);
371 |
372 | this.setBounds(300, 300, 400, 400);
373 |
374 | // 添加监听,使回车键可以输入数据(判断数据合法性),
375 | // 并輸入到聊天框,换行
376 | tf.addActionListener(new ActionListener() {
377 |
378 | @Override
379 | public void actionPerformed(ActionEvent e) {
380 | String strSend = tf.getText();
381 | // 去掉空格判断长度是否为空
382 | if (strSend.trim().length() == 0) {
383 | return;
384 | }
385 | tf.setText("");
386 | ta.append(strSend + "\n");
387 |
388 | }
389 | });
390 |
391 | //关闭事件
392 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
393 | ta.setEditable(false);//聊天区域不可以输入
394 | tf.requestFocus();//光标聚焦
395 |
396 | try {
397 | socket = new Socket(CONNSTR, CONNPORT);
398 | } catch (Exception e) {
399 | e.printStackTrace();
400 | }
401 |
402 | this.setVisible(true);
403 | }
404 | }
405 | ```
406 |
407 | 
408 |
409 | ### 4、连接上以后考虑将客户端的信息发送到服务器上
410 |
411 | 在考虑发送信息到服务器上
412 |
413 | 
414 |
415 | #### **Client**:**发送数据**
416 |
417 | ```java
418 | //Client发送数据
419 | private DataOutputStream dataOutputStream = null;
420 | ```
421 |
422 |
423 |
424 | ```java
425 | /**
426 | * 客户端发送信息到服务器上的方法
427 | */
428 | public void send(String str) {
429 | try {
430 | dataOutputStream = new DataOutputStream(socket.getOutputStream());
431 | dataOutputStream.writeUTF(str);
432 | } catch (IOException e) {
433 | // TODO Auto-generated catch block
434 | e.printStackTrace();
435 | }
436 | }
437 | ```
438 |
439 |
440 |
441 | ```java
442 | tf.addActionListener(new ActionListener() {
443 |
444 | @Override
445 | public void actionPerformed(ActionEvent e) {
446 | String strSend = tf.getText();
447 | //客户端信息strSend发送到服务器上
448 | send(strSend);
449 | // 去掉空格判断长度是否为空
450 | if (strSend.trim().length() == 0) {
451 | return;
452 | }
453 | tf.setText("");
454 | ta.append(strSend + "\n");
455 |
456 | }
457 | });
458 | ```
459 |
460 |
461 |
462 | **ClientChat.java**
463 |
464 | ```java
465 | package com.wxy;
466 |
467 | import javax.swing.*;
468 | import java.awt.*;
469 | import java.awt.event.ActionEvent;
470 | import java.awt.event.ActionListener;
471 | import java.io.DataOutputStream;
472 | import java.io.IOException;
473 | import java.net.Socket;
474 |
475 | /**
476 | * @author Administrator
477 | * @Auther: wuxy
478 | * @Date: 2021/3/2 - 03 - 02 - 20:16
479 | * @Description: com.wxy
480 | * @version: 1.0
481 | */
482 | public class ClientChat {
483 | //这是一个main方法,是程序的入口:
484 | public static void main(String[] args) {
485 | ClientJframe clientJframe = new ClientJframe();
486 | clientJframe.init();
487 | }
488 | }
489 |
490 | class ClientJframe extends JFrame {
491 | //GUI布局
492 | //聊天记录显示区
493 | private JTextArea ta = new JTextArea(10, 20);
494 | //聊天记录输入区
495 | private JTextField tf = new JTextField(20);
496 |
497 | //端口
498 | // 静态常量主机端口号
499 | private static final String CONNSTR = "127.0.0.1";
500 | // 静态常量服务器端口号
501 | private static final int CONNPORT = 8888;
502 | private Socket socket = null;
503 |
504 | //Client发送数据
505 | private DataOutputStream dataOutputStream = null;
506 |
507 | public void init() {
508 | this.setTitle("客户端窗口");
509 | this.add(ta, BorderLayout.CENTER);
510 | this.add(tf, BorderLayout.SOUTH);
511 |
512 | this.setBounds(300, 300, 400, 400);
513 |
514 | // 添加监听,使回车键可以输入数据(判断数据合法性),
515 | // 并輸入到聊天框,换行
516 | tf.addActionListener(new ActionListener() {
517 |
518 | @Override
519 | public void actionPerformed(ActionEvent e) {
520 | String strSend = tf.getText();
521 | //客户端信息strSend发送到服务器上
522 | send(strSend);
523 | // 去掉空格判断长度是否为空
524 | if (strSend.trim().length() == 0) {
525 | return;
526 | }
527 | tf.setText("");
528 | ta.append(strSend + "\n");
529 |
530 | }
531 | });
532 |
533 | //关闭事件
534 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
535 | ta.setEditable(false);//聊天区域不可以输入
536 | tf.requestFocus();//光标聚焦
537 |
538 | try {
539 | socket = new Socket(CONNSTR, CONNPORT);
540 | } catch (Exception e) {
541 | e.printStackTrace();
542 | }
543 |
544 | this.setVisible(true);
545 | }
546 |
547 | /**
548 | * 客户端发送信息到服务器上的方法
549 | */
550 | public void send(String str) {
551 | try {
552 | dataOutputStream = new DataOutputStream(socket.getOutputStream());
553 | dataOutputStream.writeUTF(str);
554 | } catch (IOException e) {
555 | // TODO Auto-generated catch block
556 | e.printStackTrace();
557 | }
558 | }
559 | }
560 | ```
561 |
562 | #### 服务器:接受数据
563 |
564 | ```java
565 | //Server接受数据
566 | private DataInputStream dataInputStream = null;
567 | ```
568 |
569 |
570 |
571 | ```java
572 | /**
573 | * 服务器接受数据的方法
574 | * */
575 | public void receiveStream(){
576 | try {
577 | dataInputStream = new DataInputStream(socket.getInputStream());
578 | String str = dataInputStream.readUTF();
579 | System.out.println(str);
580 | serverTa.append(str);
581 | }catch (Exception e){
582 | e.printStackTrace();
583 | }
584 | }
585 | ```
586 |
587 |
588 |
589 | ```java
590 | public void startServer() throws IOException{
591 | serverSocket=new ServerSocket(PORT);
592 | socket = serverSocket.accept();
593 | System.out.println("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
594 | serverTa.append("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
595 | receiveStream();
596 | }
597 | ```
598 |
599 | **ServerChat.java**
600 |
601 | ```java
602 | package com.wxy;
603 |
604 | import javax.swing.*;
605 | import java.awt.*;
606 | import java.awt.event.ActionEvent;
607 | import java.awt.event.ActionListener;
608 | import java.io.DataInputStream;
609 | import java.io.IOException;
610 | import java.net.ServerSocket;
611 | import java.net.Socket;
612 |
613 | /**
614 | * @author Administrator
615 | * @Auther: wuxy
616 | * @Date: 2021/3/2 - 03 - 02 - 20:54
617 | * @Description: com.wxy
618 | * @version: 1.0
619 | */
620 | public class ServerChat {
621 | //这是一个main方法,是程序的入口:
622 | public static void main(String[] args) {
623 | ServerJframe serverJframe = new ServerJframe();
624 | serverJframe.init();
625 | }
626 | }
627 | class ServerJframe extends JFrame{
628 | //GUI相关属性
629 | JTextArea serverTa = new JTextArea();
630 | JPanel btnTool = new JPanel();
631 | JButton startBtn = new JButton("启动");
632 | JButton stopBtn = new JButton("停止");
633 | //端口
634 | private static final int PORT = 8888;
635 | //ServerSocket
636 | private ServerSocket serverSocket = null;
637 | private Socket socket = null;
638 |
639 | //Server接受数据
640 | private DataInputStream dataInputStream = null;
641 |
642 | public void init() {
643 | this.setTitle("服务器端窗口");
644 | this.add(serverTa, BorderLayout.CENTER);//流式布局
645 | btnTool.add(startBtn);
646 | btnTool.add(stopBtn);
647 | this.add(btnTool,BorderLayout.SOUTH);
648 |
649 | this.setBounds(0,0,500,500);
650 |
651 | //按钮监听监听服务器开启
652 | startBtn.addActionListener(new ActionListener() {
653 | @Override
654 | public void actionPerformed(ActionEvent e) {
655 | try {
656 | startServer();
657 | }catch (IOException e1){
658 | e1.printStackTrace();
659 | }
660 | }
661 | });
662 | //关闭客户端窗口的同时应该关闭服务器,要修改
663 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
664 | serverTa.setEditable(false);//显示区域不可以输入文字
665 | this.setVisible(true);
666 | }
667 | /**
668 | * 服务器启动代码
669 | * */
670 | public void startServer() throws IOException{
671 | serverSocket=new ServerSocket(PORT);
672 | socket = serverSocket.accept();
673 | System.out.println("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
674 | serverTa.append("一个客户端连接服务器"+socket.getInetAddress()+"/"+socket.getPort());
675 | receiveStream();
676 | }
677 | /**
678 | * 服务器停止代码
679 | * */
680 |
681 | /**
682 | * 服务器接受数据的方法
683 | * */
684 | public void receiveStream(){
685 | try {
686 | dataInputStream = new DataInputStream(socket.getInputStream());
687 | String str = dataInputStream.readUTF();
688 | System.out.println(str);
689 | serverTa.append(str);
690 | }catch (Exception e){
691 | e.printStackTrace();
692 | }
693 | }
694 | }
695 |
696 | ```
697 |
698 | 
699 |
700 | 一个客户端只能发送一句话!
701 |
702 | ### 5.利用多线程实现连接多个客户端
703 |
704 | 
705 |
706 | 
707 |
708 | ```java
709 | // 多个客户端访问时,客户端对象存放入List中
710 | private ArrayList ccList = new ArrayList();
711 |
712 | // 服务器启动的标志 (其实ServerSocket ss 初始化出来时以为者服务器的启动)
713 | private boolean isStart = false;
714 | ```
715 |
716 |
717 |
718 | ```java
719 | /**
720 | * @author 武新宇
721 | * @deprecated 内部类声明 对象 这个对象是属于服务器端的一个连接对象
722 | */
723 | class ClientCoon implements Runnable {
724 | Socket socket = null;
725 |
726 | public ClientCoon(Socket socket) {
727 | this.socket = socket;
728 | /**
729 | * 线程启动在这里:
730 | * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
731 | */
732 | (new Thread(this)).start();
733 | }
734 |
735 | @Override
736 | public void run() {
737 | try {
738 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
739 | String str = dataInputStream.readUTF();
740 | System.out.println(str);
741 | serverTa.append(str);
742 | } catch (Exception e) {
743 | e.printStackTrace();
744 | }
745 | }
746 | }
747 | ```
748 |
749 | ```java
750 | /**
751 | * 服务器启动代码
752 | */
753 | public void startServer() throws Exception {
754 | try {
755 | try {
756 | serverSocket=new ServerSocket(PORT);
757 | isStart=true;
758 | }catch (Exception e){
759 | e.printStackTrace();
760 | }
761 | // 可以接受多个客户端的连接
762 | // 接每一個信息时,服务器不可以终断,所以将其写入while()中,判断符为服务器开关的判断符
763 | while (isStart) {
764 | socket = serverSocket.accept();
765 | ccList.add(new ClientCoon(socket));
766 | System.out.println("一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
767 | serverTa.append("一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
768 | }
769 | //服务器接受客户端一句话
770 | /*receiveStream();*/
771 | }catch (Exception e){
772 | e.printStackTrace();
773 | }
774 | }
775 | ```
776 |
777 |
778 |
779 | ```java
780 | //判断服务器是否已经开启
781 | if (isStart) {
782 | System.out.println("服务器已经启动了\n");
783 | } else {
784 | System.out.println("服务器还没有启动,请点击启动服务器!\n");
785 | }
786 | //按钮监听监听服务器开启,置开始位false
787 | startBtn.addActionListener(new ActionListener() {
788 | @Override
789 | public void actionPerformed(ActionEvent e) {
790 | try {
791 | if(serverSocket ==null){
792 | serverSocket = new ServerSocket(PORT);
793 | }
794 | isStart = true;
795 | //startServer();
796 | serverTa.append("服务器已经启动啦! \n");
797 | } catch (Exception e1) {
798 | e1.printStackTrace();
799 | }
800 | }
801 | });
802 | // 终止按钮监听停止服务器,置开始位true
803 | stopBtn.addActionListener(new ActionListener() {
804 | @Override
805 | public void actionPerformed(ActionEvent e) {
806 | try {
807 | if (serverSocket!= null) {
808 | serverSocket.close();
809 | isStart = false;
810 | }
811 | System.exit(0);
812 | serverTa.append("服务器断啦!!\n");
813 | System.out.println("服务器断啦!!\n");
814 |
815 | } catch (Exception e1) {
816 | // TODO Auto-generated catch block
817 | e1.printStackTrace();
818 | }
819 | }
820 | });
821 | ```
822 |
823 |
824 |
825 | **ServerChat.java**
826 |
827 | ```java
828 | package com.wxy;
829 |
830 | import javax.swing.*;
831 | import java.awt.*;
832 | import java.awt.event.ActionEvent;
833 | import java.awt.event.ActionListener;
834 | import java.io.DataInputStream;
835 | import java.io.IOException;
836 | import java.net.ServerSocket;
837 | import java.net.Socket;
838 | import java.util.ArrayList;
839 |
840 | /**
841 | * @author Administrator
842 | * @Auther: wuxy
843 | * @Date: 2021/3/2 - 03 - 02 - 20:54
844 | * @Description: com.wxy
845 | * @version: 1.0
846 | */
847 | public class ServerChat {
848 | //这是一个main方法,是程序的入口:
849 | public static void main(String[] args) throws Exception {
850 | ServerJframe serverJframe = new ServerJframe();
851 | serverJframe.init();
852 | }
853 | }
854 |
855 | class ServerJframe extends JFrame {
856 | //GUI相关属性
857 | JTextArea serverTa = new JTextArea();
858 | JPanel btnTool = new JPanel();
859 | JButton startBtn = new JButton("启动");
860 | JButton stopBtn = new JButton("停止");
861 | //端口
862 | private static final int PORT = 8888;
863 | //ServerSocket
864 | private ServerSocket serverSocket = null;
865 | private Socket socket = null;
866 |
867 | //Server接受数据
868 | private DataInputStream dataInputStream = null;
869 |
870 | // 多个客户端访问时,客户端对象存放入List中
871 | private ArrayList ccList = new ArrayList();
872 |
873 | // 服务器启动的标志 (其实ServerSocket ss 初始化出来时以为者服务器的启动)
874 | private boolean isStart = false;
875 |
876 | public void init() throws Exception {
877 | this.setTitle("服务器端窗口");
878 | this.add(serverTa, BorderLayout.CENTER);//流式布局
879 | btnTool.add(startBtn);
880 | btnTool.add(stopBtn);
881 | this.add(btnTool, BorderLayout.SOUTH);
882 |
883 | this.setBounds(0, 0, 500, 500);
884 |
885 | //判断服务器是否已经开启
886 | if (isStart) {
887 | System.out.println("服务器已经启动了\n");
888 | } else {
889 | System.out.println("服务器还没有启动,请点击启动服务器!\n");
890 | }
891 | //按钮监听监听服务器开启,置开始位false
892 | startBtn.addActionListener(new ActionListener() {
893 | @Override
894 | public void actionPerformed(ActionEvent e) {
895 | try {
896 | if(serverSocket ==null){
897 | serverSocket = new ServerSocket(PORT);
898 | }
899 | isStart = true;
900 | //startServer();
901 | serverTa.append("服务器已经启动啦! \n");
902 | } catch (Exception e1) {
903 | e1.printStackTrace();
904 | }
905 | }
906 | });
907 | // 终止按钮监听停止服务器,置开始位true
908 | stopBtn.addActionListener(new ActionListener() {
909 | @Override
910 | public void actionPerformed(ActionEvent e) {
911 | try {
912 | if (serverSocket!= null) {
913 | serverSocket.close();
914 | isStart = false;
915 | }
916 | System.exit(0);
917 | serverTa.append("服务器断啦!!\n");
918 | System.out.println("服务器断啦!!\n");
919 |
920 | } catch (Exception e1) {
921 | // TODO Auto-generated catch block
922 | e1.printStackTrace();
923 | }
924 | }
925 | });
926 |
927 | /**
928 | * 服务器窗口关闭应该停止服务器,需改进的代码
929 | */
930 | // this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
931 | // serverTa.setEditable(false);
932 | this.setVisible(true);
933 | startServer();
934 | }
935 |
936 | /**
937 | * 服务器启动代码
938 | */
939 | public void startServer() throws Exception {
940 | try {
941 | try {
942 | serverSocket=new ServerSocket(PORT);
943 | isStart=true;
944 | }catch (Exception e){
945 | e.printStackTrace();
946 | }
947 | // 可以接受多个客户端的连接
948 | // 接每一個信息时,服务器不可以终断,所以将其写入while()中,判断符为服务器开关的判断符
949 | while (isStart) {
950 | socket = serverSocket.accept();
951 | ccList.add(new ClientCoon(socket));
952 | System.out.println("一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
953 | serverTa.append("一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
954 | }
955 | //服务器接受客户端一句话
956 | /*receiveStream();*/
957 | }catch (Exception e){
958 | e.printStackTrace();
959 | }
960 | }
961 | /**
962 | * 服务器停止代码
963 | * */
964 |
965 | /**
966 | * 服务器接受数据的方法(客户端传送一句话),不适用多个客户端进行通话
967 | * */
968 | /*public void receiveStream(){
969 | try {
970 | dataInputStream = new DataInputStream(socket.getInputStream());
971 | String str = dataInputStream.readUTF();
972 | System.out.println(str);
973 | serverTa.append(str);
974 | }catch (Exception e){
975 | e.printStackTrace();
976 | }
977 | }*/
978 |
979 | /**
980 | * @author 武新宇
981 | * @deprecated 内部类声明 对象 这个对象是属于服务器端的一个连接对象
982 | */
983 | class ClientCoon implements Runnable {
984 | Socket socket = null;
985 |
986 | public ClientCoon(Socket socket) {
987 | this.socket = socket;
988 | /**
989 | * 线程启动在这里:
990 | * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
991 | */
992 | (new Thread(this)).start();
993 | }
994 |
995 | @Override
996 | public void run() {
997 | try {
998 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
999 | String str = dataInputStream.readUTF();
1000 | System.out.println(str);
1001 | serverTa.append(str);
1002 | } catch (Exception e) {
1003 | e.printStackTrace();
1004 | }
1005 | }
1006 | }
1007 | }
1008 | ```
1009 |
1010 | 
1011 |
1012 | 现阶段:可以连多个客户端,但是每个客户端只能打一句话
1013 |
1014 | 解决问题:
1015 |
1016 | ```java
1017 | /**
1018 | * @author 武新宇
1019 | * @deprecated 内部类声明 对象 这个对象是属于服务器端的一个连接对象
1020 | */
1021 | class ClientCoon implements Runnable {
1022 | Socket socket = null;
1023 |
1024 | public ClientCoon(Socket socket) {
1025 | this.socket = socket;
1026 | /**
1027 | * 线程启动在这里:
1028 | * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
1029 | */
1030 | (new Thread(this)).start();
1031 | }
1032 |
1033 | @Override
1034 | public void run() {
1035 | try {
1036 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
1037 | // 为了让服务器能够接受到每个客户端的多句话
1038 | while(isStart) {
1039 | //readUTF()是一种阻塞方法,接一句就执行完了,所以循环中
1040 | String str = dataInputStream.readUTF();
1041 | System.out.println("\n"+socket.getInetAddress()+"|"+socket.getPort()+"说"+str+"\n");
1042 | serverTa.append("\n"+socket.getInetAddress()+"|"+socket.getPort()+"说"+str+"\n");
1043 | }
1044 | } catch (Exception e) {
1045 | e.printStackTrace();
1046 | }
1047 | }
1048 | }
1049 | ```
1050 |
1051 |
1052 |
1053 | 
1054 |
1055 | ### 6、多个客户端的信息发送到服务器上以后,考虑把服务器上的信息发送到每个客户端
1056 |
1057 | 
1058 |
1059 | #### 服務器端:
1060 |
1061 | ```java
1062 | // 服务器向每個连接对象发送数据的方法
1063 | public void send(String str) {
1064 | try {
1065 | DataOutputStream dataOutputStream = new DataOutputStream(this.s.getOutputStream());
1066 | dataOutputStream.writeUTF(str);
1067 | } catch (SocketException e) {
1068 | e.printStackTrace();
1069 | } catch (IOException e) {
1070 | e.printStackTrace();
1071 | }
1072 | }
1073 | ```
1074 |
1075 | ```java
1076 | //接受客户端信息(多线程run()方法)
1077 | @Override
1078 | public void run() {
1079 | try {
1080 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
1081 | // 为了让服务器能够接受到每个客户端的多句话
1082 | while (isStart) {
1083 | //readUTF()是一种阻塞方法,接一句就执行完了,所以循环中
1084 | String str = dataInputStream.readUTF();
1085 | System.out.println("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
1086 | serverTa.append("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
1087 | //服务器向每个客户端发送别的客户端发来的信息
1088 | // 遍历ccList,调用send方法,在客户端里接受应该是多线程的接受
1089 | String strSend = "\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n";
1090 | Iterator iterator = ccList.iterator();
1091 | while (iterator.hasNext()){
1092 | ClientCoon clientCoon = iterator.next();
1093 | clientCoon.send(strSend);
1094 | }
1095 | }
1096 | } catch (Exception e) {
1097 | e.printStackTrace();
1098 | }
1099 | }
1100 | ```
1101 |
1102 | **ServerChat.java**
1103 |
1104 | ```java
1105 | package com.wxy;
1106 |
1107 | import javax.swing.*;
1108 | import java.awt.*;
1109 | import java.awt.event.ActionEvent;
1110 | import java.awt.event.ActionListener;
1111 | import java.io.DataInputStream;
1112 | import java.io.DataOutputStream;
1113 | import java.io.IOException;
1114 | import java.net.ServerSocket;
1115 | import java.net.Socket;
1116 | import java.net.SocketException;
1117 | import java.util.ArrayList;
1118 | import java.util.Iterator;
1119 |
1120 | /**
1121 | * @author Administrator
1122 | * @Auther: wuxy
1123 | * @Date: 2021/3/2 - 03 - 02 - 20:54
1124 | * @Description: com.wxy
1125 | * @version: 1.0
1126 | */
1127 | public class ServerChat {
1128 | //这是一个main方法,是程序的入口:
1129 | public static void main(String[] args) throws Exception {
1130 | ServerJframe serverJframe = new ServerJframe();
1131 | serverJframe.init();
1132 | }
1133 | }
1134 |
1135 | class ServerJframe extends JFrame {
1136 | //GUI相关属性
1137 | JTextArea serverTa = new JTextArea();
1138 | JPanel btnTool = new JPanel();
1139 | JButton startBtn = new JButton("启动");
1140 | JButton stopBtn = new JButton("停止");
1141 | //端口
1142 | private static final int PORT = 8888;
1143 | //ServerSocket
1144 | private ServerSocket serverSocket = null;
1145 | private Socket socket = null;
1146 |
1147 | //Server接受数据
1148 | private DataInputStream dataInputStream = null;
1149 |
1150 | // 多个客户端访问时,客户端对象存放入List中
1151 | private ArrayList ccList = new ArrayList();
1152 |
1153 | // 服务器启动的标志 (其实ServerSocket ss 初始化出来时以为者服务器的启动)
1154 | private boolean isStart = false;
1155 |
1156 | public void init() throws Exception {
1157 | this.setTitle("服务器端窗口");
1158 | this.add(serverTa, BorderLayout.CENTER);//流式布局
1159 | btnTool.add(startBtn);
1160 | btnTool.add(stopBtn);
1161 | this.add(btnTool, BorderLayout.SOUTH);
1162 |
1163 | this.setBounds(0, 0, 500, 500);
1164 |
1165 | //判断服务器是否已经开启
1166 | if (isStart) {
1167 | System.out.println("服务器已经启动了\n");
1168 | } else {
1169 | System.out.println("服务器还没有启动,请点击启动服务器!\n");
1170 | }
1171 | //按钮监听监听服务器开启,置开始位false
1172 | startBtn.addActionListener(new ActionListener() {
1173 | @Override
1174 | public void actionPerformed(ActionEvent e) {
1175 | try {
1176 | if (serverSocket == null) {
1177 | serverSocket = new ServerSocket(PORT);
1178 | }
1179 | isStart = true;
1180 | //startServer();
1181 | serverTa.append("服务器已经启动啦! \n");
1182 | } catch (Exception e1) {
1183 | e1.printStackTrace();
1184 | }
1185 | }
1186 | });
1187 | // 终止按钮监听停止服务器,置开始位true
1188 | stopBtn.addActionListener(new ActionListener() {
1189 | @Override
1190 | public void actionPerformed(ActionEvent e) {
1191 | try {
1192 | if (serverSocket != null) {
1193 | serverSocket.close();
1194 | isStart = false;
1195 | }
1196 | System.exit(0);
1197 | serverTa.append("服务器断啦!!\n");
1198 | System.out.println("服务器断啦!!\n");
1199 |
1200 | } catch (Exception e1) {
1201 | // TODO Auto-generated catch block
1202 | e1.printStackTrace();
1203 | }
1204 | }
1205 | });
1206 |
1207 | /**
1208 | * 服务器窗口关闭应该停止服务器,需改进的代码
1209 | */
1210 | // this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
1211 | // serverTa.setEditable(false);
1212 | this.setVisible(true);
1213 | startServer();
1214 | }
1215 |
1216 | /**
1217 | * 服务器启动代码
1218 | */
1219 | public void startServer() throws Exception {
1220 | try {
1221 | try {
1222 | serverSocket = new ServerSocket(PORT);
1223 | isStart = true;
1224 | } catch (Exception e) {
1225 | e.printStackTrace();
1226 | }
1227 | // 可以接受多个客户端的连接
1228 | // 接每一個信息时,服务器不可以终断,所以将其写入while()中,判断符为服务器开关的判断符
1229 | while (isStart) {
1230 | socket = serverSocket.accept();
1231 | ccList.add(new ClientCoon(socket));
1232 | System.out.println("\n" + "一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
1233 | serverTa.append("\n" + "一个客户端连接服务器" + socket.getInetAddress() + "/" + socket.getPort());
1234 | }
1235 | //服务器接受客户端一句话
1236 | /*receiveStream();*/
1237 | } catch (SocketException e) {
1238 | System.out.println("服务器终断了!!!");
1239 | } catch (Exception e) {
1240 | e.printStackTrace();
1241 | }
1242 | }
1243 | /**
1244 | * 服务器停止代码
1245 | * */
1246 |
1247 | /**
1248 | * 服务器接受数据的方法(客户端传送一句话),不适用多个客户端进行通话
1249 | * */
1250 | /*public void receiveStream(){
1251 | try {
1252 | dataInputStream = new DataInputStream(socket.getInputStream());
1253 | String str = dataInputStream.readUTF();
1254 | System.out.println(str);
1255 | serverTa.append(str);
1256 | }catch (Exception e){
1257 | e.printStackTrace();
1258 | }
1259 | }*/
1260 |
1261 | /**
1262 | * @author 武新宇
1263 | * @deprecated 内部类声明 对象 这个对象是属于服务器端的一个连接对象
1264 | */
1265 | class ClientCoon implements Runnable {
1266 | Socket socket = null;
1267 |
1268 | public ClientCoon(Socket socket) {
1269 | this.socket = socket;
1270 | /**
1271 | * 线程启动在这里:
1272 | * 初始化方法里 初始化一个线程 ,线程中封装的是自己,做整个线程的调用
1273 | */
1274 | (new Thread(this)).start();
1275 | }
1276 |
1277 | //接受客户端信息(多线程run()方法)
1278 | @Override
1279 | public void run() {
1280 | try {
1281 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
1282 | // 为了让服务器能够接受到每个客户端的多句话
1283 | while (isStart) {
1284 | //readUTF()是一种阻塞方法,接一句就执行完了,所以循环中
1285 | String str = dataInputStream.readUTF();
1286 | System.out.println("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
1287 | serverTa.append("\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n");
1288 | //服务器向每个客户端发送别的客户端发来的信息
1289 | // 遍历ccList,调用send方法,在客户端里接受应该是多线程的接受
1290 | String strSend = "\n" + socket.getInetAddress() + "|" + socket.getPort() + "说" + str + "\n";
1291 | Iterator iterator = ccList.iterator();
1292 | while (iterator.hasNext()) {
1293 | ClientCoon clientCoon = iterator.next();
1294 | clientCoon.send(strSend);
1295 | }
1296 | }
1297 | } catch (Exception e) {
1298 | e.printStackTrace();
1299 | }
1300 | }
1301 |
1302 | // 服务器向每個连接对象发送数据的方法
1303 | public void send(String str) {
1304 | try {
1305 | DataOutputStream dataOutputStream = new DataOutputStream(this.socket.getOutputStream());
1306 | dataOutputStream.writeUTF(str);
1307 | } catch (SocketException e) {
1308 | e.printStackTrace();
1309 | } catch (IOException e) {
1310 | e.printStackTrace();
1311 | }
1312 | }
1313 | }
1314 |
1315 |
1316 | }
1317 |
1318 |
1319 |
1320 |
1321 | ```
1322 |
1323 |
1324 |
1325 | #### 客户端:
1326 |
1327 | ```java
1328 | //客户端连接上服务器判断符号
1329 | private boolean isConn =false;
1330 | ```
1331 |
1332 | ```java
1333 | /**
1334 | * @author 武新宇
1335 | * @deprecated 多线程的类,实现了Runnable接口的类
1336 | */
1337 | class Receive implements Runnable {
1338 | @Override
1339 | public void run() {
1340 | try {
1341 | while (isConn) {
1342 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
1343 | String str = dataInputStream.readUTF();
1344 | ta.append(str);
1345 | }
1346 | } catch (SocketException e) {
1347 | System.out.println("服务器意外终止了!");
1348 | ta.append("服务器意外终止了!");
1349 | } catch (IOException e) {
1350 | // TODO Auto-generated catch block
1351 | e.printStackTrace();
1352 | }
1353 | }
1354 | }
1355 | ```
1356 |
1357 | ```java
1358 | try {
1359 | socket = new Socket(CONNSTR, CONNPORT);
1360 | isConn = true;
1361 | } catch (Exception e) {
1362 | e.printStackTrace();
1363 | }
1364 |
1365 | // 启动多线程
1366 | new Thread(new Receive()).start();
1367 | ```
1368 |
1369 | **ClientChat.java**
1370 |
1371 | ```java
1372 | package com.wxy;
1373 |
1374 | import javax.swing.*;
1375 | import java.awt.*;
1376 | import java.awt.event.ActionEvent;
1377 | import java.awt.event.ActionListener;
1378 | import java.io.DataInputStream;
1379 | import java.io.DataOutputStream;
1380 | import java.io.IOException;
1381 | import java.net.Socket;
1382 | import java.net.SocketException;
1383 |
1384 | /**
1385 | * @author Administrator
1386 | * @Auther: wuxy
1387 | * @Date: 2021/3/2 - 03 - 02 - 20:16
1388 | * @Description: com.wxy
1389 | * @version: 1.0
1390 | */
1391 | public class ClientChat {
1392 | //这是一个main方法,是程序的入口:
1393 | public static void main(String[] args) {
1394 | ClientJframe clientJframe = new ClientJframe();
1395 | clientJframe.init();
1396 | }
1397 | }
1398 |
1399 | class ClientJframe extends JFrame {
1400 | //GUI布局
1401 | //聊天记录显示区
1402 | private JTextArea ta = new JTextArea(10, 20);
1403 | //聊天记录输入区
1404 | private JTextField tf = new JTextField(20);
1405 |
1406 | //端口
1407 | // 静态常量主机端口号
1408 | private static final String CONNSTR = "127.0.0.1";
1409 | // 静态常量服务器端口号
1410 | private static final int CONNPORT = 8888;
1411 | private Socket socket = null;
1412 |
1413 | //Client发送数据
1414 | private DataOutputStream dataOutputStream = null;
1415 |
1416 | //客户端连接上服务器判断符号
1417 | private boolean isConn = false;
1418 |
1419 | /**
1420 | * 无参的构造方法 throws HeadlessException
1421 | */
1422 | public ClientJframe() throws HeadlessException {
1423 | super();
1424 | }
1425 |
1426 | public void init() {
1427 | this.setTitle("客户端窗口");
1428 | this.add(ta, BorderLayout.CENTER);
1429 | this.add(tf, BorderLayout.SOUTH);
1430 |
1431 | this.setBounds(300, 300, 400, 400);
1432 |
1433 | // 添加监听,使回车键可以输入数据(判断数据合法性),
1434 | // 并輸入到聊天框,换行
1435 | tf.addActionListener(new ActionListener() {
1436 |
1437 | @Override
1438 | public void actionPerformed(ActionEvent e) {
1439 | String strSend = tf.getText();
1440 | // 去掉空格判断长度是否为空
1441 | if (strSend.trim().length() == 0) {
1442 | return;
1443 | }
1444 | //客户端信息strSend发送到服务器上
1445 | send(strSend);
1446 | tf.setText("");
1447 | ta.append(strSend + "\n");
1448 |
1449 | }
1450 | });
1451 |
1452 | //关闭事件
1453 | this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
1454 | ta.setEditable(false);//聊天区域不可以输入
1455 | tf.requestFocus();//光标聚焦
1456 |
1457 | try {
1458 | socket = new Socket(CONNSTR, CONNPORT);
1459 | isConn = true;
1460 | } catch (Exception e) {
1461 | e.printStackTrace();
1462 | }
1463 |
1464 | // 启动多线程
1465 | new Thread(new Receive()).start();
1466 |
1467 | this.setVisible(true);
1468 | }
1469 |
1470 | /**
1471 | * 客户端发送信息到服务器上的方法
1472 | */
1473 | public void send(String str) {
1474 | try {
1475 | dataOutputStream = new DataOutputStream(socket.getOutputStream());
1476 | dataOutputStream.writeUTF(str);
1477 | } catch (IOException e) {
1478 | // TODO Auto-generated catch block
1479 | e.printStackTrace();
1480 | }
1481 | }
1482 |
1483 | /**
1484 | * @author 武新宇
1485 | * @deprecated 多线程的类,实现了Runnable接口的类
1486 | */
1487 | class Receive implements Runnable {
1488 | @Override
1489 | public void run() {
1490 | try {
1491 | while (isConn) {
1492 | DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
1493 | String str = dataInputStream.readUTF();
1494 | //通讯消息
1495 | ta.append(str);
1496 | }
1497 | } catch (SocketException e) {
1498 | System.out.println("服务器意外终止了!");
1499 | ta.append("服务器意外终止了!");
1500 | } catch (IOException e) {
1501 | // TODO Auto-generated catch block
1502 | e.printStackTrace();
1503 | }
1504 | }
1505 | }
1506 | }
1507 | ```
1508 |
1509 | 
--------------------------------------------------------------------------------