【问题标题】:If I want to pass my invoking class some information, what is the best way to do this?如果我想向我的调用类传递一些信息,那么最好的方法是什么?
【发布时间】:2015-05-26 18:06:47
【问题描述】:

我目前正在处理一个小型聊天,目前在如何从一个客户端获取一些数据并将其分发给其他客户端方面遇到了一些困难。

到目前为止,这是我的服务器:

public static void main(String[] args) {

    try 
    {
        @SuppressWarnings("resource")
        ServerSocket server = new ServerSocket(8000);

        Hashtable<String, Client> connectedClients = new Hashtable<String, Client>();

        while (true)
        {                                               
            Socket s = server.accept();             
            System.out.println("Client connected from " + s.getLocalAddress().getHostName());   

            Client chat = new Client(s);


            Thread t = new Thread(chat);
            t.start();
        }
    } 
    catch (Exception e) 
    {
        System.out.println("An error occured.");
                    e.printStackTrace();
    }
}

这里是客户端类:

public void run()
{
    try
    {
        @SuppressWarnings("resource")
        Scanner in = new Scanner(socket.getInputStream());
        PrintWriter out = new PrintWriter(socket.getOutputStream());
        String newLine = null;

        while (true)
        {
            newLine = in.nextLine();



            if(clientName==null){
                clientName = newLine;
            }
            else{
                out.println(clientName+":  "+newLine);
                out.flush();
            }
        }
    } 
    catch (Exception e)
    {
        System.out.println(clientName+" disconnected.");
    }   
}

(请注意,我只添加了非常重要的段落)。如您所见,我正在为每个客户端创建一个新线程,它将第一个发送的字符串作为用户名。我的 GUI 正在处理这个问题,在设置用户名之前禁用所有消息字段,这样就可以了。

但是,您可能已经注意到,有一个未使用的 Hashtable,应该填充一个字符串和一个客户端。您可能已经猜到了,这将是用户名和客户端实例。

现在的问题是,如何将数据从 Thread 类获取到调用的 Server 类。

我想出了 2 个想法:

  • 1) 使线程可观察并将所需的方法添加到我的服务器类。一旦设置了名称,它就会通知我的 Server 类,将 String 和通知类添加到我的 Hashtable,然后将其分发给其他客户端。

  • 2) 向我的服务器添加一个静态方法,它做同样的事情。

但是,我觉得我在这里想得太远了。我很确定有一个更简单的方法可以解决这个问题,但我就是想不通。

谁能帮我解决这个问题?

【问题讨论】:

  • 您应该有一个包含集合的Server 对象。现在,您只是将所有服务器组件放在 main 方法中。如果您将集合封装在一个对象中,则可以将该对象传递给每个 Client 实例
  • 我认为,使用现成的东西比从头开始构建更好,我建议使用 Netty 框架,您会在这个问题中找到关于聊天应用程序的简单示例:stackoverflow.com/questions/13923032/…

标签: java oop


【解决方案1】:

您需要将您的集合封装在一个对象中,然后将该对象与执行该集合所需操作所需的方法一起传递给每个客户端。

例如:

class ClientManager {
    private Hashtable<String, Client> clientMap = new Hashtable<>();

    public void sendGlobalMessage(String message) {
        for(Client client : clientMap.values()) {
            if(client != null) {
                client.sendMessage(message);
            }
        }
    }
}

您现在可以将其传递给每个客户。你的 Client 类应该看起来像这样,接受 ClientManager 并提供 sendMessage 方法:

class Client {
    private ClientManager manager;
    private Socket socket;

    private DataOutputStream out;

    public Client(ClientManager manager, Socket socket) {
        this.manager = manager;
        this.socket = socket;
    }

    public void run() {
        try {
            out = new DataOutputStream(socket.getOutputStream());
            DataInputStream in = new DataInputStream(socket.getInputStream());

            clientManager.sendGlobalMessage("hey!");
        }catch(IOException e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(String message) throws IOException {
        out.writeUTF(message);
    }
}

当您的 Server 接受连接时,它会创建一个 Client 对象。这是你应该通过它的时候:

class Server {
    public static void main(String[] args) {
        try(ServerSocket ss = new ServerSocket(...)) {
            ChatManager manager = new ChatManager();

            while(true) {
                Client client = new Client(manager, ss.accept());
                new Thread(client).start();
            }
        }catch(IOException e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

    【解决方案2】:

    您可以将Server 对象(如果它不在main 中)传递给Client 的每个实例,并使用该引用来维护Hashtable

    如果您从未创建过Server 对象,那么您将需要使用您的第二个选项(实际上,这并不是开箱即用的)。只需将connectedClients 设为Server 类的static 成员,然后添加另一个静态方法来维护它。

    您可能需要考虑使用synchronized 方法(除非这是您使用Hashtable 而不是HashMap 进行同步的原因)。

    【讨论】:

    • 您应该尽可能避免使用静态。它引入了全局状态,不仅难以测试,而且可能导致代码更难管理(不知道什么正在访问该静态值)。查看我的答案,我将向每个客户传递集合。这样,您就知道只有 Client 实例和 Server 可以访问该集合
    • @VinceEmigh:我想我完全误解了 OP 试图完成的任务。不过,我的第一个建议(简而言之)与您的建议足够相似:将一个公共对象传递给每个 Client 并使用它来维护列表(在我的建议中)或实际广播消息(在您的示例中)。关于使用 static - 我正在回复 OP 询问的选项,而不是推荐它。但是感谢 BP(最佳实践)规则。
    猜你喜欢
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多