I solved this by ditching cookies altogether. I've just shown the methods in the relevant classes; if this isn't enough, I will edit my answer to include the full code.
Inside the configuration class, I get the x-forwarded-for header of the request. This matches the IP address of the client, especially since my backend server is behind a proxy. If the user's IP address is in a list of users, their connection is "refreshed"; otherwise, they are added to the list. Upon disconnection, for whatever reason, the user is marked as disconnected.
A separate ConnectionMonitor class implements the Runnable interface and runs every 10 seconds, and checks to see if any clients have been disconnected for more than 30 seconds. If they have been, then they are removed from the list of users.
MyConfigClass.modifyHandshake()
@Override
public void modifyHandshake(ServerEndpointConfig config,
HandshakeRequest request,
HandshakeResponse response)
{
HttpSession theSession = (HttpSession) request.getHttpSession();
config.getUserProperties().put(HttpSession.class.getName(), theSession);
String ID = request.getHeaders().get("x-forwarded-for").get(0);
if (ChatroomServerEndpoint.users.containsKey(ID))
{
// if this user isn't new, add them back onto the list
User oldUser = ChatroomServerEndpoint.users.get(ID);
System.out.println("An old user with " + ID + " has returned.");
ChatroomServerEndpoint.users.remove(oldUser);
ChatroomServerEndpoint.users.put(ID, oldUser);
oldUser.toggleConnection(true);
System.out.println(oldUser + ", " + ChatroomServerEndpoint.users.size() );
}
else
{
// add a new user to the list
System.out.println("A new user with ID " + ID + " has arrived!");
User newUser = new User(ID);
ChatroomServerEndpoint.users.put(ID, newUser);
System.out.println(newUser + ", " + ChatroomServerEndpoint.users.size() );
}
// put this ID into the configuration for proof of concept
config.getUserProperties().put("newUser", ID);
}
ConnectionMonitor.updateUsers() runs in a separate thread.
void updateUsers()
{
for(String id : ChatroomServerEndpoint.users.keySet())
{
User theUser = ChatroomServerEndpoint.users.get(id);
if (theUser.getStatus() == User.Connection.DISCONNECTED)
{
// get the time at which the user disconnected
Calendar disconnectDate = theUser.getdisconnectionDate();
// Calendar.getTime.getTime returns milliseconds,
// so, multiply maxDisconnectTime by 1000 to see if the user has expired
if (theDate.getTime().getTime() - disconnectDate.getTime().getTime()
>= maxDisconnectTime * 1000 )
{
System.out.println(id + " has timed out");
ChatroomServerEndpoint.users.remove(id);
}
}
}
}
User
public class User {
// the ID is the user's IP address
private String id;
// connection status
public enum Connection
{
CONNECTED,
DISCONNECTED
}
private Connection status;
// the time of disconnection
private Calendar disconnectionDate;
// each user needs a WebSocket Session to be able to send and receive messages
private Session userSession;
/**
* @return the id of this user
*/
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
/**
* @return connection status
*/
public Connection getStatus() {
return status;
}
public void setStatus(Connection status) {
this.status = status;
}
public Calendar getdisconnectionDate() {
return disconnectionDate;
}
public void setdisconnectionDate(Calendar disconnectionDate) {
this.disconnectionDate = disconnectionDate;
}
/**
* @return the userSession
*/
public Session getUserSession() {
return userSession;
}
/**
* @param userSession the userSession to set
*/
public void setUserSession(Session userSession) {
this.userSession = userSession;
}
/**
* @param newID the new ID of the user
*/
public User (String newID)
{
this.id = newID;
this.status = Connection.CONNECTED;
}
/**
* Toggles the connection
* @param toggle - if true, the user is connected
*/
public void toggleConnection(boolean toggle)
{
if (toggle == false)
{
status = Connection.DISCONNECTED;
disconnectionDate = Calendar.getInstance();
}
else
{
status = Connection.CONNECTED;
disconnectionDate = Calendar.getInstance();
disconnectionDate.add(Calendar.HOUR, 1); // give an extra hour to prevent them being disconnected too soon
}
}
}