【问题标题】:How do I call a method in a specific socket thread using "implements Runnable"?如何使用“implements Runnable”在特定套接字线程中调用方法?
【发布时间】:2015-08-20 20:49:07
【问题描述】:

我有一个工作 Java 服务器(虽然边缘有点粗糙),其中包含 3 个主要类。

第一个类运行服务器并获取套接字以侦听端口并将新连接传递给客户端处理程序。

第二个类是线程客户端处理程序

第三个是从客户端处理程序调用并处理信息的协议类。处理完信息后,协议类将经过处理或格式化的响应返回给客户端处理程序以传递给客户端。

优点是第二个类只需要加载可接受的数据即可从套接字接受。数据可以传递给协议处理程序,并且协议处理程序可以加载您希望服务器用来与客户端通信的任何协议。

在这个例子中,我加载了一个基于 telnet 的聊天类。

例如,如果有人离开聊天,客户端处理程序类可能会执行以下代码:

for (i = 0; i < currentClientsConnected; i++) {
    if(threads[i] != null && threads[i] != this) {
        outputLine = threads[i].serverprotocol.processInput("** " + username + " has left the room **");
        threads[i].out.printf(outputLine);

    }
}

这会将"** [username] has left the room **" 传递给serverprotocol 类,然后以最佳方式返回数据以将消息传输给客户端。在这种情况下,serverprotocol 类使用 telnet 控制代码格式化消息,告诉客户端重新绘制屏幕、添加新消息并向上滚动缓冲区中现有的当前消息。

我也可能只希望客户端处理程序类将消息发送到用户在某些聊天室中的套接字,因此我不想总是发送到所有套接字。

在我的代码中,这是 Class 1 - 接受套接字的服务器类:

while (true) {
    int i;
    // Try and accept the connection
    try {
        clientSocket = serverSocket.accept();
        // System.out.printf("Remote IP:");
        // System.out.printf(clientSocket.getRemoteSocketAddress().toString());

        // Find an unused socket if one is available
        for (i = 0; i < maxClientsAllowed; i++) {

            // If found create thread
            if (threads[i] == null) {
                (threads[i] = new clientThread(clientSocket, threads)).start();
                break;
            }
        }

        // If all sockets are taken
        if (i == maxClientsAllowed) {
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            out.printf("Server too busy. Try later.\n");
            out.close();
            clientSocket.close();
        }
    } catch(IOException e) {
        System.out.println(e);}
}

Class 2 是一个扩展线程的类:

class clientThread extends Thread {
    private String clientName = null;
    private DataInputStream in;
    private PrintWriter out;
    private Socket clientSocket = null;
    private final clientThread[] threads;
    private int currentClientsConnected;
    private serverprotocol serverprotocol;

    public clientThread(Socket clientSocket, clientThread[] threads) {
        this.clientSocket = clientSocket;
        this.threads = threads;
        currentClientsConnected = threads.length;
    }

    public void run() {
    //stuff 
    }
}

我一直在拼命地尝试查看是否可以使用implements Runnable 来代替它,但是我没有运气根据实例调用线程的processInput(或者应该读取dataToBeProcessed)方法线程号(此处代码中简称为i)。

我见过的最接近的:

https://github.com/ico77/chat-server-client/blob/master/src/main/java/hr/ivica/chat/server/ChatServer.java

这可以利用将服务器作为线程池服务器运行。

但是,在这种情况下,sendToAll 函数通过HashMap 直接写入与套接字关联的PrintWriters。服务器不允许您发送到单独的协议处理程序类,甚至是单独的ChatServerWorker 类实例。这意味着我不能,例如,只向套接字 1 和 3 发送消息,然后向套接字 2 发送单独的消息。

我在网上找不到一个可以在不使用extends Thread 的情况下调用套接字处理程序实例的示例。

具体来说,我想保留使用以下行的能力:

threads[i].out.printf(outputLine);

if(threads[i].[class].[var] == 'something') {
// stuff
}

整数可用于引用线程实例,或该线程使用的任何类变量或方法。

我错过了什么吗?

【问题讨论】:

  • 你不能'调用线程中的方法',除非你是那个线程。但是,您可以调用对象中的方法。你的问题仍然模糊不清。

标签: java multithreading sockets java-threads


【解决方案1】:

您的大问题是您将线程本身直接用作ServerClient 线程之间的通信层,这是您不应该做的事情。

相反,创建您自己的 interface Message 对象以在线程之间传递不同的信息,并使用 LinkedBlockingQueue 来处理它们。

你应该有:

  • 一个队列供服务器接收消息
  • 根据您的实施,每个客户端线程使用一个队列来接收来自服务器的消息,或者一个共享队列(如果设计为任何线程都可以处理任何消息)。

所以你可能会这样做:

消息:

public interface Message {
  accept(Server server);
}

断开连接消息(我只做一个):

public class DisconnectionMessage implements Message {
  String username;

  public void accept(Server server) {
    server.handleMessage(this);
  }
}

服务器可运行:

public void run() {
  while(isServerOnline()) {
    Message clientMessage = queue.poll();
    clientMessage.accept(this);
  }
}

public void handleMessage(DisconnectionMessage msg) {
  // code
}

public void handleMessage(ConnectionMessage msg) {
  // code
}

etc.

客户端可运行:

private final Socket socket;
private final BlockingQueue<Message> queue;

public Client(BlockingQueue<Message> queue, Socket socket) {
  this.queue = queue;
  this.socket = socket;
}

public void run() {
  while(true) {
    Message msg = receiveMessage();
    queue.offer(msg);
  }
}

【讨论】:

  • 感谢您抽出宝贵时间回复我的问题。我知道我有点困难,因为我对 Java 和 OOP 总体来说还是个新手。线程本身不在客户端处理程序和服务器之间进行通信。服务器将客户端处理程序作为线程启动。线程咨询第 3 类 - 协议处理程序,但客户端处理程序本身处理所有传入和传出数据。我的代码非常基于github.com/fusion2004/MultiThreadChat-Server/blob/master/src/…(并不是说这是好的代码)...
  • 具体来说,虽然我了解消息如何使用您的方法从套接字/客户端处理程序传输的要点,但我并不希望所有客户端都能够看到所有消息。例如,如果聊天者在不同的聊天室。 “implements Runnable”似乎暗示线程应该非常独立地工作,而我希望一个线程以更加团队合作的方式定位并与其他线程(如果可能)交谈。 I.E “我有一条来自这个线程的消息。你们是同一个房间里的其他线程吗?如果是的话,这就是我收到的消息。”
  • @Aniseedwolf 所以为每个房间创建一个队列
  • 好吧,用户也可能希望彼此直接私下交谈。也许我可以在每个线程启动和结束时创建一个计数器,并将该数字传递给每个新的可运行实例。 (例如stackoverflow.com/questions/9123272/…)然后使用“queue[i]=new LinkedBlockingQueue();”,其中'i'是接收消息的线程?我猜队列数组应该是所有线程都可以读取和写入的地方,并且线程只会读取预期的实例......但是你有一堆线程都在轮询数组?
【解决方案2】:

我不确定我是否理解您的问题。 简短的回答:如果你想让 clientThread 成为一个 Runnable,只需执行它然后更改行

  (threads[i] = new clientThread(clientSocket, threads)).start();

进入

  (threads[i] = new Thread(new clientThread(clientSocket, threads))).start();

如果您查看文档: http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#Thread(java.lang.Runnable)

线程接受具有 Runnable 超类型的对象。

长答案:您不应该直接存储线程,而是在服务器端进行代表客户端的抽象。这种抽象应该封装通信功能。这样,如果你想实现一个不同的通信库,你可以轻松地继承它并避免打破开闭原则。

https://en.wikipedia.org/wiki/Open/closed_principle

祝你好运。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-06
    • 1970-01-01
    • 2016-11-22
    • 2011-02-05
    • 1970-01-01
    • 2019-11-19
    • 2019-02-03
    • 1970-01-01
    相关资源
    最近更新 更多