Another complete overhaul of client-server communication:
-Added broadcastAnnouncement message to ClientHandler -Bugfix: "Connection lost" at startup -Added LOGIN$username to protocol
This commit is contained in:
parent
51d969e298
commit
c64c754d22
@ -20,16 +20,24 @@ public class Client {
|
|||||||
private Socket socket;
|
private Socket socket;
|
||||||
private BufferedReader in;
|
private BufferedReader in;
|
||||||
private BufferedWriter out;
|
private BufferedWriter out;
|
||||||
public String userName;
|
|
||||||
public ClientPinger clientPinger;
|
public ClientPinger clientPinger;
|
||||||
|
|
||||||
public Client(Socket socket, String userName) {
|
public Client(Socket socket) {
|
||||||
try {
|
try {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||||
this.in = new BufferedReader((new InputStreamReader((socket.getInputStream()))));
|
this.in = new BufferedReader((new InputStreamReader((socket.getInputStream()))));
|
||||||
this.userName = userName;
|
|
||||||
sendMsgToServer(getUsername()); //todo: dont just send username directly pls
|
//sending the initial name to server.
|
||||||
|
String systemName;
|
||||||
|
try {
|
||||||
|
systemName = System.getProperty("user.name");
|
||||||
|
} catch (Exception e) {
|
||||||
|
systemName = "Unknown User";
|
||||||
|
}
|
||||||
|
if (systemName == null) systemName = "Unknown User";
|
||||||
|
sendMsgToServer("LOGON$" + systemName);
|
||||||
|
|
||||||
clientPinger = new ClientPinger(this, this.socket);
|
clientPinger = new ClientPinger(this, this.socket);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -53,9 +61,7 @@ public class Client {
|
|||||||
sendMsgToServer(formattedMSG);
|
sendMsgToServer(formattedMSG);
|
||||||
}
|
}
|
||||||
Thread.sleep(20);
|
Thread.sleep(20);
|
||||||
} catch (IOException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
//LOGGER.debug("just checked next line");
|
//LOGGER.debug("just checked next line");
|
||||||
@ -67,12 +73,10 @@ public class Client {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a thread which listens for incoming messages
|
* Starts a thread which listens for incoming chat messages / other messages that the user
|
||||||
|
* has to see
|
||||||
*/
|
*/
|
||||||
public void chatListener() {
|
public void chatListener() {
|
||||||
/*TODO: what type of decoding has to be done
|
|
||||||
TODO how shall input be logged?
|
|
||||||
*/
|
|
||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -144,19 +148,18 @@ public class Client {
|
|||||||
String hostname;
|
String hostname;
|
||||||
int port = 42069; //can be set via argument later if needed.
|
int port = 42069; //can be set via argument later if needed.
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
System.out.println("Enter the host's IP address (or type localhost)");
|
System.out.println("Enter the host's IP address (or type l for localhost)");
|
||||||
hostname = sc.next();
|
hostname = sc.next();
|
||||||
|
if (hostname == "l") {
|
||||||
|
hostname = "localhost";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
hostname = args[0];
|
hostname = args[0];
|
||||||
}
|
}
|
||||||
String systemName = System.getProperty("user.name");
|
|
||||||
System.out.println("Choose a nickname (Suggestion: " + systemName
|
|
||||||
+ "): "); //Suggests a name based on System username
|
|
||||||
String username = sc.next();
|
|
||||||
Socket socket;
|
Socket socket;
|
||||||
try {
|
try {
|
||||||
socket = new Socket(hostname, 42069);
|
socket = new Socket(hostname, 42069);
|
||||||
Client client = new Client(socket, username);
|
Client client = new Client(socket);
|
||||||
client.chatListener();
|
client.chatListener();
|
||||||
Thread cP = new Thread(client.clientPinger);
|
Thread cP = new Thread(client.clientPinger);
|
||||||
cP.start();
|
cP.start();
|
||||||
@ -169,10 +172,6 @@ public class Client {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return userName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Socket getSocket() {
|
public Socket getSocket() {
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,11 +16,13 @@ public class JClientProtocolParser {
|
|||||||
* @param c this Client(required so this method can access the Client's methods)
|
* @param c this Client(required so this method can access the Client's methods)
|
||||||
*/
|
*/
|
||||||
public static void parse(String msg, Client c) {
|
public static void parse(String msg, Client c) {
|
||||||
|
//LOGGER.debug("got message: " + msg + ".");
|
||||||
String header = ""; //"header" is the first 5 characters, i.e. the protocol part
|
String header = ""; //"header" is the first 5 characters, i.e. the protocol part
|
||||||
try {
|
try {
|
||||||
header = msg.substring(0, 5);
|
header = msg.substring(0, 5);
|
||||||
} catch (IndexOutOfBoundsException e) {
|
} catch (IndexOutOfBoundsException e) {
|
||||||
System.out.println("Received unknown command");
|
System.out.println("Received unknown command");
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
switch (header) {
|
switch (header) {
|
||||||
case "SPING":
|
case "SPING":
|
||||||
@ -40,7 +42,7 @@ public class JClientProtocolParser {
|
|||||||
break;
|
break;
|
||||||
case "QUITC":
|
case "QUITC":
|
||||||
c.closeEverything();
|
c.closeEverything();
|
||||||
System.out.println("bye!");
|
break;
|
||||||
default:
|
default:
|
||||||
System.out.println("Received unknown command");
|
System.out.println("Received unknown command");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class ClientPinger implements Runnable {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(2000);
|
Thread.sleep(20000);
|
||||||
while (socket.isConnected() && !socket.isClosed()) {
|
while (socket.isConnected() && !socket.isClosed()) {
|
||||||
gotPingBack = false;
|
gotPingBack = false;
|
||||||
client.sendMsgToServer("CPING");
|
client.sendMsgToServer("CPING");
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
Client commands:
|
Client to server commands:
|
||||||
|
|
||||||
Implemented:
|
Implemented:
|
||||||
|
* LOGON$name Log on, with the name set to whatever follow $.
|
||||||
* CHATA$message Send chat message to all
|
* CHATA$message Send chat message to all
|
||||||
* QUITS request to quit server/ leave server. The server should then remove the
|
* QUITS request to quit server/ leave server. The server should then remove the
|
||||||
relevant ClientHandler and close the sockets, but before doing so, it sends
|
relevant ClientHandler and close the sockets, but before doing so, it sends
|
||||||
@ -20,7 +21,7 @@ Future / planned:
|
|||||||
* VOTEH humans voting who is the ghost
|
* VOTEH humans voting who is the ghost
|
||||||
* LISTP list players/clients in session with the Server
|
* LISTP list players/clients in session with the Server
|
||||||
|
|
||||||
Server Commands:
|
Server to client Commands:
|
||||||
|
|
||||||
Implemented:
|
Implemented:
|
||||||
* SPING Ping from server to client
|
* SPING Ping from server to client
|
||||||
|
|||||||
@ -33,19 +33,21 @@ public class ClientHandler implements Runnable {
|
|||||||
try {
|
try {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
this.out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
|
||||||
this.in = new BufferedReader((new InputStreamReader((socket.getInputStream()))));
|
this.in = new BufferedReader(new InputStreamReader((socket.getInputStream())));
|
||||||
this.clientUserName = in.readLine();
|
this.clientUserName = "Mysterious Passenger"; //todo: duplicate handling for this
|
||||||
// duplicate handling: if username already taken, assign random name to client
|
/*
|
||||||
|
// todo: duplicate handling more elegantly
|
||||||
if (AllClientNames.allNames("").contains(clientUserName)) {
|
if (AllClientNames.allNames("").contains(clientUserName)) {
|
||||||
clientUserName = NameGenerator.randomName(clientUserName);
|
clientUserName = NameGenerator.randomName(clientUserName);
|
||||||
}
|
}
|
||||||
// add username to list of all client names for future duplicate checking
|
// add username to list of all client names for future duplicate checking
|
||||||
AllClientNames.allNames(clientUserName);
|
AllClientNames.allNames(clientUserName);
|
||||||
|
|
||||||
|
*/
|
||||||
connectedClients.add(this);
|
connectedClients.add(this);
|
||||||
serverPinger = new ServerPinger(out, socket);
|
serverPinger = new ServerPinger(out, socket);
|
||||||
Thread sP = new Thread(serverPinger);
|
Thread sP = new Thread(serverPinger);
|
||||||
sP.start();
|
sP.start();
|
||||||
broadcastChatMessage("SERVER: " + clientUserName + " has joined the Server");
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -109,23 +111,52 @@ public class ClientHandler implements Runnable {
|
|||||||
* @param newName The desired new name to replace the old one with.
|
* @param newName The desired new name to replace the old one with.
|
||||||
*/
|
*/
|
||||||
public void changeUsername(String newName) {
|
public void changeUsername(String newName) {
|
||||||
if (AllClientNames.allNames("").contains(newName)) {
|
if (AllClientNames.allNames("").contains(newName)) { //todo: more elegant solution
|
||||||
newName = NameGenerator.randomName(newName);
|
newName = NameGenerator.randomName(newName);
|
||||||
}
|
}
|
||||||
String h = this.clientUserName; //just a friendly little helper
|
String h = this.clientUserName; //just a friendly little helper
|
||||||
this.clientUserName = newName;
|
this.clientUserName = newName;
|
||||||
AllClientNames.allNames(newName);
|
AllClientNames.allNames(newName);
|
||||||
broadcastChatMessage(h + " have changed their nickname to " + clientUserName);
|
broadcastAnnouncement(h + " has changed their nickname to " + clientUserName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcasts a Message to all active clients in the form "Username: msg"
|
* Sets the client's username on login, if the username is already taken, a similar
|
||||||
|
* option is chosen. Functionally, the only difference between this method and changeUsername
|
||||||
|
* is that it doesn't print out the name change.
|
||||||
*
|
*
|
||||||
* @param msg the Message to be broadcasted
|
* @param name The desired name.
|
||||||
|
*/
|
||||||
|
public void setUsernameOnLogin(String name) {
|
||||||
|
//todo: duplicate checking
|
||||||
|
this.clientUserName = name;
|
||||||
|
broadcastAnnouncement( clientUserName + " has joined the Server");
|
||||||
|
//todo: add this name to namelist
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts a Message to all active clients in the form "Username: @msg"
|
||||||
|
*
|
||||||
|
* @param msg the Message to be broadcast
|
||||||
*/
|
*/
|
||||||
public void broadcastChatMessage(String msg) {
|
public void broadcastChatMessage(String msg) {
|
||||||
for (ClientHandler client : connectedClients) {
|
for (ClientHandler client : connectedClients) {
|
||||||
client.sendMsgToClient("CHATM$" + clientUserName + ": \"" + msg + "\"");
|
client.sendMsgToClient("CHATM$" + clientUserName + ": " + msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts a non-chat Message to all active clients. This can be used for server
|
||||||
|
* messages / announcements rather than chat messages. The message will be printed to the user ex-
|
||||||
|
* actly as it is given to this method. Unlike broadcastChatMessage, it will also be printed onto
|
||||||
|
* the server console.
|
||||||
|
*
|
||||||
|
* @param msg the Message to be broadcast
|
||||||
|
*/
|
||||||
|
public void broadcastAnnouncement(String msg) {
|
||||||
|
System.out.println(msg);
|
||||||
|
for (ClientHandler client : connectedClients) {
|
||||||
|
client.sendMsgToClient("CHATM$" + msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ public class JServerProtocolParser {
|
|||||||
* @param h this ClientHandler (required so this method can access the ClientHandler's methods)
|
* @param h this ClientHandler (required so this method can access the ClientHandler's methods)
|
||||||
*/
|
*/
|
||||||
public static void parse(String msg, ClientHandler h) {
|
public static void parse(String msg, ClientHandler h) {
|
||||||
|
//LOGGER.debug("got message: " + msg + ".");
|
||||||
String header = ""; //"header" is the first 5 characters, i.e. the protocol part
|
String header = ""; //"header" is the first 5 characters, i.e. the protocol part
|
||||||
try {
|
try {
|
||||||
header = msg.substring(0, 5);
|
header = msg.substring(0, 5);
|
||||||
@ -32,6 +33,14 @@ public class JServerProtocolParser {
|
|||||||
//sends chat message to all connected clients
|
//sends chat message to all connected clients
|
||||||
h.broadcastChatMessage(msg.substring(6));
|
h.broadcastChatMessage(msg.substring(6));
|
||||||
break;
|
break;
|
||||||
|
case "LOGON":
|
||||||
|
//sets name to whatever follows LOGON$
|
||||||
|
try {
|
||||||
|
h.setUsernameOnLogin(msg.substring(6));
|
||||||
|
} catch (Exception e) {
|
||||||
|
h.setUsernameOnLogin("A Mysterious Passenger");
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "NAMEC":
|
case "NAMEC":
|
||||||
//changes name to whatever follows NAMEC$. If the new name is already in use, it will append
|
//changes name to whatever follows NAMEC$. If the new name is already in use, it will append
|
||||||
//random numbers to the name.
|
//random numbers to the name.
|
||||||
@ -47,6 +56,7 @@ public class JServerProtocolParser {
|
|||||||
break;
|
break;
|
||||||
case "QUITS":
|
case "QUITS":
|
||||||
//safely disconnects the user
|
//safely disconnects the user
|
||||||
|
h.broadcastAnnouncement(h.getClientUserName() + " has left the server.");
|
||||||
h.disconnectClient();
|
h.disconnectClient();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -34,15 +34,6 @@ public class Server {
|
|||||||
Thread th = new Thread(nextClient);
|
Thread th = new Thread(nextClient);
|
||||||
connectedClients.add(nextClient);
|
connectedClients.add(nextClient);
|
||||||
th.start();
|
th.start();
|
||||||
// close socket + remove client if client is disconnected
|
|
||||||
/* TODO: Modify or remove this disconnection handling - right now, it is not functioning
|
|
||||||
(when disconnecting there are "broken pipe" exceptions on client side and "stream closed"
|
|
||||||
exceptions on the server side).
|
|
||||||
*/
|
|
||||||
if (nextClient.getSocket().getInputStream().read() == -1) {
|
|
||||||
System.out.println("client disconnected. closing socket");
|
|
||||||
socket.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
Reference in New Issue
Block a user