关于聊天室中客户端部分
整体思路
客户端的代码用到的类如上所示,其中 entity 中的两个类仅用于界面,所以不会进行介绍。
Thread
客户端线程,一个线程表示一个用户,处理服务器发来的消息,在里面用了 currentFrame 这个变量来表示当前窗口。
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
//客户端线程 监听服务器发送过来的信息
public class Thread extends Thread {
//当前窗体
private JFrame currentFrame;
public ClientThread(JFrame frame){
currentFrame = frame;
}
public void run() {
try {
//客户端与服务器处于连接状态
while (DataBuffer.clientSeocket.isConnected()) {
//从客户端得输入流读取服务器响应信息
Response response = (Response) DataBuffer.ois.readObject();
ResponseType type = response.getType();
System.out.println("获取了响应内容:" + type);
if (type == ResponseType.LOGIN) {//用户登录
User newUser = (User)response.getData("loginUser"); //添加到在线用户列表
DataBuffer.onlineUserListModel.addElement(newUser);
//在服务器端重新打印在线用户信息
ChatFrame.onlineCountLbl.setText(
"在线用户列表("+ DataBuffer.onlineUserListModel.getSize() +")");
ClientUtil.appendTxt2MsgListArea("【系统消息】用户"+newUser.getNickname() + "上线了!\n"); //在客户端页面提示用户上线信息
}else if(type == ResponseType.LOGOUT){ //用户退出
User newUser = (User)response.getData("logoutUser"); //从在线用户列表删除
DataBuffer.onlineUserListModel.removeElement(newUser);
//在服务器端重新打印在线用户信息
ChatFrame.onlineCountLbl.setText(
"在线用户列表("+ DataBuffer.onlineUserListModel.getSize() +")");
ClientUtil.appendTxt2MsgListArea("【系统消息】用户"+newUser.getNickname() + "下线了!\n"); //在客户端聊天界面提示用户下线
}else if(type == ResponseType.CHAT){ //聊天
Message msg = (Message)response.getData("txtMsg");
ClientUtil.appendTxt2MsgListArea(msg.getMessage());
}else if(type == ResponseType.TOSENDFILE){ //准备发送文件
toSendFile(response);
}else if(type == ResponseType.AGREERECEIVEFILE){ //对方同意接收文件
sendFile(response);
}else if(type == ResponseType.REFUSERECEIVEFILE){ //对方拒绝接收文件
ClientUtil.appendTxt2MsgListArea("【文件消息】对方拒绝接收,文件发送失败!\n");
}else if(type == ResponseType.RECEIVEFILE){ //开始接收文件
receiveFile(response);
}else if(type == ResponseType.BOARD){ //服务器发送广播消息
Message msg = (Message)response.getData("txtMsg");
ClientUtil.appendTxt2MsgListArea(msg.getMessage());
}else if(type == ResponseType.REMOVE){ //服务器剔除用户
ChatFrame.remove();
}
}
} catch (IOException e) {
//e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//发送文件
private void sendFile(Response response) {
//创建待发送文件对象
final FileInfo sendFile = (FileInfo)response.getData("sendFile");
//输入输出缓冲字节流
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
Socket socket = null;
try {
//套接字连接
socket = new Socket(sendFile.getDestIp(),sendFile.getDestPort());
//文件读入
bis = new BufferedInputStream(new FileInputStream(sendFile.getSrcName()));
//文件写出
bos = new BufferedOutputStream(socket.getOutputStream());
//写入缓冲区
byte[] buffer = new byte[1024];
int n = -1;
while ((n = bis.read(buffer)) != -1){
bos.write(buffer, 0, n);
}
bos.flush();
synchronized (this) {
//提示消息
ClientUtil.appendTxt2MsgListArea("【文件消息】文件发送完毕!\n");
}
} catch (IOException e) {
e.printStackTrace();
}finally{
IOUtil.close(bis,bos);
SocketUtil.close(socket);
}
}
//接收文件
private void receiveFile(Response response) {
//创建待发送文件对象
final FileInfo sendFile = (FileInfo)response.getData("sendFile");
//输入输出缓冲字节流
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(sendFile.getDestPort());
//接收
socket = serverSocket.accept();
//缓冲读
bis = new BufferedInputStream(socket.getInputStream());
//缓冲写出
bos = new BufferedOutputStream(new FileOutputStream(sendFile.getDestName()));
byte[] buffer = new byte[1024];
int n = -1;
while ((n = bis.read(buffer)) != -1){
bos.write(buffer, 0, n);
}
bos.flush();
synchronized (this) {
//提示信息
ClientUtil.appendTxt2MsgListArea("【文件消息】文件接收完毕!存放在["
+ sendFile.getDestName()+"]\n");
}
} catch (IOException e) {
e.printStackTrace();
}finally{
IOUtil.close(bis,bos);
SocketUtil.close(socket);
SocketUtil.close(serverSocket);
}
}
// 准备发送文件
private void toSendFile(Response response) {
FileInfo sendFile = (FileInfo)response.getData("sendFile");
//获取发送者昵称
String fromName = sendFile.getFromUser().getNickname()
+ "(" + sendFile.getFromUser().getId() + ")";
//获取文件名称
String fileName = sendFile.getSrcName()
.substring(sendFile.getSrcName().lastIndexOf(File.separator)+1);
//弹出提示窗口 获得用户的选择
int select = JOptionPane.showConfirmDialog(this.currentFrame,
fromName + " 向您发送文件 [" + fileName+ "]!\n同意接收吗?",
"接收文件", JOptionPane.YES_NO_OPTION);
try {
Request request = new Request();
request.setAttribute("sendFile", sendFile);
//用户同意接受文件
if (select == JOptionPane.YES_OPTION) {
//选择接收文件的存放地址
JFileChooser jfc = new JFileChooser();
jfc.setSelectedFile(new File(fileName));
//地址选择结果
int result = jfc.showSaveDialog(this.currentFrame);
//地址没有问题
if (result == JFileChooser.APPROVE_OPTION){
//设置目的地文件名
sendFile.setDestName(jfc.getSelectedFile().getCanonicalPath());
//设置目标地的IP和接收文件的端口
sendFile.setDestIp(DataBuffer.ip);
sendFile.setDestPort(DataBuffer.RECEIVE_FILE_PORT);
request.setAction("agreeReceiveFile");
ClientUtil.appendTxt2MsgListArea("【文件消息】您已同意接收来自 "
+ fromName +" 的文件,正在接收文件 ...\n");
} else {//地址选择有误或未选择地址
request.setAction("refuseReceiveFile");
ClientUtil.appendTxt2MsgListArea("【文件消息】您已拒绝接收来自 "
+ fromName +" 的文件!\n");
}
} else {//拒绝接受文件
request.setAction("refuseReceiveFile");
ClientUtil.appendTxt2MsgListArea("【文件消息】您已拒绝接收来自 "
+ fromName +" 的文件!\n");
}
ClientUtil.sendTextRequest2(request);
} catch (IOException e) {
e.printStackTrace();
}
}
}
ClientUtil
用于客户端向服务器发送消息。
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
//客户端发送请求到服务器的工具
public class ClientUtil {
//发送请求对象,主动接收响应
public static Response sendTextRequest(Request request) throws IOException {
Response response = null;
try {
//发送请求
DataBuffer.oos.writeObject(request);
DataBuffer.oos.flush();
System.out.println("客户端发送了请求对象:" + request.getAction());
if (!"exit".equals(request.getAction())) {
// 获取响应
response = (Response) DataBuffer.ois.readObject();
System.out.println("客户端获取到了响应对象:" + response.getStatus());
} else {
System.out.println("客户端断开连接");
}
} catch (IOException e) {
throw e;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return response;
}
//发送请求对象,不主动接收响应
public static void sendTextRequest2(Request request) throws IOException {
try {
DataBuffer.oos.writeObject(request); // 发送请求
DataBuffer.oos.flush();
System.out.println("客户端发送了请求对象:" + request.getAction());
} catch (IOException e) {
throw e;
}
}
//把指定文本添加到消息列表文本域中
public static void appendTxt2MsgListArea(String txt) {
ChatFrame.msgListArea.append(txt);
//把光标定位到文本域的最后一行
ChatFrame.msgListArea.setCaretPosition(ChatFrame.msgListArea.getDocument().getLength());
}
}
DataBuffer
用于客户端从文件中读取数据,进行缓存。
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
public class DataBuffer {
//当前客户端的用户信息
public static User currentUser;
//在线用户列表
public static List<User> onlineUsers;
//当前客户端连接到服务器的socket
public static Socket clientSeocket;
//当前客户端连接到服务器的输出流
public static ObjectOutputStream oos;
//当前客户端连接到服务器的输入流
public static ObjectInputStream ois;
//服务器配置参数属性集
public static Properties configProp;
// 当前客户端的屏幕尺寸
public static Dimension screenSize;
//本客户端的IP地址
public static String ip ;
//用来接收文件的端口
public static final int RECEIVE_FILE_PORT = 6667;
// 在线用户JList的Model
public static OnlineUserListModel onlineUserListModel;
static{
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
//加载服务器配置文件
configProp = new Properties();
try {
//获取本地IP地址
ip = InetAddress.getLocalHost().getHostAddress();
//从输入流中读取属性列表(键和元素对)
configProp.load(Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("serverconfig.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
private DataBuffer(){}
}
ChatGUI
聊天界面大致形状
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
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
public class ChatGUI extends JFrame {
private static final long serialVersionUID = -3426717670093483287L;
//聊天对方的信息Label
private JLabel otherInfoLbl;
//当前用户信息Lbl
private JLabel currentUserLbl;
//聊天信息列表区域
public static JTextArea msgListArea;
//要发送的信息区域
public static JTextArea sendArea;
//在线用户列表
public static JList onlineList;
// 在线用户数统计Lbl
public static JLabel onlineCountLbl;
//准备发送文件
public static FileInfo sendFile;
//私聊复选框
public JCheckBox rybqBtn;
public ChatFrame(){
this.init();
//调用任意已注册 WindowListener 的对象后自动隐藏并释放该窗体。
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.setVisible(true);
}
//初始化
public void init(){
this.setTitle("MY CHART ROOM");
this.setSize(550, 500);
this.setResizable(false);
//设置默认窗体在屏幕中央
int x = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int y = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
this.setLocation((x - this.getWidth()) / 2, (y-this.getHeight())/ 2);
//左边用户面板
JPanel userPanel = new JPanel();
userPanel.setLayout(new BorderLayout());
//右边主面板
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
// 创建一个分隔窗格
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
userPanel, mainPanel);
splitPane.setDividerLocation(125);
splitPane.setDividerSize(10);
splitPane.setOneTouchExpandable(true);
this.add(splitPane, BorderLayout.CENTER);
//在线用户列表展示
JPanel onlineListPane = new JPanel();
onlineListPane.setLayout(new BorderLayout());
onlineCountLbl = new JLabel("在线用户");
onlineListPane.add(onlineCountLbl, BorderLayout.NORTH);
//当前用户面板
JPanel currentUserPane = new JPanel();
currentUserPane.setLayout(new BorderLayout());
Border border = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
currentUserPane.setBorder(BorderFactory.createTitledBorder(border,
"当前用户", TitledBorder.LEFT,TitledBorder.TOP));
this.add(currentUserPane, BorderLayout.NORTH);
// 右边用户列表创建一个分隔窗格
JSplitPane splitPane3 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
currentUserPane, onlineListPane);
splitPane3.setDividerLocation(60);
splitPane3.setDividerSize(1);
userPanel.add(splitPane3, BorderLayout.CENTER);
//获取在线用户并缓存
DataBuffer.onlineUserListModel = new OnlineUserListModel(DataBuffer.onlineUsers);
//在线用户列表
onlineList = new JList(DataBuffer.onlineUserListModel);
onlineList.setCellRenderer(new MyCellRenderer());
//设置为单选模式
onlineList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
onlineListPane.add(new JScrollPane(onlineList,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
//当前用户信息Label
currentUserLbl = new JLabel();
currentUserPane.add(currentUserLbl);
//右上方信息显示面板
JPanel infoPanel = new JPanel();
infoPanel.setLayout(new BorderLayout());
//右下方发送消息面板
JPanel sendPanel = new JPanel();
sendPanel.setLayout(new BorderLayout());
// 创建一个分隔窗格
JSplitPane splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
infoPanel, sendPanel);
splitPane2.setDividerLocation(300);
splitPane2.setDividerSize(1);
mainPanel.add(splitPane2, BorderLayout.CENTER);
otherInfoLbl = new JLabel("当前状态:群聊中...");
infoPanel.add(otherInfoLbl, BorderLayout.NORTH);
msgListArea = new JTextArea();
msgListArea.setLineWrap(true);
//给信息窗口添加滚动条
infoPanel.add(new JScrollPane(msgListArea,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
JPanel tempPanel = new JPanel();
tempPanel.setLayout(new BorderLayout());
sendPanel.add(tempPanel, BorderLayout.NORTH);
// 聊天按钮面板
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
tempPanel.add(btnPanel, BorderLayout.CENTER);
//字体按钮
JButton fontBtn = new JButton(new ImageIcon("images/font.png"));
fontBtn.setMargin(new Insets(0,0,0,0));
fontBtn.setToolTipText("设置字体和格式");
btnPanel.add(fontBtn);
//表情按钮
JButton faceBtn = new JButton(new ImageIcon("images/sendFace.png"));
faceBtn.setMargin(new Insets(0,0,0,0));
faceBtn.setToolTipText("选择表情");
btnPanel.add(faceBtn);
//发送文件按钮
JButton sendFileBtn = new JButton(new ImageIcon("images/sendPic.png"));
sendFileBtn.setMargin(new Insets(0,0,0,0));
sendFileBtn.setToolTipText("向对方发送文件");
btnPanel.add(sendFileBtn);
//私聊按钮
rybqBtn = new JCheckBox("私聊");
tempPanel.add(rybqBtn, BorderLayout.EAST);
//要发送的信息的区域
sendArea = new JTextArea();
sendArea.setLineWrap(true);
sendPanel.add(new JScrollPane(sendArea,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
// 聊天按钮面板
JPanel btn2Panel = new JPanel();
btn2Panel.setLayout(new FlowLayout(FlowLayout.RIGHT));
this.add(btn2Panel, BorderLayout.SOUTH);
JButton closeBtn = new JButton("关闭");
closeBtn.setToolTipText("退出程序");
btn2Panel.add(closeBtn);
JButton submitBtn = new JButton("发送");
submitBtn.setToolTipText("按Enter键发送消息");
btn2Panel.add(submitBtn);
sendPanel.add(btn2Panel, BorderLayout.SOUTH);
/*-------注册事件监听器--------*/
//关闭窗口
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
logout();
}
});
//关闭按钮的事件
closeBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
logout();
}
});
//选择某个用户私聊
rybqBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
if(rybqBtn.isSelected()){
User selectedUser = (User)onlineList.getSelectedValue();
if(null == selectedUser){
otherInfoLbl.setText("当前状态:私聊(从在线用户列表中选择某个用户进行私聊)...");
}else if(DataBuffer.currentUser.getId() == selectedUser.getId()){
otherInfoLbl.setText("警告:不允许和自己私聊!!!");
}else{
otherInfoLbl.setText("当前状态:与 "+ selectedUser.getNickname()
+"(" + selectedUser.getId() + ") 私聊中...");
}
}else{
otherInfoLbl.setText("当前状态:群聊...");
}
}
});
//选择某个用户
onlineList.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
User selectedUser = (User)onlineList.getSelectedValue();
if(rybqBtn.isSelected()){
if(DataBuffer.currentUser.getId() == selectedUser.getId()){
otherInfoLbl.setText("警告:不允许和自己私聊!!!");
}else{
otherInfoLbl.setText("当前状态:与 "+ selectedUser.getNickname()
+"(" + selectedUser.getId() + ") 私聊中...");
}
}
}
});
//发送文本消息
//回车发送
sendArea.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e){
if(e.getKeyCode() == KeyEvent.VK_ENTER){
sendTxtMsg();
}
}
});
//点击按钮发送
submitBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
sendTxtMsg();
}
});
//发送文件
sendFileBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
sendFile();
}
});
this.loadData(); //加载初始数据
}
//加载数据
public void loadData(){
//加载当前用户数据
if(null != DataBuffer.currentUser){
//头像
currentUserLbl.setIcon(
new ImageIcon("images/" + DataBuffer.currentUser.getHead() + ".png"));
//用户昵称和账号
currentUserLbl.setText(DataBuffer.currentUser.getNickname()
+ "(" + DataBuffer.currentUser.getId() + ")");
}
//设置在线用户列表
onlineCountLbl.setText("在线用户列表("+ DataBuffer.onlineUserListModel.getSize() +")");
//启动监听服务器消息的线程
new ClientThread(this).start();
}
//关闭客户端
private void logout() {
//弹出提示窗口
int select = JOptionPane.showConfirmDialog(ChatFrame.this,
"确定要退出吗?\n\n退出程序将会中断与服务器的连接!", "退出聊天室",
JOptionPane.YES_NO_OPTION);
//选择退出
if (select == JOptionPane.YES_OPTION) {
//创建请求对象
Request req = new Request();
req.setAction("exit");
req.setAttribute("user", DataBuffer.currentUser);
try {
//发送请求
ClientUtil.sendTextRequest(req);
} catch (IOException ex) {
ex.printStackTrace();
}finally{
System.exit(0);
}
}else{
// 未选择退出 不响应
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
}
}
//服务器踢除用户
public static void remove() {
int select = JOptionPane.showConfirmDialog(sendArea,
"你已被踢出聊天室!\n\n", "系统通知",
JOptionPane.YES_NO_OPTION);
//创建请求对象 等同于用户退出
Request req = new Request();
req.setAction("exit");
req.setAttribute("user", DataBuffer.currentUser);
try {
ClientUtil.sendTextRequest(req);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
System.exit(0);
}
}
//发送文本消息
public void sendTxtMsg(){
String content = sendArea.getText();
if ("".equals(content)) { //无内容
JOptionPane.showMessageDialog(ChatFrame.this, "不能发送空消息!",
"不能发送", JOptionPane.ERROR_MESSAGE);
} else { //发送
User selectedUser = (User)onlineList.getSelectedValue();
//如果设置了ToUser 表示私聊,否则群聊
Message msg = new Message();
if(rybqBtn.isSelected()){ //私聊
if(null == selectedUser){//私聊对象为空
JOptionPane.showMessageDialog(ChatFrame.this, "没有选择私聊对象!",
"不能发送", JOptionPane.ERROR_MESSAGE);
return;
}else if (DataBuffer.currentUser.getId() == selectedUser.getId()){//私聊对象为自己
JOptionPane.showMessageDialog(ChatFrame.this, "不能给自己发送消息!",
"不能发送", JOptionPane.ERROR_MESSAGE);
return;
}else{
msg.setToUser(selectedUser);
}
}
//获取系统时间
msg.setFromUser(DataBuffer.currentUser);
msg.setSendTime(new Date());
DateFormat df = new SimpleDateFormat("HH:mm:ss");
//存储消息的相关信息
StringBuffer sb = new StringBuffer();
sb.append(" ").append(df.format(msg.getSendTime())).append(" ")
.append(msg.getFromUser().getNickname())
.append("(").append(msg.getFromUser().getId()).append(") ");
if(!this.rybqBtn.isSelected()){ //群聊
sb.append("对大家说");
}
sb.append("\n ").append(content).append("\n");
msg.setMessage(sb.toString());
//创建请求对象 存储消息信息
Request request = new Request();
request.setAction("chat");
request.setAttribute("msg", msg);
try {
//发送请求
ClientUtil.sendTextRequest2(request);
} catch (IOException e) {
e.printStackTrace();
}
//JTextArea 中发送消息后,清空内容并回到首行
InputMap inputMap = sendArea.getInputMap();
ActionMap actionMap = sendArea.getActionMap();
Object transferTextActionKey = "TRANSFER_TEXT";
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),transferTextActionKey);
actionMap.put(transferTextActionKey,new AbstractAction() {
private static final long serialVersionUID = 7041841945830590229L;
public void actionPerformed(ActionEvent e) {
sendArea.setText("");
sendArea.requestFocus();
}
});
sendArea.setText("");
ClientUtil.appendTxt2MsgListArea(msg.getMessage());
}
}
//发送文件
private void sendFile() {
User selectedUser = (User)onlineList.getSelectedValue();
if(null != selectedUser){ //选择了发送文件的对象
if(DataBuffer.currentUser.getId() == selectedUser.getId()){
JOptionPane.showMessageDialog(ChatFrame.this, "不能给自己发送文件!",
"不能发送", JOptionPane.ERROR_MESSAGE);
}else{
//选择要发送的文件
JFileChooser jfc = new JFileChooser();
if (jfc.showOpenDialog(ChatFrame.this) == JFileChooser.APPROVE_OPTION) {
File file = jfc.getSelectedFile();
//创建要发送的文件对象 存储文件内容及相关信息
sendFile = new FileInfo();
//设置文件发送者
sendFile.setFromUser(DataBuffer.currentUser);
//设置文件接收者
sendFile.setToUser(selectedUser);
try {
//待发送文件的源地址及文件名
sendFile.setSrcName(file.getCanonicalPath());
} catch (IOException e1) {
e1.printStackTrace();
}
//设置发送时间
sendFile.setSendTime(new Date());
//创建请求对象
Request request = new Request();
request.setAction("toSendFile");
request.setAttribute("file", sendFile);
try {
//发送请求
ClientUtil.sendTextRequest2(request);
} catch (IOException e) {
e.printStackTrace();
}
//打印提示信息
ClientUtil.appendTxt2MsgListArea("【文件消息】向 "
+ selectedUser.getNickname() + "("
+ selectedUser.getId() + ") 发送文件 ["
+ file.getName() + "],等待对方接收...\n");
}
}
}else{
JOptionPane.showMessageDialog(ChatFrame.this, "警告:只能给指定用户发送文件!!!",
"不能发送", JOptionPane.ERROR_MESSAGE);
}
}
}
LoginGUI
登录界面大致如下
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
public class LoginGUI extends JFrame{
private static final long serialVersionUID = -3426717670093483287L;
private JTextField idTxt;
private JPasswordField pwdFld;
public LoginFrame(){
this.init();
setVisible(true);
}
public void init(){
this.setTitle("登录");
this.setSize(430,330);
//设置默认窗口在屏幕中央
int x = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int y = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
this.setLocation((x-this.getWidth())/2,(y-this.getHeight())/2);
//不允许用户改变窗口大小;
this.setResizable(false);
//把logo放在JFrame的上面
Icon icon = new ImageIcon("images/logo.png");
JLabel label = new JLabel(icon);
label.setPreferredSize(new Dimension(430,150));
this.add(label,BorderLayout.NORTH);
//登录信息
JPanel mainPanel = new JPanel();
// 具有“浮雕化”外观效果的边框(效果为凹陷)
Border border = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
mainPanel.setBorder(BorderFactory.createTitledBorder(border,"输入登录信息",TitledBorder.CENTER,TitledBorder.TOP));
this.add(mainPanel,BorderLayout.CENTER);
mainPanel.setLayout(null);
JLabel nameLbl = new JLabel("账号");
nameLbl.setBounds(110,30,70,22);
mainPanel.add(nameLbl);
idTxt = new JTextField();
idTxt.setBounds(150,30,150,22);
idTxt.requestFocusInWindow();//用户名获得焦点
mainPanel.add(idTxt);
JLabel pwdLbl = new JLabel("密码");
pwdLbl.setBounds(110,60,40,22);
mainPanel.add(pwdLbl);
pwdFld = new JPasswordField();
pwdFld.setBounds(150,60,150,22);
mainPanel.add(pwdFld);
//按钮面板放置在JFrame的下面
JPanel btnPanel = new JPanel();
this.add(btnPanel,BorderLayout.SOUTH);
btnPanel.setLayout(new BorderLayout());
btnPanel.setBorder(new EmptyBorder(2,8,4,8));
JButton registenBtn = new JButton("注册");
btnPanel.add(registenBtn,BorderLayout.WEST);
JButton submitBtn = new JButton("登录");
btnPanel.add(submitBtn,BorderLayout.EAST);
//关闭窗口
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e){
Request req = new Request();
req.setAction("exit");
try{
ClientUtil.sendTextRequest(req);
}catch(IOException ex){
ex.printStackTrace();
}finally {
System.exit(0);
}
}
});
//注册
registenBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new RegisterFrame();
}
});
//登录
submitBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
login();
}
});
}
//登录
private void login(){
if(idTxt.getText().length()==0||pwdFld.getPassword().length==0){
JOptionPane.showMessageDialog(LoginFrame.this,
"请输入账号密码!," ,
"输入有误",JOptionPane.ERROR_MESSAGE);
idTxt.requestFocusInWindow();
return;
}
//创建请求
Request req = new Request();
req.setAction("userLogin");
req.setAttribute("id", idTxt.getText());
req.setAttribute("password", new String(pwdFld.getPassword()));
//获取响应
Response response = null;
try {
response = ClientUtil.sendTextRequest(req);
} catch (IOException e1) {
e1.printStackTrace();
}
if(response.getStatus() == ResponseStatus.OK){
//获取当前用户
User user2 = (User)response.getData("user");
if(user2!= null){ //登录成功
DataBuffer.currentUser = user2;
//获取当前在线用户列表
DataBuffer.onlineUsers = (List<User>)response.getData("onlineUsers");
LoginFrame.this.dispose();
new ChatFrame(); //打开聊天窗口
}else{ //登录失败
String str = (String)response.getData("msg");
JOptionPane.showMessageDialog(LoginFrame.this,
str,
"登录失败",JOptionPane.ERROR_MESSAGE);
}
}else{
JOptionPane.showMessageDialog(LoginFrame.this,
"服务器内部错误,请稍后再试!!!","登录失败",JOptionPane.ERROR_MESSAGE);
}
}
}
RegisterGUI
注册界面大致如下
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
public class RegisterGUI extends JFrame{
private static final long serialVersionUID = -768631070458723803L;
private JPasswordField pwdFld;
private JPasswordField pwd2Fld;
private JTextField nickname;
private JComboBox head;
private JRadioButton sex0;
private JRadioButton sex1;
private JButton ok;
private JButton reset;
private JButton cancel;
public RegisterFrame(){
this.init();
setVisible(true);
}
public void init(){
this.setTitle("注册新账号");
setBounds((DataBuffer.screenSize.width - 387)/2,
(DataBuffer.screenSize.height - 267)/2,
387, 267);
getContentPane().setLayout(null);
setResizable(false);
JLabel lable =new JLabel("昵称");//label显示
lable.setBounds(24,35,59,17);
getContentPane().add(lable);
nickname = new JTextField(); //昵称
nickname.setBounds(90, 34, 110, 22);
getContentPane().add(nickname);
JLabel label5 = new JLabel("密码: *");
label5.setBounds(24, 72, 50, 17);
getContentPane().add(label5);
JLabel label3 = new JLabel("确认密码: *");
label3.setBounds(24, 107, 65, 17);
getContentPane().add(label3);
pwdFld = new JPasswordField(); //密码框
pwdFld.setBounds(90, 70, 110, 22);
getContentPane().add(pwdFld);
pwd2Fld = new JPasswordField(); //确认密码框
pwd2Fld.setBounds(90, 105, 110, 22);
getContentPane().add(pwd2Fld);
JLabel label4 = new JLabel("性别:");
label4.setBounds(230, 36, 31, 17);
getContentPane().add(label4);
sex1 = new JRadioButton("女",true); //性别选项
sex1.setBounds (268, 31,44, 25);
getContentPane().add(sex1);
sex0 = new JRadioButton("男");
sex0.setBounds(310, 31, 44, 25);
getContentPane().add(sex0);
ButtonGroup buttonGroup = new ButtonGroup(); //单选按钮组
buttonGroup.add(sex0);
buttonGroup.add(sex1);
JLabel label6 = new JLabel("头像:");
label6.setBounds(230, 72, 31, 17);
getContentPane().add(label6);
head = new JComboBox(); //下拉列表图标
head.setBounds(278, 70, 65, 45);
head.setMaximumRowCount(5);
for (int i = 0; i < 13; i++) {
head.addItem(new ImageIcon("images/" + i + ".png"));
//通过循环添加图片 注意图片名字要取成1,2,3,4,5,等
}
head.setSelectedIndex(0);
getContentPane().add(head);
//按钮
ok = new JButton("确认");
ok.setBounds(27, 176, 60, 28);
getContentPane().add(ok);
reset = new JButton("重填");
reset.setBounds(123, 176, 60, 28);
getContentPane().add(reset);
cancel = new JButton("取消");
cancel.setBounds(268, 176, 60, 28);
getContentPane().add(cancel);
/*---------注册事件监听器----------*/
//取消按钮监听事件处理
cancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent event) {
RegisterFrame.this.dispose();
}
});
//关闭窗口
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e){
RegisterFrame.this.dispose();
}
});
// 重置按钮监听事件处理
reset.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
nickname.setText("");
pwdFld.setText("");
pwd2Fld.setText("");
nickname.requestFocusInWindow(); //用户名获得焦点
}
});
//确认按钮监听事件处理
ok.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
if (pwdFld.getPassword().length==0 || pwd2Fld.getPassword().length==0) {
JOptionPane.showMessageDialog(RegisterFrame.this, "带 “ * ” 为必填内容!");
//判断用户名和密码是否为空
} else if (!new String(pwdFld.getPassword()).equals(new String(pwd2Fld.getPassword()))) {
JOptionPane.showMessageDialog(RegisterFrame.this, "两次输入密码不一致!");
pwdFld.setText("");
pwd2Fld.setText("");
pwdFld.requestFocusInWindow();
//判断两次密码是否一致
} else {
User user = new User(new String(pwdFld.getPassword()),
nickname.getText(),
sex0.isSelected() ? 'm' : 'f',
head.getSelectedIndex());
try {
RegisterFrame.this.regist(user);
} catch (IOException e1) {
e1.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
}
});
}
//注册
private void regist(User user) throws IOException, ClassNotFoundException{
Request request = new Request();
request.setAction("userRegist");
request.setAttribute("user",user);
//获得响应
Response response = ClientUtil.sendTextRequest(request);
ResponseStatus status=response.getStatus();
switch (status){
case OK:
User user2 = (User)response.getData("user");
JOptionPane.showMessageDialog(RegisterFrame.this,
"注册成功,您的账号为 :"+ user2.getId() + ",请牢记!!!",
"注册成功",JOptionPane.INFORMATION_MESSAGE);
this.setVisible(false);
break;
default:
JOptionPane.showMessageDialog(RegisterFrame.this,
"注册失败,请稍后再试!!!",
"服务器内部错误!",
JOptionPane.ERROR_MESSAGE);
}
}
}
运行结果
基本完成,除了不够美观。
个人认为项目的难点是在客户端,之前考虑了很久关于界面的切换,因为涉及到了登陆界面、注册界面、聊天界面,所以如何将客户端的socket与这几个界面联系起来是个值得思考的问题。同时,也思考了好久好友列表的展示方法,最后参考了别人的设计方法。