-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathClient.java
215 lines (210 loc) · 7.85 KB
/
Client.java
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
package mmn16q1;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Formatter;
import java.util.Scanner;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Client extends JFrame {
private Socket connection;
private JTextArea ta;
private JPanel Panel;
private JPanel bottomPanel;
private JTextField tf;
private Formatter output;
private Scanner input;
private String un;
private JTextArea tfUn;
private JScrollPane chatSp;
private JScrollPane unSp;
private boolean clientLeft=false; // turns true if client decided to close the chat.
private JTextArea unTa;
private String host;
public Client() {
super("Chat");
host=null;
boolean firstTime=true; //Help checking if a user input has been asked more than once, for changing the prompt when needed.
do { //verify that the host the user gives as input format is correct.
if(firstTime) {
firstTime=false;
host=JOptionPane.showInputDialog("Enter the server IP and port (IP:port)");
}else
host=JOptionPane.showInputDialog("Something's wrong with the IP:Port you entered\nEnter the server IP and port (IP:port)");
if(host==null)
return;
int portLoc=host.indexOf(":");
if(portLoc==-1) {
host=null;
continue;
}
try{
int port=Integer.parseInt(host.substring(portLoc+1, host.length()));
if(port<0||port>65535) {
host=null;
continue;
}
InetAddress.getByName(host.substring(0,portLoc));
}catch (NumberFormatException|UnknownHostException e) {
host=null;
}
} while (host==null);
//setting up swing things
Panel=new JPanel();
ta=new JTextArea();
unTa=new JTextArea();
unTa.setEditable(false);
Panel.setLayout(new BorderLayout());
add(Panel);
chatSp=new JScrollPane(ta);
unSp=new JScrollPane(unTa);
Panel.add(unSp,BorderLayout.WEST);
Panel.add(chatSp);
ta.setEditable(false);
bottomPanel=new JPanel();
tf=new JTextField();
bottomPanel.setLayout(new BorderLayout());
bottomPanel.add(tf);
tfUn=new JTextArea();
tfUn.setEditable(false);
bottomPanel.add(tfUn,BorderLayout.WEST);
Panel.add(bottomPanel,BorderLayout.SOUTH);
tf.addActionListener(new ActionListener() { //textfield's action listener that activates when a user tries to send a message, and send the username and message to the server.
@Override
public void actionPerformed(ActionEvent e) {
if(!e.getActionCommand().equals("")){
output.format("%s: %s\n",un,e.getActionCommand());
output.flush();
tf.setText("");
}
}
});
ta.setEditable(false);
setSize(300, 300);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() { //window listener that make the client to announce its leaving before exiting.
@Override
public void windowClosing(WindowEvent e) {
try {
clientLeft=true;
output.format("Logout\n");
output.flush();
connection.close();
}
catch (IOException e1) {
e1.printStackTrace();
}
}
});
try { //connecting to the chat server.
connection=new Socket(InetAddress.getByName(host.substring(0, host.indexOf(":"))), Integer.parseInt((host.substring(host.indexOf(":")+1, host.length()))));
output=new Formatter(connection.getOutputStream());
input=new Scanner(connection.getInputStream());
}
catch (UnknownHostException |java.net.ConnectException e) {
JOptionPane.showMessageDialog(this, "Can't connect to host.");
dispose();
return;
}
catch (IOException e) {
e.printStackTrace();
}
try {
// connected to the server, connecting to the chat.
firstTime=true;
do {
if(firstTime) {
un=JOptionPane.showInputDialog("Enter your name");
firstTime=false;
}else
un=JOptionPane.showInputDialog("Use English letters only with capital letter first to describe your name.\nEnter your name"); // need to check if you can
if(un==null||un.length()==0) {
output.format("No username entered");
output.flush();
connection.close();
dispose();
return;
}
} while (!isUsernameLegal(un));
output.format("%s\n",un); // sends the username to the server.
output.flush();
un=input.nextLine(); // add the duplicate number to the username.
tfUn.setText(String.format("%s :",un));
initConnectedArea(Integer.parseInt(input.nextLine()));
//connected to the chat
setVisible(true);
String in=null; //holds the input.
while(input.hasNextLine()) {
switch (in=input.nextLine()) {
case "Shutdown":
break;
case "Connected":
unTa.append(String.format("%s\n",input.nextLine()));
String unTaHolder=unTa.getText();
int firstNumberIndex=unTaHolder.indexOf(":")+1;
int lastNumberIndex=firstNumberIndex;
try {
//updates the number of online useres.
//this try block will throw when we reach the first non number character, which means we found the index of the last number.
while(true) {
Integer.parseInt(unTaHolder.substring(firstNumberIndex,lastNumberIndex+1));
lastNumberIndex++;
}
}catch(NumberFormatException e) {} //Nothing to handle, continue to update after I found the index of the last number at unTa
//in both cases, we add the pre-number-text that been there before, update the number of users, and then chain the updated user list that's chained at the updated unTa.
if(Integer.parseInt(unTaHolder.substring(firstNumberIndex,lastNumberIndex))==1) //We didn't need to specify usernames before because only 1 user was connected, but now we do, so we add it.
unTa.setText(String.format("%s%d\nUsernames:%s",unTaHolder.substring(0, firstNumberIndex),(Integer.parseInt(unTaHolder.substring(firstNumberIndex,lastNumberIndex))+1),unTaHolder.substring(lastNumberIndex, unTa.getText().length())));
else //We don't need to add "Usernames:" again, as it presented already.
unTa.setText(String.format("%s%d%s",unTaHolder.substring(0, firstNumberIndex),(Integer.parseInt(unTaHolder.substring(firstNumberIndex,lastNumberIndex))+1),unTaHolder.substring(lastNumberIndex, unTa.getText().length())));
break;
case "Left":
ta.append(String.format("%s\n",input.nextLine()));
initConnectedArea(Integer.parseInt(input.nextLine())); //I considered to detect and remove the user that left, but in any way it'll be O(n), as the user can be the last user that appears while processing. So I decided to reuse the code instead of writing new code that kinda process the same.
break;
default:
ta.append(String.format("%s\n",in));
break;
}
}
if(!clientLeft) {
JOptionPane.showMessageDialog(null, "Connection lost");
}
dispose();
output.close();
input.close();
return;
}
catch (IOException e) {
e.printStackTrace();
}
}
private void initConnectedArea(int numOfOnlineClients) {
unTa.setText("");
unTa.append(String.format("Users online:%d\n",numOfOnlineClients));
if(numOfOnlineClients>1)
unTa.append("Usernames:\n");
for (int i = 0; i < numOfOnlineClients-1; i++) {
unTa.append(String.format("%s\n",input.nextLine())); // receive and show the 1 user from list of online users.
}
}
//Verify that the username is written with English letters only.
private boolean isUsernameLegal(String un) {
if(un.charAt(0)<'A'||un.charAt(0)>'Z')
return false;
for (int i = 1; i < un.length(); i++) {
if(un.charAt(i)<'a'||un.charAt(i)>'z')
return false;
}
return true;
}
}