jeudi 15 juin 2017

Save socket with client identifier

The problem: I have a Class that contains a Map< String, Socket >, which is static, and I use it as reference to get the Socket given the name of the client. But in order to do this, when the client is accepted by the server I should save his identifier, too. The problem is that his identifier is also the name of the client, and I don't know it until the client sends it and this happens only after the client is accepted.

My code:

Server Socket

public class SocketServer {
private int port;

private ServerSocket serverSocket;
private ExecutorService executorService;

public SocketServer(int port) {
    this.port = port;
    executorService = Executors.newCachedThreadPool();
}

public void startServer() {
    initServer();
    handleConnections();
    executorService.shutdown();
}

private void initServer() {
    try {
        serverSocket = new ServerSocket(port);
    } catch (IOException e) {
        // log exception
    }
}

private void handleConnections() {
    while (true) {
        try {
            Socket socket = serverSocket.accept();
            executorService.submit(new ClientHandler(socket));
        } catch (IOException e) {
            // log exception
            break;
        }
    }
}
}

Client Handler

public class ClientHandler implements Runnable {
private Socket socket;

public ClientHandler(Socket socket) {
    this.socket = socket;
}

public void run() {
    try {
        Thread thread = new Thread(new Reader(socket));
        thread.start();
    } catch (IOException e) {
        // log exception
    }
}

private void close() {
    try {
        objectOutputStream.close();
        socket.close();
    } catch (IOException e) {
        // log exception
    }
}
}

Reader

public class Reader implements Runnable {
private final static Logger LOGGER = Logger.getLogger(Reader.class.getName());

private Socket socket;
private ObjectInputStream objectInputStream;


public Reader(Socket socket) throws IOException {
    this.socket = socket;
    objectInputStream = new ObjectInputStream(socket.getInputStream());
}

@Override
public void run() {
    while (true) {
        try {
            Visitable visitable = (Visitable)objectInputStream.readObject();
            Visitor visitor = new VisitorImpl();
            visitable.acceptVisitor(visitor);
        } catch (IOException | ClassNotFoundException e) {
            // log exception
        }
    }
}
}

In the while(true) i keep reading and I handle every kind of request through a Visitor Pattern. Here I have a Class implementing Visitable that can contain the Socket, so if I can set it the Visitor can register the client with his String and Socket, but in this part of the program I don't have access to the setters of the classes implementing the Visitable. I could do something like

if (visitable instanceof VisitableImplWithSocket) {
    ((VisitableImplWithSocket) visitable).setSocket(socket);
}

But I need this only one time (for every client) right after the client is accepted and it's inside a while loop, so I don't really like it. Any alternatives?

NOTE: I need the Class with the Map< String, Socket > because a client can decide how to connect to the server (socket or RMI) and I need to keep a reference to both the Socket and the Class on which I can do a RMI callback. This class looks like

public class ListConnections {
    private static Map<String, Socket> CLIENT_SOCKET_MAP;
    private static Map<String, Registrable> CLIENT_RMI_MAP;
    ...
}

Aucun commentaire:

Enregistrer un commentaire