Java Server and Client 实现聊天室

本文最后更新于:June 1, 2023 9:58 PM

cs的作业,这里放一下吧。肝了两天两夜,要写吐了。

用java socket实现简易聊天室, 因为server端不是我写的,我就发下client端的.

这是几个截图:

下面是源码:

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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
* ChatClient.java
* @version 1.0
* @author Trotyl
* @since 2020 Dec.3
* The client side of a chat program
*/

class ChatClient {
private JFrame connectWindow;//first window
private JTextField typeField;//type field
private JTextArea msgArea;//message area
private Socket mySocket; //socket for connection
private BufferedReader input; //reader for network stream
private PrintWriter output; //printWriter for network output
private boolean running = true; //thread status via boolean
private JLabel connectionError;//label
private boolean isConnected = false;
private JComboBox userList;//list of all online users
private String userName;//current user name
private JTextArea statusArea;//area of all the users online
private JLabel label;
private String selected;//selected user

public static void main(String[] args) {
new ChatClient().getPort();
}

/**
* window for entering the ip and port
*/
public void getPort() {
//setting up the connecting window
connectWindow = new JFrame("connect");
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(400, 250));
JLabel name = new JLabel("Connect");
name.setBounds(170, 20, 70, 20);
panel.setLayout(null);
JLabel hostL = new JLabel("ip ");
JTextField hostT = new JTextField(10);
hostL.setBounds(110, 70, 70, 20);
hostT.setText("127.0.0.1");
hostT.setBounds(170, 70, 150, 20);
JLabel portL = new JLabel("port ");
portL.setBounds(110, 150, 70, 20);
JTextField portT = new JTextField(10);
portT.setText("5000");
portT.setBounds(170, 150, 150, 20);
JButton connectButton = new JButton("connect");
connectButton.setBounds(80, 200, 90, 30);
connectionError = new JLabel("");
connectionError.setForeground(Color.red);
connectionError.setBounds(200, 200, 200, 30);
//adding all the components
panel.add(name);
panel.add(hostL);
panel.add(hostT);
panel.add(portL);
panel.add(portT);
panel.add(connectionError);
panel.add(connectButton);
//exit on close
connectWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
connectWindow.add(panel);
connectWindow.pack();
//press the connect button when press enter
connectWindow.getRootPane().setDefaultButton(connectButton);
//open in the middle of the screen
connectWindow.setLocationRelativeTo(null);
connectWindow.setVisible(true);
connectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String host = hostT.getText();
String port = portT.getText();
//connect
connect(host, port);
hostT.setText("");
portT.setText("");
if (isConnected) {
//open login page
logIn();
}
}
});
}

public Socket connect(String ip, String port) {
System.out.println("Attempting to make a connection..");
//default ip and port are 127.0.0.1 and 5000
try {
int port1 = Integer.parseInt(port);
mySocket = new Socket(ip, port1); //attempt socket connection (local address). This will wait until a connection is made
InputStreamReader stream1 = new InputStreamReader(mySocket.getInputStream()); //Stream for network input
input = new BufferedReader(stream1);
output = new PrintWriter(mySocket.getOutputStream()); //assign printWriter to network stream
connectWindow.dispose();
isConnected = true;
} catch (Exception e) { //connection error occurred
System.out.println("Connection to Server Failed");
connectionError.setText("Wrong ip or port");
}
if (isConnected == true) {
//this is to get rid of the first and second welcome message that disturbs the login
try {
if (input.ready()) {
input.readLine();
}
} catch (IOException ex) {
System.out.println("Failed to receive msg from the server");
}
try {
if (input.ready()) {
input.readLine();
}
} catch (IOException ex) {
System.out.println("Failed to receive msg from the server");
}
}
System.out.println("Connection made.");
return mySocket;
}

/**
* window for login
*/
public void logIn() {
//setting up the log in page
JFrame logInWindow = new JFrame("LogIn");
JPanel panel = new JPanel();
panel.setLayout((new GridLayout(4, 2)));
JLabel passwordL = new JLabel("password");
JTextField passwordT = new JTextField(10);
JLabel userNameL = new JLabel("username");
JTextField userNameT = new JTextField(10);
JButton logInButton = new JButton("log in");
JButton registerButton = new JButton("register");
JLabel logInError = new JLabel("");
JLabel blank = new JLabel("");
logInError.setForeground(Color.red);
//adding all the components
panel.add(userNameT);
panel.add(userNameL);
panel.add(passwordT);
panel.add(passwordL);
panel.add(logInButton);
panel.add(registerButton);
panel.add(logInError);
panel.add(blank);
logInWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
logInWindow.add(BorderLayout.CENTER, panel);
logInWindow.setSize(500, 200);
logInWindow.setLocationRelativeTo(null);
logInWindow.setVisible(true);
logInWindow.getRootPane().setDefaultButton(logInButton);
//when the log in button is pressed
logInButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
userName = userNameT.getText();
String password = passwordT.getText();
//check if username and password is blank
if (userName.equals("") || password.equals("")) {
logInError.setText("username and password can't be blank");
} else {
//pass in the password and username
output.println(userName + ":" + password);
output.flush();
//read message
boolean loop = true;
while (loop) {
try {
if (input.ready()) {
//check for an incoming message
String msg;
msg = input.readLine(); //read the message
if (msg.equals("Username is currently online! Please use another account! \"username:password\"")) {
logInError.setText("user online, try another account");
loop = false;
} else if (msg.equals("Username or password incorrect! Please try again! \"username:password\"")) {
logInError.setText("username of password incorrect");
loop = false;
} else if (msg.startsWith("Welcome")) {//if success
//close the log in window and open the message window
logInWindow.dispose();
loop = false;
startMessaging();
}
}
} catch (IOException ex) {
System.out.println("Failed to receive msg from the server");
}
}
}
}
});

//when the register button is pressed
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//close the login widow and open a register window
logInWindow.dispose();
register();
}
});
}

/**
* window for registering an account
*/
public void register() {
//components
JFrame registrationWindow = new JFrame("Registration");
JPanel panel = new JPanel();
panel.setLayout((new GridLayout(3, 3)));
JLabel userNameL = new JLabel("user name ");
JTextField userNameT = new JTextField(10);
JLabel passwordL = new JLabel("password ");
JTextField passwordT = new JTextField(10);
JButton registerButton = new JButton("register");
JButton logInButton = new JButton("log in");
JLabel text = new JLabel("");
text.setForeground(Color.red);
//add the components to panel
panel.add(userNameL);
panel.add(userNameT);
panel.add(registerButton);
panel.add(passwordL);
panel.add(passwordT);
panel.add(logInButton);
panel.add(text);
//exit on close
registrationWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
registrationWindow.add(BorderLayout.CENTER, panel);
registrationWindow.setSize(550, 170);
registrationWindow.setLocationRelativeTo(null);
registrationWindow.setVisible(true);
//when log in button is pressed
logInButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//close the register window and open the log in window
registrationWindow.dispose();
logIn();
}
});

//when register button is pressed
registerButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//get the username and password
String userName = userNameT.getText();
String password = passwordT.getText();
//check if they are blank
if (userName.equals("") || password.equals("")) {
text.setText("name and pwd can't be blank");
} else {
//pass in the username and password to the server
output.println("register:" + userName + ":" + password);
output.flush();
boolean loop = true;
while (loop) {
try {
if (input.ready()) {
//check for an incoming message
String msg;
msg = input.readLine(); //read the message
if (msg.equals("Username already exists, please use another one")) {
text.setText("username already exists");
loop = false;
} else if (msg.startsWith("Successfully")) {
registrationWindow.dispose();
loop = false;
//start login window
logIn();
}
}
} catch (IOException ex) {
System.out.println("Failed to receive msg from the server");
}
}
}
}
});
//exit on close
registrationWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}

/**
* message window
*/
public void startMessaging() {
//send message to all users as default
selected = "All";
//start two new threads
Thread t = new Thread(new CheckMessage());
Thread t2 = new Thread(new checkUsers());
t.start();
t2.start();
//frame and components
JFrame window = new JFrame("Chat Client");
JPanel southPanel = new JPanel();
southPanel.setLayout(new GridLayout(2, 2));
label = new JLabel("press [Enter] to send");
//area that contains status of online users
statusArea = new JTextArea(18, 14);
statusArea.setEditable(false);
JButton sendButton = new JButton("SEND");
sendButton.addActionListener(new SendButtonListener());
typeField = new JTextField(10);
//place where selecting which person to chat with
userList = new JComboBox();
userList.addItem("All");
msgArea = new JTextArea();
msgArea.setEditable(false);
southPanel.add(userList);
southPanel.add(label);
southPanel.add(typeField);
southPanel.add(sendButton);
//add a scroll pane to the two text areas
JScrollPane jsp = new JScrollPane(msgArea);
JScrollPane jsp2 = new JScrollPane(statusArea);
window.add(BorderLayout.SOUTH, southPanel);
window.add(jsp);
window.add(BorderLayout.WEST, jsp2);
//exit on close
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(400, 400);
window.setVisible(true);
//getting all the users' information just after log in
output.println("-list");
output.flush();
//open at middle of the screen
window.setLocationRelativeTo(null);
window.getRootPane().setDefaultButton(sendButton);
window.addWindowListener(new WindowListener() {
@Override
public void windowOpened(WindowEvent e) {
}

@Override
public void windowClosing(WindowEvent e) {
//tell the server to close it's socket when closing
output.println("$closed$");
output.flush();
}

@Override
public void windowClosed(WindowEvent e) {
running = false;
}

@Override
public void windowIconified(WindowEvent e) {
}

@Override
public void windowDeiconified(WindowEvent e) {
}

@Override
public void windowActivated(WindowEvent e) {
}

@Override
public void windowDeactivated(WindowEvent e) {
}
});
}

//inner class that implements Runnable
class CheckMessage implements Runnable {
//convert second to hours or minutes or still seconds
String convertTime(String sec) {
int second = Integer.parseInt(sec);
if (second > 86400) {
return 86400 / 24 + " days";
} else if (second > 3600) {
return second / 60 + " hours";
} else if (second > 60) {
return second / 60 + " mins";
} else {
return second + " secs";
}
}

@Override
public void run() {
while (running) { // loop unit a message is received
try {
if (input.ready()) {//check for an incoming message
String msg;
msg = input.readLine(); //read the message
if (msg.equals("use -pm:username:message to send a pm") || msg.equals("Online Users: ") || msg.contains(", seen")) {
if (msg.equals("Online Users: ")) {
statusArea.setText("Online Users:\n");
selected = userList.getSelectedItem() + "";
userList.removeAllItems();
userList.addItem("All");
if (!selected.equals("All")) {
userList.addItem(selected);
}
userList.setSelectedItem(selected);
}
int i = msg.indexOf(", seen");
if (i != -1) {
String onlineUser = msg.substring(0, i);
String userStatus = msg.substring(0, i) + "\nlast activity [" + convertTime(msg.substring(i + 7, msg.indexOf("."))) + "] ago";
statusArea.append(userStatus + "\n");
if (!onlineUser.equals(userName) && !onlineUser.equals(selected)) {
userList.addItem(onlineUser);
}
}
} else if (msg.equals("Unknown user!")) {
label.setForeground(Color.red);
label.setText("[" + selected + "] is offline");
userList.setSelectedItem("All");
} else if (!msg.startsWith("use -")) {
//if it's not a server instruction
msgArea.append(msg + "\n");
//getting who is the one sending the message
int i = msg.indexOf("->");
if (i != -1) {
label.setForeground(new Color(0, 102, 0));
if (msg.substring(0, i).equals(userName)) {
label.setText("You are talking only to [" + msg.substring(i + 2, msg.indexOf(":")) + "]");
} else {
label.setText("[" + msg.substring(0, i) + "] is talking only to you");
}
} else if (msg.contains(":")) {
String name = msg.substring(0, msg.indexOf(":"));
label.setForeground(Color.blue);
if (!name.equals(userName)) {
label.setText("[" + msg.substring(0, msg.indexOf(":")) + "] is talking to everyone");
} else {
label.setText("You are talking to everyone");
}
}
}
}
} catch (IOException e) {
System.out.println("Failed to receive msg from the server");
}
}
try { //after leaving the main loop we need to close all the sockets
input.close();
output.close();
mySocket.close();
System.exit(0);
} catch (Exception e) {
System.out.println("Failed to close socket");
}
}
}

//another inner class that implements Runnable
class checkUsers implements Runnable {
@Override
//check the status of all the users every second
public void run() {
while (running) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
output.println("-list");
output.flush();
}
}
}

//****** Inner Classes for Action Listeners ****
// send - send msg to server (also flush), then clear the JTextField
class SendButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
//Send a message to the client
msgArea.setCaretPosition(msgArea.getText().length());
String userSelected = userList.getSelectedItem() + "";
if (!userSelected.equals("All")) {
//sending a private massage
/* added a space in the end because it's going to have an
error when blank massage is sent privately*/
output.println("-pm:" + userSelected + ":" + typeField.getText() + " ");
} else {
output.println(typeField.getText());
}
output.flush();
typeField.setText("");
}
}
}//end ChatClient class