使用客户端标识保存套接字

时间:2017-06-15 17:06:19

标签: java oop design-patterns serversocket

问题:我有一个包含Map<字符串,套接字>,这是静态的,我用它作为参考来获取给定客户端名称的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;
        }
    }
}
}

客户端处理程序

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
    }
}
}

阅读器

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
        }
    }
}
}

在while(true)中,我继续阅读,并通过访客模式处理各种请求。这里我有一个实现Visitable的Class,可以包含Socket,所以如果我可以设置它,Visitor可以用他的String和Socket注册客户端,但是在程序的这一部分我没有访问类的setter实现可访问性。我可以做类似

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

但是我只需要一次(对于每个客户端)在客户端被接受之后它就在一个while循环中,所以我真的不喜欢它。任何替代方案?

注意:我需要带有Map<的Class。字符串,插座>因为客户端可以决定如何连接到服务器(套接字或RMI),我需要保持对Socket和Class的引用,我可以在其上进行RMI回调。 这个类看起来像

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

编辑:

我需要保留对客户端的引用,以便在任何给定时间向特定客户端发送消息。此引用应该是Client的名称,它是作为客户端输入提供的String。

客户 - &gt;连接

服务器 - &gt;接受客户

客户 - &gt;发送姓名

服务器 - &gt;使用客户端名称作为密钥在Map中保存Socket(或更好的ObjectOutputStream)。

从现在开始,服务器可以在需要时向客户端发送消息。 多个客户端应该可以同时连接(使用唯一名称)。

至于现在,我做了以下更改(似乎有效,但我不知道这是否是一个很好的解决方案)

客户端处理程序(更改)

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

读者(更改)

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

在连接之后,当客户端发送其名称时,我会(在处理响应的VisitorImpl方法中)

ObjectOutputStream currentOutputStream = SocketOutputMemory.getOutputStream(Thread.currentThread().getId());
Connections.addClient(clientLogin.getClientName(), currentOutputStream);

这是我用来将Thread ID与ObjectOutputStream相关联的新“内存”类,因此当Client发送其名称时,我可以检索ObjectOutputStream。

public final class SocketOutputMemory {
private static Map<Long, ObjectOutputStream> objectOutputStreamMap;

private SocketOutputMemory()  {
    throw new IllegalAccessError("Utility class");
}

public static void init() {
    objectOutputStreamMap = new HashMap<>();
}

public static void add(Long threadId, ObjectOutputStream objectOutputStream) {
    objectOutputStreamMap.put(threadId, objectOutputStream);
}

public static ObjectOutputStream getOutputStream(Long threadId) {
    return objectOutputStreamMap.get(threadId);
}
}

0 个答案:

没有答案