0

我有一个工作 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(或者应该读取的)方法(在此处的代码中简单地调用) .dataToBeProcessedi

我见过的最接近的:

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

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

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

我在网上找不到一个可以在不使用extends Thread.

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

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

或者

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

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

我错过了什么吗?

4

2 回答 2

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);
  }
}
于 2015-08-20T21:24:16.957 回答
0

我不确定我是否理解你的问题。简短的回答:如果你想让 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

祝你好运。

于 2015-08-20T21:36:11.600 回答