-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClient.java
More file actions
366 lines (341 loc) · 10.8 KB
/
Client.java
File metadata and controls
366 lines (341 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
package com.figer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class Client{
private JFrame frame;
private JList userList;
private JTextArea textArea;
private JTextField textField;
private JTextField txt_port;
private JTextField txt_hostIp;
private JTextField txt_name;
private JButton btn_start;
private JButton btn_stop;
private JButton btn_send;
private JPanel northPanel;
private JPanel southPanel;
private JScrollPane rightScroll;
private JScrollPane leftScroll;
private JSplitPane centerSplit;
private DefaultListModel listModel;
private boolean isConnected = false;
private Socket socket;
private PrintWriter writer;
private BufferedReader reader;
private MessageThread messageThread;// 负责接收消息的线程
private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有在线用户
// 主方法,程序入口
public static void main(String[] args) {
new Client();
}
// 执行发送
public void send() {
if (!isConnected) {
JOptionPane.showMessageDialog(frame, "还没有连接服务器,无法发送消息!", "错误",
JOptionPane.ERROR_MESSAGE);
return;
}
String message = textField.getText().trim();
if (message == null || message.equals("")) {
JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误",
JOptionPane.ERROR_MESSAGE);
return;
}
sendMessage(frame.getTitle() + "@" + "ALL" + "@" + message);
textField.setText(null);
}
// 构造方法
public Client() {
textArea = new JTextArea();
textArea.setEditable(false);
textArea.setForeground(Color.blue);
textField = new JTextField();
txt_port = new JTextField("6666");
txt_hostIp = new JTextField("127.0.0.1");
txt_name = new JTextField("xiaoqiang");
btn_start = new JButton("连接");
btn_stop = new JButton("断开");
btn_send = new JButton("发送");
listModel = new DefaultListModel();
userList = new JList(listModel);
northPanel = new JPanel();
northPanel.setLayout(new GridLayout(1, 7));
northPanel.add(new JLabel("端口"));
northPanel.add(txt_port);
northPanel.add(new JLabel("服务器IP"));
northPanel.add(txt_hostIp);
northPanel.add(new JLabel("姓名"));
northPanel.add(txt_name);
northPanel.add(btn_start);
northPanel.add(btn_stop);
northPanel.setBorder(new TitledBorder("连接信息"));
rightScroll = new JScrollPane(textArea);
rightScroll.setBorder(new TitledBorder("消息显示区"));
leftScroll = new JScrollPane(userList);
leftScroll.setBorder(new TitledBorder("在线用户"));
southPanel = new JPanel(new BorderLayout());
southPanel.add(textField, "Center");
southPanel.add(btn_send, "East");
southPanel.setBorder(new TitledBorder("写消息"));
centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll,
rightScroll);
centerSplit.setDividerLocation(100);
frame = new JFrame("客户机");
// 更改JFrame的图标:
frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png")));
frame.setLayout(new BorderLayout());
frame.add(northPanel, "North");
frame.add(centerSplit, "Center");
frame.add(southPanel, "South");
frame.setSize(600, 400);
int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width;
int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height;
frame.setLocation((screen_width - frame.getWidth()) / 2,
(screen_height - frame.getHeight()) / 2);
frame.setVisible(true);
// 写消息的文本框中按回车键时事件
textField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
send();
}
});
// 单击发送按钮时事件
btn_send.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
send();
}
});
// 单击连接按钮时事件
btn_start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int port;
if (isConnected) {
JOptionPane.showMessageDialog(frame, "已处于连接上状态,不要重复连接!",
"错误", JOptionPane.ERROR_MESSAGE);
return;
}
try {
try {
port = Integer.parseInt(txt_port.getText().trim());
} catch (NumberFormatException e2) {
throw new Exception("端口号不符合要求!端口为整数!");
}
String hostIp = txt_hostIp.getText().trim();
String name = txt_name.getText().trim();
if (name.equals("") || hostIp.equals("")) {
throw new Exception("姓名、服务器IP不能为空!");
}
boolean flag = connectServer(port, hostIp, name);
if (flag == false) {
throw new Exception("与服务器连接失败!");
}
frame.setTitle(name);
JOptionPane.showMessageDialog(frame, "成功连接!");
} catch (Exception exc) {
JOptionPane.showMessageDialog(frame, exc.getMessage(),
"错误", JOptionPane.ERROR_MESSAGE);
}
}
});
// 单击断开按钮时事件
btn_stop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (!isConnected) {
JOptionPane.showMessageDialog(frame, "已处于断开状态,不要重复断开!",
"错误", JOptionPane.ERROR_MESSAGE);
return;
}
try {
boolean flag = closeConnection();// 断开连接
if (flag == false) {
throw new Exception("断开连接发生异常!");
}
JOptionPane.showMessageDialog(frame, "成功断开!");
} catch (Exception exc) {
JOptionPane.showMessageDialog(frame, exc.getMessage(),
"错误", JOptionPane.ERROR_MESSAGE);
}
}
});
// 关闭窗口时事件
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (isConnected) {
closeConnection();// 关闭连接
}
System.exit(0);// 退出程序
}
});
}
/**
* 连接服务器
*
* @param port
* @param hostIp
* @param name
*/
public boolean connectServer(int port, String hostIp, String name) {
// 连接服务器
try {
socket = new Socket(hostIp, port);// 根据端口号和服务器ip建立连接
writer = new PrintWriter(socket.getOutputStream());
reader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
// 发送客户端用户基本信息(用户名和ip地址)
sendMessage(name + "@" + socket.getLocalAddress().toString());
// 开启接收消息的线程
messageThread = new MessageThread(reader, textArea);
messageThread.start();
isConnected = true;// 已经连接上了
return true;
} catch (Exception e) {
textArea.append("与端口号为:" + port + " IP地址为:" + hostIp
+ " 的服务器连接失败!" + "\r\n");
isConnected = false;// 未连接上
return false;
}
}
/**
* 发送消息
*
* @param message
*/
public void sendMessage(String message) {
writer.println(message);
writer.flush();
}
/**
* 客户端主动关闭连接
*/
@SuppressWarnings("deprecation")
public synchronized boolean closeConnection() {
try {
sendMessage("CLOSE");// 发送断开连接命令给服务器
messageThread.stop();// 停止接受消息线程
// 释放资源
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
socket.close();
}
isConnected = false;
return true;
} catch (IOException e1) {
e1.printStackTrace();
isConnected = true;
return false;
}
}
// 不断接收消息的线程
class MessageThread extends Thread {
private BufferedReader reader;
private JTextArea textArea;
// 接收消息线程的构造方法
public MessageThread(BufferedReader reader, JTextArea textArea) {
this.reader = reader;
this.textArea = textArea;
}
// 被动的关闭连接
public synchronized void closeCon() throws Exception {
// 清空用户列表
listModel.removeAllElements();
// 被动的关闭连接释放资源
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
if (socket != null) {
socket.close();
}
isConnected = false;// 修改状态为断开
}
public void run() {
String message = "";
while (true) {
try {
message = reader.readLine();
StringTokenizer stringTokenizer = new StringTokenizer(
message, "/@");
String command = stringTokenizer.nextToken();// 命令
if (command.equals("CLOSE"))// 服务器已关闭命令
{
textArea.append("服务器已关闭!\r\n");
closeCon();// 被动的关闭连接
return;// 结束线程
} else if (command.equals("ADD")) {// 有用户上线更新在线列表
String username = "";
String userIp = "";
if ((username = stringTokenizer.nextToken()) != null
&& (userIp = stringTokenizer.nextToken()) != null) {
User user = new User(username, userIp);
onLineUsers.put(username, user);
listModel.addElement(username);
}
} else if (command.equals("DELETE")) {// 有用户下线更新在线列表
String username = stringTokenizer.nextToken();
User user = (User) onLineUsers.get(username);
onLineUsers.remove(user);
listModel.removeElement(username);
} else if (command.equals("USERLIST")) {// 加载在线用户列表
int size = Integer
.parseInt(stringTokenizer.nextToken());
String username = null;
String userIp = null;
for (int i = 0; i < size; i++) {
username = stringTokenizer.nextToken();
userIp = stringTokenizer.nextToken();
User user = new User(username, userIp);
onLineUsers.put(username, user);
listModel.addElement(username);
}
} else if (command.equals("MAX")) {// 人数已达上限
textArea.append(stringTokenizer.nextToken()
+ stringTokenizer.nextToken() + "\r\n");
closeCon();// 被动的关闭连接
JOptionPane.showMessageDialog(frame, "服务器缓冲区已满!", "错误",
JOptionPane.ERROR_MESSAGE);
return;// 结束线程
} else {// 普通消息
textArea.append(message + "\r\n");
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}