问题:我有一个包含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);
}
}